1: <?php
2: /**
3: * Magento
4: *
5: * NOTICE OF LICENSE
6: *
7: * This source file is subject to the Open Software License (OSL 3.0)
8: * that is bundled with this package in the file LICENSE.txt.
9: * It is also available through the world-wide-web at this URL:
10: * http://opensource.org/licenses/osl-3.0.php
11: * If you did not receive a copy of the license and are unable to
12: * obtain it through the world-wide-web, please send an email
13: * to license@magentocommerce.com so we can send you a copy immediately.
14: *
15: * DISCLAIMER
16: *
17: * Do not edit or add to this file if you wish to upgrade Magento to newer
18: * versions in the future. If you wish to customize Magento for your
19: * needs please refer to http://www.magentocommerce.com for more information.
20: *
21: * @category Mage
22: * @package Mage_CatalogRule
23: * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
24: * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25: */
26:
27:
28: /**
29: * Catalog Rule data model
30: *
31: * @method Mage_CatalogRule_Model_Resource_Rule _getResource()
32: * @method Mage_CatalogRule_Model_Resource_Rule getResource()
33: * @method string getName()
34: * @method Mage_CatalogRule_Model_Rule setName(string $value)
35: * @method string getDescription()
36: * @method Mage_CatalogRule_Model_Rule setDescription(string $value)
37: * @method string getFromDate()
38: * @method Mage_CatalogRule_Model_Rule setFromDate(string $value)
39: * @method string getToDate()
40: * @method Mage_CatalogRule_Model_Rule setToDate(string $value)
41: * @method Mage_CatalogRule_Model_Rule setCustomerGroupIds(string $value)
42: * @method int getIsActive()
43: * @method Mage_CatalogRule_Model_Rule setIsActive(int $value)
44: * @method string getConditionsSerialized()
45: * @method Mage_CatalogRule_Model_Rule setConditionsSerialized(string $value)
46: * @method string getActionsSerialized()
47: * @method Mage_CatalogRule_Model_Rule setActionsSerialized(string $value)
48: * @method int getStopRulesProcessing()
49: * @method Mage_CatalogRule_Model_Rule setStopRulesProcessing(int $value)
50: * @method int getSortOrder()
51: * @method Mage_CatalogRule_Model_Rule setSortOrder(int $value)
52: * @method string getSimpleAction()
53: * @method Mage_CatalogRule_Model_Rule setSimpleAction(string $value)
54: * @method float getDiscountAmount()
55: * @method Mage_CatalogRule_Model_Rule setDiscountAmount(float $value)
56: * @method string getWebsiteIds()
57: * @method Mage_CatalogRule_Model_Rule setWebsiteIds(string $value)
58: *
59: * @category Mage
60: * @package Mage_CatalogRule
61: * @author Magento Core Team <core@magentocommerce.com>
62: */
63: class Mage_CatalogRule_Model_Rule extends Mage_Rule_Model_Abstract
64: {
65: /**
66: * Related cache types config path
67: */
68: const XML_NODE_RELATED_CACHE = 'global/catalogrule/related_cache_types';
69:
70: /**
71: * Prefix of model events names
72: *
73: * @var string
74: */
75: protected $_eventPrefix = 'catalogrule_rule';
76:
77: /**
78: * Parameter name in event
79: *
80: * In observe method you can use $observer->getEvent()->getRule() in this case
81: *
82: * @var string
83: */
84: protected $_eventObject = 'rule';
85:
86: /**
87: * Store matched product Ids
88: *
89: * @var array
90: */
91: protected $_productIds;
92:
93: /**
94: * Limitation for products collection
95: *
96: * @var int|array|null
97: */
98: protected $_productsFilter = null;
99:
100: /**
101: * Store current date at "Y-m-d H:i:s" format
102: *
103: * @var string
104: */
105: protected $_now;
106:
107: /**
108: * Cached data of prices calculated by price rules
109: *
110: * @var array
111: */
112: protected static $_priceRulesData = array();
113:
114: /**
115: * Init resource model and id field
116: */
117: protected function _construct()
118: {
119: parent::_construct();
120: $this->_init('catalogrule/rule');
121: $this->setIdFieldName('rule_id');
122: }
123:
124: /**
125: * Getter for rule conditions collection
126: *
127: * @return Mage_CatalogRule_Model_Rule_Condition_Combine
128: */
129: public function getConditionsInstance()
130: {
131: return Mage::getModel('catalogrule/rule_condition_combine');
132: }
133:
134: /**
135: * Getter for rule actions collection
136: *
137: * @return Mage_CatalogRule_Model_Rule_Action_Collection
138: */
139: public function getActionsInstance()
140: {
141: return Mage::getModel('catalogrule/rule_action_collection');
142: }
143:
144: /**
145: * Get catalog rule customer group Ids
146: *
147: * @return array
148: */
149: public function getCustomerGroupIds()
150: {
151: if (!$this->hasCustomerGroupIds()) {
152: $customerGroupIds = $this->_getResource()->getCustomerGroupIds($this->getId());
153: $this->setData('customer_group_ids', (array)$customerGroupIds);
154: }
155: return $this->_getData('customer_group_ids');
156: }
157:
158: /**
159: * Retrieve current date for current rule
160: *
161: * @return string
162: */
163: public function getNow()
164: {
165: if (!$this->_now) {
166: return now();
167: }
168: return $this->_now;
169: }
170:
171: /**
172: * Set current date for current rule
173: *
174: * @param string $now
175: */
176: public function setNow($now)
177: {
178: $this->_now = $now;
179: }
180:
181: /**
182: * Get array of product ids which are matched by rule
183: *
184: * @return array
185: */
186: public function getMatchingProductIds()
187: {
188: if (is_null($this->_productIds)) {
189: $this->_productIds = array();
190: $this->setCollectedAttributes(array());
191:
192: if ($this->getWebsiteIds()) {
193: /** @var $productCollection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection */
194: $productCollection = Mage::getResourceModel('catalog/product_collection');
195: $productCollection->addWebsiteFilter($this->getWebsiteIds());
196: if ($this->_productsFilter) {
197: $productCollection->addIdFilter($this->_productsFilter);
198: }
199: $this->getConditions()->collectValidatedAttributes($productCollection);
200:
201: Mage::getSingleton('core/resource_iterator')->walk(
202: $productCollection->getSelect(),
203: array(array($this, 'callbackValidateProduct')),
204: array(
205: 'attributes' => $this->getCollectedAttributes(),
206: 'product' => Mage::getModel('catalog/product'),
207: )
208: );
209: }
210: }
211:
212: return $this->_productIds;
213: }
214:
215: /**
216: * Callback function for product matching
217: *
218: * @param $args
219: * @return void
220: */
221: public function callbackValidateProduct($args)
222: {
223: $product = clone $args['product'];
224: $product->setData($args['row']);
225:
226: if ($this->getConditions()->validate($product)) {
227: $this->_productIds[] = $product->getId();
228: }
229: }
230:
231: /**
232: * Apply rule to product
233: *
234: * @param int|Mage_Catalog_Model_Product $product
235: * @param array|null $websiteIds
236: *
237: * @return void
238: */
239: public function applyToProduct($product, $websiteIds = null)
240: {
241: if (is_numeric($product)) {
242: $product = Mage::getModel('catalog/product')->load($product);
243: }
244: if (is_null($websiteIds)) {
245: $websiteIds = $this->getWebsiteIds();
246: }
247: $this->getResource()->applyToProduct($this, $product, $websiteIds);
248: }
249:
250: /**
251: * Apply all price rules, invalidate related cache and refresh price index
252: *
253: * @return Mage_CatalogRule_Model_Rule
254: */
255: public function applyAll()
256: {
257: $this->getResourceCollection()->walk(array($this->_getResource(), 'updateRuleProductData'));
258: $this->_getResource()->applyAllRulesForDateRange();
259: $this->_invalidateCache();
260: $indexProcess = Mage::getSingleton('index/indexer')->getProcessByCode('catalog_product_price');
261: if ($indexProcess) {
262: $indexProcess->reindexAll();
263: }
264: }
265:
266: /**
267: * Apply all price rules to product
268: *
269: * @param int|Mage_Catalog_Model_Product $product
270: * @return Mage_CatalogRule_Model_Rule
271: */
272: public function applyAllRulesToProduct($product)
273: {
274: $this->_getResource()->applyAllRulesForDateRange(NULL, NULL, $product);
275: $this->_invalidateCache();
276:
277: if ($product instanceof Mage_Catalog_Model_Product) {
278: $productId = $product->getId();
279: } else {
280: $productId = $product;
281: }
282:
283: if ($productId) {
284: Mage::getSingleton('index/indexer')->processEntityAction(
285: new Varien_Object(array('id' => $productId)),
286: Mage_Catalog_Model_Product::ENTITY,
287: Mage_Catalog_Model_Product_Indexer_Price::EVENT_TYPE_REINDEX_PRICE
288: );
289: }
290: }
291:
292: /**
293: * Calculate price using catalog price rule of product
294: *
295: * @param Mage_Catalog_Model_Product $product
296: * @param float $price
297: * @return float|null
298: */
299: public function calcProductPriceRule(Mage_Catalog_Model_Product $product, $price)
300: {
301: $priceRules = null;
302: $productId = $product->getId();
303: $storeId = $product->getStoreId();
304: $websiteId = Mage::app()->getStore($storeId)->getWebsiteId();
305: if ($product->hasCustomerGroupId()) {
306: $customerGroupId = $product->getCustomerGroupId();
307: } else {
308: $customerGroupId = Mage::getSingleton('customer/session')->getCustomerGroupId();
309: }
310: $dateTs = Mage::app()->getLocale()->storeTimeStamp($storeId);
311: $cacheKey = date('Y-m-d', $dateTs) . "|$websiteId|$customerGroupId|$productId|$price";
312:
313: if (!array_key_exists($cacheKey, self::$_priceRulesData)) {
314: $rulesData = $this->_getResource()->getRulesFromProduct($dateTs, $websiteId, $customerGroupId, $productId);
315: if ($rulesData) {
316: foreach ($rulesData as $ruleData) {
317: if ($product->getParentId()) {
318: if (!empty($ruleData['sub_simple_action'])) {
319: $priceRules = Mage::helper('catalogrule')->calcPriceRule(
320: $ruleData['sub_simple_action'],
321: $ruleData['sub_discount_amount'],
322: $priceRules ? $priceRules : $price
323: );
324: } else {
325: $priceRules = $price;
326: }
327: if ($ruleData['action_stop']) {
328: break;
329: }
330: } else {
331: $priceRules = Mage::helper('catalogrule')->calcPriceRule(
332: $ruleData['action_operator'],
333: $ruleData['action_amount'],
334: $priceRules ? $priceRules : $price
335: );
336: if ($ruleData['action_stop']) {
337: break;
338: }
339: }
340: }
341: return self::$_priceRulesData[$cacheKey] = $priceRules;
342: } else {
343: self::$_priceRulesData[$cacheKey] = null;
344: }
345: } else {
346: return self::$_priceRulesData[$cacheKey];
347: }
348: return null;
349: }
350:
351: /**
352: * Filtering products that must be checked for matching with rule
353: *
354: * @param int|array $productIds
355: */
356: public function setProductsFilter($productIds)
357: {
358: $this->_productsFilter = $productIds;
359: }
360:
361: /**
362: * Returns products filter
363: *
364: * @return array|int|null
365: */
366: public function getProductsFilter()
367: {
368: return $this->_productsFilter;
369: }
370:
371: /**
372: * Invalidate related cache types
373: *
374: * @return Mage_CatalogRule_Model_Rule
375: */
376: protected function _invalidateCache()
377: {
378: $types = Mage::getConfig()->getNode(self::XML_NODE_RELATED_CACHE);
379: if ($types) {
380: $types = $types->asArray();
381: Mage::app()->getCacheInstance()->invalidateType(array_keys($types));
382: }
383: return $this;
384: }
385:
386:
387:
388:
389: /**
390: * @deprecated after 1.11.2.0
391: *
392: * @param string $format
393: *
394: * @return string
395: */
396: public function toString($format='')
397: {
398: return '';
399: }
400:
401: /**
402: * Returns rule as an array for admin interface
403: *
404: * @deprecated after 1.11.2.0
405: *
406: * @param array $arrAttributes
407: *
408: * Output example:
409: * array(
410: * 'name'=>'Example rule',
411: * 'conditions'=>{condition_combine::toArray}
412: * 'actions'=>{action_collection::toArray}
413: * )
414: *
415: * @return array
416: */
417: public function toArray(array $arrAttributes = array())
418: {
419: return parent::toArray($arrAttributes);
420: }
421: }
422: