vendor/isotope/isotope-core/system/modules/isotope_rules/library/Isotope/Model/Rule.php line 360

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\MemberModel;
  13. use Contao\Model;
  14. use Contao\Model\Collection;
  15. use Contao\StringUtil;
  16. use Isotope\Interfaces\IsotopeProduct;
  17. use Isotope\Isotope;
  18. use Isotope\Translation;
  19. /**
  20.  * @property int    $id
  21.  * @property int    $tstamp
  22.  * @property string $type
  23.  * @property string $name
  24.  * @property string $label
  25.  * @property string $discount
  26.  * @property int    $tax_class
  27.  * @property array  $groupRules
  28.  * @property string $groupCondition
  29.  * @property string $applyTo
  30.  * @property string $rounding
  31.  * @property bool   $enableCode
  32.  * @property string $code
  33.  * @property bool   $singleCode
  34.  * @property int    $limitPerMember
  35.  * @property int    $limitPerConfig
  36.  * @property int    $minSubtotal
  37.  * @property int    $maxSubtotal
  38.  * @property string $minWeight
  39.  * @property string $maxWeight
  40.  * @property int    $minItemQuantity
  41.  * @property int    $maxItemQuantity
  42.  * @property string $quantityMode
  43.  * @property int    $startDate
  44.  * @property int    $endDate
  45.  * @property int    $startTime
  46.  * @property int    $endTime
  47.  * @property string $configRestrictions
  48.  * @property bool   $configCondition
  49.  * @property string $memberRestrictions
  50.  * @property bool   $memberCondition
  51.  * @property string $productRestrictions
  52.  * @property bool   $productCondition
  53.  * @property string $attributeName
  54.  * @property string $attributeCondition
  55.  * @property string $attributeValue
  56.  * @property bool   $enabled
  57.  * @property bool   $groupOnly
  58.  */
  59. class Rule extends Model
  60. {
  61.     const ROUND_NORMAL 'normal';
  62.     const ROUND_UP 'up';
  63.     const ROUND_DOWN 'down';
  64.     const GROUP_FIRST 'first';
  65.     const GROUP_ALL 'all';
  66.     /**
  67.      * Name of the current table
  68.      * @var string
  69.      */
  70.     protected static $strTable 'tl_iso_rule';
  71.     /**
  72.      * Get label for rule
  73.      * @return  string
  74.      */
  75.     public function getLabel()
  76.     {
  77.         return Translation::get(($this->label ? : $this->name));
  78.     }
  79.     /**
  80.      * Return true if the rule has a percentage (not fixed) amount
  81.      * @return bool
  82.      */
  83.     public function isPercentage()
  84.     {
  85.         return '%' === substr($this->discount, -1);
  86.     }
  87.     /**
  88.      * Return percentage amount (if applicable)
  89.      * @return float
  90.      * @throws UnexpectedValueException
  91.      */
  92.     public function getPercentage()
  93.     {
  94.         if (!$this->isPercentage()) {
  95.             throw new \UnexpectedValueException('Rule does not have a percentage amount.');
  96.         }
  97.         return (float) substr($this->discount0, -1);
  98.     }
  99.     /**
  100.      * Return percentage label if price is percentage
  101.      * @return  string
  102.      */
  103.     public function getPercentageLabel()
  104.     {
  105.         return $this->isPercentage() ? $this->discount '';
  106.     }
  107.     public static function findByProduct(IsotopeProduct $objProduct$strField$fltPrice)
  108.     {
  109.         return static::findByConditions(array("type='product'"), array(), array($objProduct), 'low_price' === $strField, array($strField => $fltPrice));
  110.     }
  111.     public static function findForCart($intId null)
  112.     {
  113.         $arrProcedures = array("(type='cart' OR type='cart_group')""enableCode=''");
  114.         if (null === $intId) {
  115.             $arrProcedures[] = "groupOnly=''";
  116.         } else {
  117.             $arrProcedures[] = 'id='.(int)$intId;
  118.         }
  119.         return static::findByConditions($arrProcedures);
  120.     }
  121.     public static function findForCartWithCoupons()
  122.     {
  123.         return static::findByConditions(array("(type='cart' OR type='cart_group')""enableCode='1'"));
  124.     }
  125.     /**
  126.      * @deprecated Deprecated since 2.1.9, to be removed in 3.0
  127.      * @see Rule::findActiveWithoutCoupons
  128.      */
  129.     public static function findActiveWitoutCoupons()
  130.     {
  131.         return static::findActiveWithoutCoupons();
  132.     }
  133.     public static function findActiveWithoutCoupons()
  134.     {
  135.         return static::findByConditions(array("(type='product' OR ((type='cart' OR type='cart_group') AND enableCode=''))"));
  136.     }
  137.     /**
  138.      * @param string $strCode
  139.      * @param array $arrCollectionItems
  140.      *
  141.      * @return Rule|null
  142.      */
  143.     public static function findOneByCouponCode($strCode$arrCollectionItems)
  144.     {
  145.         $objRules = static::findByConditions(array("(type='cart' OR type='cart_group')""enableCode='1'"'code=?'"groupOnly=''"), array($strCode), $arrCollectionItems);
  146.         if (null !== $objRules) {
  147.             return $objRules->current();
  148.         }
  149.         return null;
  150.     }
  151.     /**
  152.      * Fetch rules
  153.      */
  154.     protected static function findByConditions($arrProcedures$arrValues = array(), $arrProducts null$blnIncludeVariants false$arrAttributeData = array())
  155.     {
  156.         // Only enabled rules
  157.         $arrProcedures[] = "enabled='1'";
  158.         // Date & Time restrictions
  159.         $date date('Y-m-d H:i:s');
  160.         $time date('H:i:s');
  161.         $arrProcedures[] = "(startDate='' OR startDate <= UNIX_TIMESTAMP('$date'))";
  162.         $arrProcedures[] = "(endDate='' OR endDate >= UNIX_TIMESTAMP('$date'))";
  163.         $arrProcedures[] = "(startTime='' OR startTime <= UNIX_TIMESTAMP('1970-01-01 $time'))";
  164.         $arrProcedures[] = "(endTime='' OR endTime >= UNIX_TIMESTAMP('1970-01-01 $time'))";
  165.         // Limits
  166.         $arrProcedures[] = "(limitPerConfig=0 OR limitPerConfig>(SELECT COUNT(*) FROM tl_iso_rule_usage WHERE pid=r.id AND config_id=" . (int) Isotope::getConfig()->id " AND order_id NOT IN (SELECT id FROM tl_iso_product_collection WHERE type='order' AND source_collection_id=" . (int) Isotope::getCart()->id ")))";
  167.         if (Isotope::getCart()->member 0) {
  168.             $arrProcedures[] = "(limitPerMember=0 OR limitPerMember>(SELECT COUNT(*) FROM tl_iso_rule_usage WHERE pid=r.id AND member_id=" . (int) Isotope::getCart()->member " AND order_id NOT IN (SELECT id FROM tl_iso_product_collection WHERE type='order' AND source_collection_id=" . (int) Isotope::getCart()->id ")))";
  169.         }
  170.         // Store config restrictions
  171.         $arrProcedures[] = "(configRestrictions=''
  172.                             OR (configRestrictions='1' AND configCondition='1' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='configs' AND object_id=" . (int) Isotope::getConfig()->id ")>0)
  173.                             OR (configRestrictions='1' AND configCondition='0' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='configs' AND object_id=" . (int) Isotope::getConfig()->id ")=0))";
  174.         // Member restrictions
  175.         if (Isotope::getCart()->member 0) {
  176.             $objMember MemberModel::findByPk(Isotope::getCart()->member);
  177.             $arrGroups = (null === $objMember) ? array() : array_map('intval'StringUtil::deserialize($objMember->groupstrue));
  178.             $arrProcedures[] = "(memberRestrictions='none'
  179.                                 OR (memberRestrictions='guests' AND memberCondition='0')
  180.                                 OR (memberRestrictions='members' AND memberCondition='1' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='members' AND object_id=" . (int) Isotope::getCart()->member ")>0)
  181.                                 OR (memberRestrictions='members' AND memberCondition='0' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='members' AND object_id=" . (int) Isotope::getCart()->member ")=0)
  182.                                 " . (!empty($arrGroups) ? "
  183.                                 OR (memberRestrictions='groups' AND memberCondition='1' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='groups' AND object_id IN (" implode(','$arrGroups) . "))>0)
  184.                                 OR (memberRestrictions='groups' AND memberCondition='0' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='groups' AND object_id IN (" implode(','$arrGroups) . "))=0)" '') . ")";
  185.         } else {
  186.             $arrProcedures[] = "(memberRestrictions='none' OR (memberRestrictions='guests' AND memberCondition='1'))";
  187.         }
  188.         // Product restrictions
  189.         if (!\is_array($arrProducts)) {
  190.             $arrProducts Isotope::getCart()->getItems();
  191.         }
  192.         if (!empty($arrProducts)) {
  193.             $arrProductIds = [0];
  194.             $arrVariantIds = [0];
  195.             $arrAttributes = [];
  196.             $arrTypes = [0];
  197.             // Prepare product attribute condition
  198.             $objAttributeRules Database::getInstance()->execute("SELECT attributeName, attributeCondition FROM " . static::$strTable " WHERE enabled='1' AND productRestrictions='attribute' AND attributeName!='' GROUP BY attributeName, attributeCondition");
  199.             while ($objAttributeRules->next()) {
  200.                 $arrAttributes[] = array
  201.                 (
  202.                     'attribute' => $objAttributeRules->attributeName,
  203.                     'condition' => $objAttributeRules->attributeCondition,
  204.                     'values'    => [],
  205.                 );
  206.             }
  207.             foreach ($arrProducts as $objProduct) {
  208.                 if ($objProduct instanceof ProductCollectionItem) {
  209.                     if (!$objProduct->hasProduct()) {
  210.                         continue;
  211.                     }
  212.                     $objProduct $objProduct->getProduct();
  213.                 }
  214.                 $arrProductIds[] = (int) $objProduct->getProductId();
  215.                 $arrVariantIds[] = (int) $objProduct->{$objProduct->getPk()};
  216.                 $arrTypes[] = (int) $objProduct->type;
  217.                 if ($objProduct->isVariant()) {
  218.                     $arrVariantIds[] = (int) $objProduct->pid;
  219.                 }
  220.                 if ($blnIncludeVariants && $objProduct->hasVariants()) {
  221.                     $arrVariantIds array_merge($arrVariantIds$objProduct->getVariantIds());
  222.                 }
  223.                 $arrOptions $objProduct->getOptions();
  224.                 foreach ($arrAttributes as $k => $restriction) {
  225.                     $varValue null;
  226.                     if (isset($arrAttributeData[$restriction['attribute']])) {
  227.                         $varValue $arrAttributeData[$restriction['attribute']];
  228.                     } elseif (isset($arrOptions[$restriction['attribute']])) {
  229.                         $varValue $arrOptions[$restriction['attribute']];
  230.                     } else {
  231.                         $varValue $objProduct->{$restriction['attribute']};
  232.                     }
  233.                     if (!\is_null($varValue)) {
  234.                         $arrAttributes[$k]['values'][] = \is_array($varValue) ? serialize($varValue) : $varValue;
  235.                     }
  236.                 }
  237.             }
  238.             $arrProductIds array_unique($arrProductIds);
  239.             $arrVariantIds array_unique($arrVariantIds);
  240.             $arrRestrictions = array("productRestrictions='none'");
  241.             $arrRestrictions[] = "(productRestrictions='producttypes' AND productCondition='1' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='producttypes' AND object_id IN (" implode(','$arrTypes) . "))>0)";
  242.             $arrRestrictions[] = "(productRestrictions='producttypes' AND productCondition='0' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='producttypes' AND object_id IN (" implode(','$arrTypes) . "))=0)";
  243.             $arrRestrictions[] = "(productRestrictions='products' AND productCondition='1' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='products' AND object_id IN (" implode(','$arrProductIds) . "))>0)";
  244.             $arrRestrictions[] = "(productRestrictions='products' AND productCondition='0' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='products' AND object_id IN (" implode(','$arrProductIds) . "))=0)";
  245.             $arrRestrictions[] = "(productRestrictions='variants' AND productCondition='1' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='variants' AND object_id IN (" implode(','$arrVariantIds) . "))>0)";
  246.             $arrRestrictions[] = "(productRestrictions='variants' AND productCondition='0' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='variants' AND object_id IN (" implode(','$arrVariantIds) . "))=0)";
  247.             $arrRestrictions[] = "(productRestrictions='pages' AND productCondition='1' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='pages' AND object_id IN (SELECT page_id FROM " ProductCategory::getTable() . " WHERE pid IN (" implode(','$arrProductIds) . ")))>0)";
  248.             $arrRestrictions[] = "(productRestrictions='pages' AND productCondition='0' AND (SELECT COUNT(*) FROM tl_iso_rule_restriction WHERE pid=r.id AND type='pages' AND object_id IN (SELECT page_id FROM " ProductCategory::getTable() . " WHERE pid IN (" implode(','$arrProductIds) . ")))=0)";
  249.             foreach ($arrAttributes as $restriction) {
  250.                 if (empty($restriction['values'])) {
  251.                     continue;
  252.                 }
  253.                 $strRestriction "(productRestrictions='attribute' AND attributeName='" $restriction['attribute'] . "' AND attributeCondition='" $restriction['condition'] . "' AND ";
  254.                 switch ($restriction['condition']) {
  255.                     case 'eq':
  256.                     case 'neq':
  257.                         $strRestriction .= sprintf(
  258.                             "attributeValue %s IN (%s)",
  259.                             ('neq' === $restriction['condition'] ? 'NOT' ''),
  260.                             implode(', 'array_fill(0, \count($restriction['values']), '?'))
  261.                         );
  262.                         $arrValues array_merge($arrValues$restriction['values']);
  263.                         break;
  264.                     case 'lt':
  265.                     case 'gt':
  266.                     case 'elt':
  267.                     case 'egt':
  268.                         $arrOR = array();
  269.                         foreach ($restriction['values'] as $value) {
  270.                             $arrOR[] = sprintf(
  271.                                 'attributeValue %s%s ?',
  272.                                 (('lt' === $restriction['condition'] || 'elt' === $restriction['condition']) ? '>' '<'),
  273.                                 (('elt' === $restriction['condition'] || 'egt' === $restriction['condition']) ? '=' '')
  274.                             );
  275.                             $arrValues[] = $value;
  276.                         }
  277.                         $strRestriction .= '(' implode(' OR '$arrOR) . ')';
  278.                         break;
  279.                     case 'starts':
  280.                     case 'ends':
  281.                     case 'contains':
  282.                         $arrOR = array();
  283.                         foreach ($restriction['values'] as $value) {
  284.                             $arrOR[] = sprintf(
  285.                                 "? LIKE CONCAT(%sattributeValue%s)",
  286.                                 (('ends' === $restriction['condition'] || 'contains' === $restriction['condition']) ? "'%', " ''),
  287.                                 (('starts' === $restriction['condition'] || 'contains' === $restriction['condition']) ? ", '%'" '')
  288.                             );
  289.                             $arrValues[] = $value;
  290.                         }
  291.                         $strRestriction .= '(' implode(' OR '$arrOR) . ')';
  292.                         break;
  293.                     default:
  294.                         throw new \InvalidArgumentException(
  295.                             sprintf('Unknown rule condition "%s"'$restriction['condition'])
  296.                         );
  297.                 }
  298.                 $arrRestrictions[] = $strRestriction ')';
  299.             }
  300.             $arrProcedures[] = '(' implode(' OR '$arrRestrictions) . ')';
  301.         }
  302.         $objResult Database::getInstance()
  303.             ->prepare('SELECT * FROM tl_iso_rule r WHERE ' implode(' AND '$arrProcedures))
  304.             ->execute($arrValues)
  305.         ;
  306.         if ($objResult->numRows) {
  307.             return Collection::createFromDbResult($objResult, static::$strTable);
  308.         }
  309.         return null;
  310.     }
  311. }