vendor/isotope/isotope-core/system/modules/isotope/library/Isotope/Model/ProductPrice.php line 81

Open in your IDE?
  1. <?php
  2. /*
  3.  * Isotope eCommerce for Contao Open Source CMS
  4.  *
  5.  * Copyright (C) 2009 - 2019 terminal42 gmbh & Isotope eCommerce Workgroup
  6.  *
  7.  * @link       https://isotopeecommerce.org
  8.  * @license    https://opensource.org/licenses/lgpl-3.0.html
  9.  */
  10. namespace Isotope\Model;
  11. use Contao\Database;
  12. use Contao\Database\Result;
  13. use Contao\Date;
  14. use Contao\Model;
  15. use Contao\Model\Collection;
  16. use Contao\Model\QueryBuilder;
  17. use Contao\StringUtil;
  18. use Isotope\Collection\ProductPrice as ProductPriceCollection;
  19. use Isotope\Interfaces\IsotopePrice;
  20. use Isotope\Interfaces\IsotopeProduct;
  21. use Isotope\Interfaces\IsotopeProductCollection;
  22. use Isotope\Isotope;
  23. /**
  24.  * ProductPrice defines an advanced price of a product
  25.  */
  26. class ProductPrice extends Model implements IsotopePrice
  27. {
  28.     /**
  29.      * Name of the current table
  30.      * @var string
  31.      */
  32.     protected static $strTable 'tl_iso_product_price';
  33.     /**
  34.      * Tiers for this price
  35.      * @var array
  36.      */
  37.     protected $arrTiers = array();
  38.     /**
  39.      * Construct the object
  40.      *
  41.      * @param Result $objResult
  42.      */
  43.     public function __construct(Result $objResult null)
  44.     {
  45.         parent::__construct($objResult);
  46.         $this->arrTiers array_combine(
  47.             explode(','$this->arrData['tier_keys']),
  48.             explode(','$this->arrData['tier_values'])
  49.         );
  50.         ksort($this->arrTiers);
  51.     }
  52.     /**
  53.      * Return true if more than one price is available
  54.      *
  55.      * @return bool
  56.      */
  57.     public function hasTiers()
  58.     {
  59.         return (\count($this->arrTiers) > 1);
  60.     }
  61.     /**
  62.      * Return price
  63.      *
  64.      * @param int   $intQuantity
  65.      * @param array $arrOptions
  66.      *
  67.      * @return float
  68.      */
  69.     public function getAmount($intQuantity 1, array $arrOptions = array())
  70.     {
  71.         return Isotope::calculatePrice($this->getValueForTier($intQuantity), $this'price'$this->tax_classnull$arrOptions);
  72.     }
  73.     /**
  74.      * Return original price
  75.      *
  76.      * @param int   $intQuantity
  77.      * @param array $arrOptions
  78.      *
  79.      * @return float
  80.      */
  81.     public function getOriginalAmount($intQuantity 1, array $arrOptions = array())
  82.     {
  83.         return Isotope::calculatePrice($this->getValueForTier($intQuantity), $this'original_price'$this->tax_classnull$arrOptions);
  84.     }
  85.     /**
  86.      * Return net price (without taxes)
  87.      *
  88.      * @param int   $intQuantity
  89.      * @param array $arrOptions
  90.      *
  91.      * @return float
  92.      */
  93.     public function getNetAmount($intQuantity 1, array $arrOptions = array())
  94.     {
  95.         $fltAmount $this->getValueForTier($intQuantity);
  96.         /** @var TaxClass $objTaxClass */
  97.         if (($objTaxClass $this->getRelated('tax_class')) !== null) {
  98.             $fltAmount $objTaxClass->calculateNetPrice($fltAmount);
  99.         }
  100.         return Isotope::calculatePrice($fltAmount$this'net_price'0null$arrOptions);
  101.     }
  102.     /**
  103.      * Return gross price (with all taxes)
  104.      *
  105.      * @param int   $intQuantity
  106.      * @param array $arrOptions
  107.      *
  108.      * @return float
  109.      */
  110.     public function getGrossAmount($intQuantity 1, array $arrOptions = array())
  111.     {
  112.         $fltAmount $this->getValueForTier($intQuantity);
  113.         /** @var TaxClass $objTaxClass */
  114.         if (($objTaxClass $this->getRelated('tax_class')) !== null) {
  115.             $fltAmount $objTaxClass->calculateGrossPrice($fltAmount);
  116.         }
  117.         return Isotope::calculatePrice($fltAmount$this'gross_price'0null$arrOptions);
  118.     }
  119.     /**
  120.      * Get lowest amount of all tiers
  121.      *
  122.      * @param array $arrOptions
  123.      *
  124.      * @return float
  125.      */
  126.     public function getLowestAmount(array $arrOptions = array())
  127.     {
  128.         if (!$this->hasTiers()) {
  129.             return $this->getAmount();
  130.         }
  131.         return Isotope::calculatePrice(min($this->arrTiers), $this'price'$this->tax_classnull$arrOptions);
  132.     }
  133.     /**
  134.      * Return price tiers array
  135.      *
  136.      * @return array
  137.      */
  138.     public function getTiers()
  139.     {
  140.         return $this->arrTiers;
  141.     }
  142.     /**
  143.      * Return lowest tier (= minimum quantity)
  144.      *
  145.      * @return int
  146.      */
  147.     public function getLowestTier()
  148.     {
  149.         $intMin = (int) min(array_keys($this->arrTiers));
  150.         return $intMin ?: 1;
  151.     }
  152.     /**
  153.      * Return value for a price tier, finding closest match
  154.      *
  155.      * @param int $intTier
  156.      *
  157.      * @return float
  158.      */
  159.     public function getValueForTier($intTier)
  160.     {
  161.         do {
  162.             if (isset($this->arrTiers[$intTier])) {
  163.                 return $this->arrTiers[$intTier];
  164.             }
  165.             --$intTier;
  166.         } while ($intTier 0);
  167.         return $this->arrTiers[min(array_keys($this->arrTiers))];
  168.     }
  169.     /**
  170.      * Generate price for HTML rendering
  171.      *
  172.      * @param bool  $blnShowTiers
  173.      * @param int   $intQuantity
  174.      * @param array $arrOptions
  175.      *
  176.      * @return string
  177.      */
  178.     public function generate($blnShowTiers false$intQuantity 1, array $arrOptions = array())
  179.     {
  180.         $blnShowFrom false;
  181.         $fltPrice $this->getAmount($intQuantity$arrOptions);
  182.         if ($blnShowTiers) {
  183.             $fltLowest $this->getLowestAmount($arrOptions);
  184.             if ($fltPrice != $fltLowest) {
  185.                 $blnShowFrom true;
  186.                 $fltPrice $fltLowest;
  187.             }
  188.         }
  189.         $strPrice Isotope::formatPriceWithCurrency($fltPrice);
  190.         if ($blnShowFrom) {
  191.             return sprintf($GLOBALS['TL_LANG']['MSC']['priceRangeLabel'], $strPrice);
  192.         }
  193.         $fltOriginalPrice $this->getOriginalAmount($intQuantity$arrOptions);
  194.         if ($fltPrice $fltOriginalPrice) {
  195.             $strOriginalPrice Isotope::formatPriceWithCurrency($fltOriginalPrice);
  196.             // @deprecated remove <strike>, should be a CSS setting
  197.             return '<div class="original_price"><strike>' $strOriginalPrice '</strike></div><div class="price">' $strPrice '</div>';
  198.         }
  199.         return $strPrice;
  200.     }
  201.     public function setProduct(IsotopeProduct $product)
  202.     {
  203.         $this->arrRelated['pid'] = $product;
  204.     }
  205.     /**
  206.      * Find prices for a given product and collection
  207.      *
  208.      * @param IsotopeProduct                             $objProduct
  209.      * @param IsotopeProductCollection|ProductCollection $objCollection
  210.      * @param array                                      $arrOptions
  211.      *
  212.      * @return IsotopePrice
  213.      */
  214.     public static function findByProductAndCollection(IsotopeProduct $objProductIsotopeProductCollection $objCollection, array $arrOptions = array())
  215.     {
  216.         $t = static::$strTable;
  217.         $arrOptions['column'] = array();
  218.         $arrOptions['value'] = array();
  219.         if ($objProduct->hasAdvancedPrices()) {
  220.             $time Date::floorToMinute($objCollection->getLastModification());
  221.             $arrGroups = static::getMemberGroups($objCollection->getRelated('member'));
  222.             $arrOptions['column'][] = "$t.config_id IN (" . (int) $objCollection->config_id ",0)";
  223.             $arrOptions['column'][] = "$t.member_group IN(" implode(','$arrGroups) . ")";
  224.             $arrOptions['column'][] = "($t.start='' OR $t.start<'$time')";
  225.             $arrOptions['column'][] = "($t.stop='' OR $t.stop>'" . ($time 60) . "')";
  226.             $arrOptions['order'] = "$t.config_id DESC, " Database::getInstance()->findInSet('member_group'$arrGroups) . ", $t.start DESC, $t.stop DESC";
  227.         } else {
  228.             $arrOptions['column'][] = "$t.config_id=0";
  229.             $arrOptions['column'][] = "$t.member_group=0";
  230.             $arrOptions['column'][] = "$t.start=''";
  231.             $arrOptions['column'][] = "$t.stop=''";
  232.         }
  233.         if ($objProduct->hasVariantPrices() && !$objProduct->isVariant()) {
  234.             $arrIds $objProduct->getVariantIds() ?: array(0);
  235.             $arrOptions['column'][] = "$t.pid IN (" implode(','$arrIds) . ")";
  236.         } else {
  237.             $arrOptions['column'][] = "$t.pid=" . ($objProduct->hasVariantPrices() ? $objProduct->getId() : $objProduct->getProductId());
  238.         }
  239.         /** @var ProductPriceCollection $objResult */
  240.         $objResult = static::find($arrOptions);
  241.         return (null === $objResult) ? null $objResult->filterDuplicatesBy('pid');
  242.     }
  243.     /**
  244.      * @param int   $intProduct
  245.      * @param array $arrOptions
  246.      *
  247.      * @return ProductPrice|null
  248.      * @deprecated use findPrimaryByProductId
  249.      */
  250.     public static function findPrimaryByProduct($intProduct, array $arrOptions = array())
  251.     {
  252.         return static::findPrimaryByProductId($intProduct$arrOptions);
  253.     }
  254.     /**
  255.      * Find primary price for a product
  256.      *
  257.      * @param int   $intProduct
  258.      * @param array $arrOptions
  259.      *
  260.      * @return ProductPrice|null
  261.      */
  262.     public static function findPrimaryByProductId($intProduct, array $arrOptions = array())
  263.     {
  264.         $t = static::$strTable;
  265.         $arrOptions array_merge(
  266.             array(
  267.                 'column' => array(
  268.                     "$t.config_id=0",
  269.                     "$t.member_group=0",
  270.                     "$t.start=''",
  271.                     "$t.stop=''",
  272.                     "$t.pid=" $intProduct
  273.                 ),
  274.                 'limit'  => 1,
  275.                 'return' => 'Model'
  276.             ),
  277.             $arrOptions
  278.         );
  279.         return static::find($arrOptions);
  280.     }
  281.     /**
  282.      * Find primary price for multiple product/variant IDs
  283.      *
  284.      * @param array $arrIds
  285.      * @param array $arrOptions
  286.      *
  287.      * @return Collection|null
  288.      */
  289.     public static function findPrimaryByProductIds(array $arrIds, array $arrOptions = array())
  290.     {
  291.         $t = static::$strTable;
  292.         $arrOptions array_merge(
  293.             array(
  294.                 'column' => array(
  295.                     "$t.config_id=0",
  296.                     "$t.member_group=0",
  297.                     "$t.start=''",
  298.                     "$t.stop=''",
  299.                     "$t.pid IN (" implode(','$arrIds) . ")",
  300.                 ),
  301.                 'return' => 'Collection'
  302.             ),
  303.             $arrOptions
  304.         );
  305.         $objResult = static::find($arrOptions);
  306.         return (null === $objResult) ? null $objResult->filterDuplicatesBy('pid');
  307.     }
  308.     /**
  309.      * Find advanced price for multiple product/variant IDs
  310.      *
  311.      * @param array                                      $arrIds
  312.      * @param IsotopeProductCollection|ProductCollection $objCollection
  313.      *
  314.      * @return Collection|null
  315.      */
  316.     public static function findAdvancedByProductIdsAndCollection(array $arrIdsIsotopeProductCollection $objCollection null)
  317.     {
  318.         if (null === $objCollection) {
  319.             $configIds = [0];
  320.             $objMember null;
  321.         } else {
  322.             $configIds = [(int) $objCollection->config_id0];
  323.             $objMember $objCollection->getRelated('member');
  324.         }
  325.         $arrGroups = static::getMemberGroups($objMember);
  326.         return static::findAdvancedByProductIds($arrIds$arrGroups$configIds);
  327.     }
  328.     /**
  329.      * Find advanced price for multiple product/variant IDs
  330.      *
  331.      * @return Collection|null
  332.      */
  333.     public static function findAdvancedByProductIds(array $arrIds, array $arrGroups = [0], array $configIds = [0])
  334.     {
  335.         $time Date::floorToMinute();
  336.         $queries = [];
  337.         foreach ($arrIds as $id) {
  338.             $queries[] = "
  339.                 SELECT
  340.                     tl_iso_product_price.*,
  341.                     GROUP_CONCAT(tl_iso_product_pricetier.min) AS tier_keys,
  342.                     GROUP_CONCAT(tl_iso_product_pricetier.price) AS tier_values
  343.                 FROM tl_iso_product_price
  344.                 LEFT JOIN tl_iso_product_pricetier ON tl_iso_product_pricetier.pid = tl_iso_product_price.id
  345.                 WHERE
  346.                     config_id IN (" implode(','$configIds) . ") AND
  347.                     member_group IN(" implode(','$arrGroups) . ") AND
  348.                     (start='' OR start<'$time') AND
  349.                     (stop='' OR stop>'" . ($time 60) . "') AND
  350.                     tl_iso_product_price.pid=" $id "
  351.                 GROUP BY tl_iso_product_price.id
  352.                 ORDER BY config_id DESC, " Database::getInstance()->findInSet('member_group'$arrGroups) . ", start DESC, stop DESC
  353.                 LIMIT 1
  354.             ";
  355.         }
  356.         $objResult Database::getInstance()->query('('.implode(") UNION ("$queries).')');
  357.         if ($objResult->numRows) {
  358.             return ProductPriceCollection::createFromDbResult($objResult, static::$strTable);
  359.         }
  360.         return null;
  361.     }
  362.     /**
  363.      * Compile a list of member groups suitable for retrieving prices. This includes a 0 at the last position in array
  364.      *
  365.      * @param object $objMember
  366.      *
  367.      * @return array
  368.      */
  369.     protected static function getMemberGroups($objMember)
  370.     {
  371.         if (null !== $objMember) {
  372.             $arrGroups StringUtil::deserialize($objMember->groups);
  373.         }
  374.         if (!isset($arrGroups) || !\is_array($arrGroups)) {
  375.             $arrGroups = array();
  376.         }
  377.         $arrGroups[] = 0;
  378.         return $arrGroups;
  379.     }
  380.     /**
  381.      * {@inheritdoc}
  382.      */
  383.     protected static function buildFindQuery(array $arrOptions)
  384.     {
  385.         $arrOptions['group'] = (($arrOptions['group'] ?? null) ? $arrOptions['group'].', ' '') . 'tl_iso_product_price.id';
  386.         $query QueryBuilder::find($arrOptions);
  387.         $from  substr($querystrpos($query'*')+1);
  388.         $query "SELECT tl_iso_product_price.*, GROUP_CONCAT(tl_iso_product_pricetier.min) AS tier_keys, GROUP_CONCAT(tl_iso_product_pricetier.price) AS tier_values" $from;
  389.         $query str_replace(
  390.             'FROM tl_iso_product_price',
  391.             'FROM tl_iso_product_price LEFT JOIN tl_iso_product_pricetier ON tl_iso_product_pricetier.pid = tl_iso_product_price.id',
  392.             $query
  393.         );
  394.         return $query;
  395.     }
  396.     /**
  397.      * {@inheritdoc}
  398.      */
  399.     protected static function createCollection(array $arrModels$strTable)
  400.     {
  401.         return new ProductPriceCollection($arrModels$strTable);
  402.     }
  403.     /**
  404.      * {@inheritdoc}
  405.      */
  406.     protected static function createCollectionFromDbResult(Result $objResult$strTable)
  407.     {
  408.         return ProductPriceCollection::createFromDbResult($objResult$strTable);
  409.     }
  410. }