1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26:
27: 28: 29: 30: 31: 32: 33:
34:
35: 36: 37: 38:
39: class Mage_Catalog_Model_Layer_Filter_Price extends Mage_Catalog_Model_Layer_Filter_Abstract
40: {
41: 42: 43:
44: const XML_PATH_RANGE_CALCULATION = 'catalog/layered_navigation/price_range_calculation';
45: const XML_PATH_RANGE_STEP = 'catalog/layered_navigation/price_range_step';
46: const XML_PATH_RANGE_MAX_INTERVALS = 'catalog/layered_navigation/price_range_max_intervals';
47: const XML_PATH_ONE_PRICE_INTERVAL = 'catalog/layered_navigation/one_price_interval';
48: const XML_PATH_INTERVAL_DIVISION_LIMIT = 'catalog/layered_navigation/interval_division_limit';
49:
50: 51: 52:
53: const RANGE_CALCULATION_AUTO = 'auto';
54: const RANGE_CALCULATION_IMPROVED = 'improved';
55: const RANGE_CALCULATION_MANUAL = 'manual';
56:
57: 58: 59:
60: const MIN_RANGE_POWER = 10;
61:
62: 63: 64: 65: 66:
67: protected $_resource;
68:
69: 70: 71: 72:
73: public function __construct()
74: {
75: parent::__construct();
76: $this->_requestVar = 'price';
77: }
78:
79: 80: 81: 82: 83:
84: protected function _getResource()
85: {
86: if (is_null($this->_resource)) {
87: $this->_resource = Mage::getResourceModel('catalog/layer_filter_price');
88: }
89: return $this->_resource;
90: }
91:
92: 93: 94: 95: 96:
97: public function getPriceRange()
98: {
99: $range = $this->getData('price_range');
100: if (!$range) {
101: $currentCategory = Mage::registry('current_category_filter');
102: if ($currentCategory) {
103: $range = $currentCategory->getFilterPriceRange();
104: } else {
105: $range = $this->getLayer()->getCurrentCategory()->getFilterPriceRange();
106: }
107:
108: $maxPrice = $this->getMaxPriceInt();
109: if (!$range) {
110: $calculation = Mage::app()->getStore()->getConfig(self::XML_PATH_RANGE_CALCULATION);
111: if ($calculation == self::RANGE_CALCULATION_AUTO) {
112: $index = 1;
113: do {
114: $range = pow(10, (strlen(floor($maxPrice)) - $index));
115: $items = $this->getRangeItemCounts($range);
116: $index++;
117: }
118: while($range > self::MIN_RANGE_POWER && count($items) < 2);
119: } else {
120: $range = (float)Mage::app()->getStore()->getConfig(self::XML_PATH_RANGE_STEP);
121: }
122: }
123:
124: $this->setData('price_range', $range);
125: }
126:
127: return $range;
128: }
129:
130: 131: 132: 133: 134:
135: public function getMaxPriceInt()
136: {
137: $maxPrice = $this->getData('max_price_int');
138: if (is_null($maxPrice)) {
139: $maxPrice = $this->getLayer()->getProductCollection()->getMaxPrice();
140: $maxPrice = floor($maxPrice);
141: $this->setData('max_price_int', $maxPrice);
142: }
143:
144: return $maxPrice;
145: }
146:
147: 148: 149: 150: 151: 152:
153: public function getRangeItemCounts($range)
154: {
155: $rangeKey = 'range_item_counts_' . $range;
156: $items = $this->getData($rangeKey);
157: if (is_null($items)) {
158: $items = $this->_getResource()->getCount($this, $range);
159:
160: $i = 0;
161: $lastIndex = null;
162: $maxIntervalsNumber = $this->getMaxIntervalsNumber();
163: $calculation = Mage::app()->getStore()->getConfig(self::XML_PATH_RANGE_CALCULATION);
164: foreach ($items as $k => $v) {
165: ++$i;
166: if ($calculation == self::RANGE_CALCULATION_MANUAL && $i > 1 && $i > $maxIntervalsNumber) {
167: $items[$lastIndex] += $v;
168: unset($items[$k]);
169: } else {
170: $lastIndex = $k;
171: }
172: }
173: $this->setData($rangeKey, $items);
174: }
175:
176: return $items;
177: }
178:
179: 180: 181: 182: 183: 184: 185: 186:
187: protected function _renderItemLabel($range, $value)
188: {
189: $store = Mage::app()->getStore();
190: $fromPrice = $store->formatPrice(($value - 1) * $range);
191: $toPrice = $store->formatPrice($value*$range);
192:
193: return Mage::helper('catalog')->__('%s - %s', $fromPrice, $toPrice);
194: }
195:
196: 197: 198: 199: 200: 201: 202:
203: protected function _renderRangeLabel($fromPrice, $toPrice)
204: {
205: $store = Mage::app()->getStore();
206: $formattedFromPrice = $store->formatPrice($fromPrice);
207: if ($toPrice === '') {
208: return Mage::helper('catalog')->__('%s and above', $formattedFromPrice);
209: } elseif ($fromPrice == $toPrice && Mage::app()->getStore()->getConfig(self::XML_PATH_ONE_PRICE_INTERVAL)) {
210: return $formattedFromPrice;
211: } else {
212: if ($fromPrice != $toPrice) {
213: $toPrice -= .01;
214: }
215: return Mage::helper('catalog')->__('%s - %s', $formattedFromPrice, $store->formatPrice($toPrice));
216: }
217: }
218:
219: 220: 221: 222: 223:
224: protected function _getCacheKey()
225: {
226: $key = $this->getLayer()->getStateKey()
227: . '_PRICES_GRP_' . Mage::getSingleton('customer/session')->getCustomerGroupId()
228: . '_CURR_' . Mage::app()->getStore()->getCurrentCurrencyCode()
229: . '_ATTR_' . $this->getAttributeModel()->getAttributeCode()
230: . '_LOC_'
231: ;
232: $taxReq = Mage::getSingleton('tax/calculation')->getRateRequest(false, false, false);
233: $key.= implode('_', $taxReq->getData());
234:
235: return $key;
236: }
237:
238: 239: 240: 241: 242:
243: protected function _getAdditionalRequestData()
244: {
245: $result = '';
246: $appliedInterval = $this->getInterval();
247: if ($appliedInterval) {
248: $result = ',' . $appliedInterval[0] . '-' . $appliedInterval[1];
249: $priorIntervals = $this->getResetValue();
250: if ($priorIntervals) {
251: $result .= ',' . $priorIntervals;
252: }
253: }
254:
255: return $result;
256: }
257:
258: 259: 260: 261: 262:
263: protected function _getCalculatedItemsData()
264: {
265:
266: $algorithmModel = Mage::getSingleton('catalog/layer_filter_price_algorithm');
267: $collection = $this->getLayer()->getProductCollection();
268: $appliedInterval = $this->getInterval();
269: if ($appliedInterval
270: && $collection->getPricesCount() <= $this->getIntervalDivisionLimit()
271: ) {
272: return array();
273: }
274: $algorithmModel->setPricesModel($this)->setStatistics(
275: $collection->getMinPrice(),
276: $collection->getMaxPrice(),
277: $collection->getPriceStandardDeviation(),
278: $collection->getPricesCount()
279: );
280:
281: if ($appliedInterval) {
282: if ($appliedInterval[0] == $appliedInterval[1] || $appliedInterval[1] === '0') {
283: return array();
284: }
285: $algorithmModel->setLimits($appliedInterval[0], $appliedInterval[1]);
286: }
287:
288: $items = array();
289: foreach ($algorithmModel->calculateSeparators() as $separator) {
290: $items[] = array(
291: 'label' => $this->_renderRangeLabel($separator['from'], $separator['to']),
292: 'value' => (($separator['from'] == 0) ? '' : $separator['from'])
293: . '-' . $separator['to'] . $this->_getAdditionalRequestData(),
294: 'count' => $separator['count'],
295: );
296: }
297:
298: return $items;
299: }
300:
301: 302: 303: 304: 305:
306: protected function _getItemsData()
307: {
308: if (Mage::app()->getStore()->getConfig(self::XML_PATH_RANGE_CALCULATION) == self::RANGE_CALCULATION_IMPROVED) {
309: return $this->_getCalculatedItemsData();
310: } elseif ($this->getInterval()) {
311: return array();
312: }
313:
314: $range = $this->getPriceRange();
315: $dbRanges = $this->getRangeItemCounts($range);
316: $data = array();
317:
318: if (!empty($dbRanges)) {
319: $lastIndex = array_keys($dbRanges);
320: $lastIndex = $lastIndex[count($lastIndex) - 1];
321:
322: foreach ($dbRanges as $index => $count) {
323: $fromPrice = ($index == 1) ? '' : (($index - 1) * $range);
324: $toPrice = ($index == $lastIndex) ? '' : ($index * $range);
325:
326: $data[] = array(
327: 'label' => $this->_renderRangeLabel($fromPrice, $toPrice),
328: 'value' => $fromPrice . '-' . $toPrice,
329: 'count' => $count,
330: );
331: }
332: }
333:
334: return $data;
335: }
336:
337: 338: 339: 340: 341:
342: protected function _applyPriceRange()
343: {
344: $this->_getResource()->applyPriceRange($this);
345: return $this;
346: }
347:
348: 349: 350: 351: 352: 353:
354: protected function _validateFilter($filter)
355: {
356: $filter = explode('-', $filter);
357: if (count($filter) != 2) {
358: return false;
359: }
360: foreach ($filter as $v) {
361: if (($v !== '' && $v !== '0' && (float)$v <= 0) || is_infinite((float)$v)) {
362: return false;
363: }
364: }
365:
366: return $filter;
367: }
368:
369: 370: 371: 372: 373: 374: 375: 376:
377: public function apply(Zend_Controller_Request_Abstract $request, $filterBlock)
378: {
379: 380: 381:
382: $filter = $request->getParam($this->getRequestVar());
383: if (!$filter) {
384: return $this;
385: }
386:
387:
388: $filterParams = explode(',', $filter);
389: $filter = $this->_validateFilter($filterParams[0]);
390: if (!$filter) {
391: return $this;
392: }
393:
394: list($from, $to) = $filter;
395:
396: $this->setInterval(array($from, $to));
397:
398: $priorFilters = array();
399: for ($i = 1; $i < count($filterParams); ++$i) {
400: $priorFilter = $this->_validateFilter($filterParams[$i]);
401: if ($priorFilter) {
402: $priorFilters[] = $priorFilter;
403: } else {
404:
405: $priorFilters = array();
406: break;
407: }
408: }
409: if ($priorFilters) {
410: $this->setPriorIntervals($priorFilters);
411: }
412:
413: $this->_applyPriceRange();
414: $this->getLayer()->getState()->addFilter($this->_createItem(
415: $this->_renderRangeLabel(empty($from) ? 0 : $from, $to),
416: $filter
417: ));
418:
419: return $this;
420: }
421:
422: 423: 424: 425: 426: 427: 428: 429:
430: protected function _applyToCollection($range, $index)
431: {
432: $this->_getResource()->applyFilterToCollection($this, $range, $index);
433: return $this;
434: }
435:
436: 437: 438: 439: 440:
441: public function getCustomerGroupId()
442: {
443: $customerGroupId = $this->_getData('customer_group_id');
444: if (is_null($customerGroupId)) {
445: $customerGroupId = Mage::getSingleton('customer/session')->getCustomerGroupId();
446: }
447: return $customerGroupId;
448: }
449:
450: 451: 452: 453: 454: 455:
456: public function setCustomerGroupId($customerGroupId)
457: {
458: return $this->setData('customer_group_id', $customerGroupId);
459: }
460:
461: 462: 463: 464: 465:
466: public function getCurrencyRate()
467: {
468: $rate = $this->_getData('currency_rate');
469: if (is_null($rate)) {
470: $rate = Mage::app()->getStore($this->getStoreId())->getCurrentCurrencyRate();
471: }
472: if (!$rate) {
473: $rate = 1;
474: }
475: return $rate;
476: }
477:
478: 479: 480: 481: 482: 483:
484: public function setCurrencyRate($rate)
485: {
486: return $this->setData('currency_rate', $rate);
487: }
488:
489: 490: 491: 492: 493:
494: public function getMaxIntervalsNumber()
495: {
496: return (int)Mage::app()->getStore()->getConfig(self::XML_PATH_RANGE_MAX_INTERVALS);
497: }
498:
499: 500: 501: 502: 503:
504: public function getIntervalDivisionLimit()
505: {
506: return (int)Mage::app()->getStore()->getConfig(self::XML_PATH_INTERVAL_DIVISION_LIMIT);
507: }
508:
509: 510: 511: 512: 513:
514: public function getResetValue()
515: {
516: $priorIntervals = $this->getPriorIntervals();
517: $value = array();
518: if ($priorIntervals) {
519: foreach ($priorIntervals as $priorInterval) {
520: $value[] = implode('-', $priorInterval);
521: }
522: return implode(',', $value);
523: }
524: return parent::getResetValue();
525: }
526:
527: 528: 529: 530: 531:
532: public function getClearLinkText()
533: {
534: if (Mage::app()->getStore()->getConfig(self::XML_PATH_RANGE_CALCULATION) == self::RANGE_CALCULATION_IMPROVED
535: && $this->getPriorIntervals()
536: ) {
537: return Mage::helper('catalog')->__('Clear Price');
538: }
539:
540: return parent::getClearLinkText();
541: }
542:
543: 544: 545: 546: 547: 548: 549: 550: 551:
552: public function loadPrices($limit, $offset = null, $lowerPrice = null, $upperPrice = null)
553: {
554: $prices = $this->_getResource()->loadPrices($this, $limit, $offset, $lowerPrice, $upperPrice);
555: if ($prices) {
556: $prices = array_map('floatval', $prices);
557: }
558:
559: return $prices;
560: }
561:
562: 563: 564: 565: 566: 567: 568: 569:
570: public function loadPreviousPrices($price, $index, $lowerPrice = null)
571: {
572: $prices = $this->_getResource()->loadPreviousPrices($this, $price, $index, $lowerPrice);
573: if ($prices) {
574: $prices = array_map('floatval', $prices);
575: }
576:
577: return $prices;
578: }
579:
580: 581: 582: 583: 584: 585: 586: 587:
588: public function loadNextPrices($price, $rightIndex, $upperPrice = null)
589: {
590: $prices = $this->_getResource()->loadNextPrices($this, $price, $rightIndex, $upperPrice);
591: if ($prices) {
592: $prices = array_map('floatval', $prices);
593: }
594:
595: return $prices;
596: }
597: }
598: