14if (Loader::includeModule(
'landing'))
18 protected $catalogIncluded =
null;
20 protected $saleIncluded =
null;
22 protected $iblockId = 0;
25 protected $catalog =
null;
27 protected $productFields =
null;
29 protected $priceTypes =
null;
34 public function __construct()
36 parent::__construct();
37 $this->catalogIncluded = Loader::includeModule(
'catalog');
38 $this->saleIncluded = Loader::includeModule(
'sale');
44 public function getElementListData()
50 $settings = self::getInternalSettings();
52 $rawSelect = $this->getPreparedSelectFields();
53 if (empty($rawSelect))
56 $rawOrder = $this->getOrder();
57 self::prepareOrder($settings, $rawOrder);
59 $rawSelect[] =
'DETAIL_PAGE_URL';
60 $this->prepareSelectFields($settings, $rawSelect);
63 $settings[
'filter'] = $this->getIblockElementListFilter();
65 $settings[
'limit'] = $this->getLimit();
67 $detailPageUrl = $this->getOptionsValue(
'detail_page_url');
68 if (!empty($detailPageUrl))
69 $settings[
'templates'][
'detailPageUrl'] = $detailPageUrl;
71 $settings[
'loadSeo'] =
false;
73 $settings[
'mode'] =
'list';
75 return array_values($this->getElementsInternal($settings));
82 public function getElementData($element)
89 if (!is_string($element) && !is_int($element))
91 $element = (int)$element;
95 $rawSelect = $this->getPreparedSelectFields();
96 if (empty($rawSelect))
99 $settings = self::getInternalSettings();
101 $this->prepareSelectFields($settings, $rawSelect);
105 if (!empty($internalFilter) && is_array($internalFilter))
106 $filter = array_merge($filter, $internalFilter);
107 $filter[
'ID'] = $element;
108 $settings[
'filter'] = $filter;
111 $settings[
'loadSeo'] =
true;
113 $settings[
'mode'] =
'detail';
115 $result = $this->getElementsInternal($settings);;
119 $result = reset($result);
122 $this->seo->setProperties($result[
'SEO_PROPERTIES']);
123 unset($result[
'SEO_PROPERTIES']);
134 private static function getInternalSettings()
139 'element_properties' => [],
145 'sortByPrice' =>
false,
153 protected function getPreparedSelectFields()
155 $result = parent::getPreparedSelectFields();
158 if (!in_array(
'ID', $result))
162 if (!in_array(
'IBLOCK_ID', $result))
164 $result[] =
'IBLOCK_ID';
166 if (!in_array(
'NAME', $result))
179 protected function getOrder()
181 $result = parent::getOrder();
182 if (!is_array($result))
194 protected function getLimit()
196 return (
int)$this->getSettingsValue(
'limit');
202 protected function initIblock()
204 $internalFilter = $this->getInternalFilter();
205 if (!empty($internalFilter) && is_array($internalFilter))
207 if (isset($internalFilter[
'IBLOCK_ID']))
208 $this->iblockId = (int)$internalFilter[
'IBLOCK_ID'];
209 if ($this->iblockId > 0 && $this->catalogIncluded)
211 $catalog = \CCatalogSku::GetInfoByProductIBlock($this->iblockId);
212 if (!empty($catalog))
213 $this->catalog = $catalog;
217 unset($internalFilter);
223 protected function getIblockElementListFilter()
225 $result = $this->compileIblockElementListFilter(
226 $this->getPreparedFilter($this->getFilterFieldsDescription())
228 $internalFilter = $this->getInternalFilter();
229 if (!empty($internalFilter) && is_array($internalFilter))
231 $result = array_merge($result, $internalFilter);
233 unset($internalFilter);
241 protected function compileIblockElementListFilter(array $filter)
244 if (isset($filter[
'master']))
245 $result = $filter[
'master'];
246 if (!empty($this->catalog) && isset($filter[
'offer']))
248 $subFilter = $filter[
'offer'];
249 $subFilter[
'IBLOCK_ID'] = $this->catalog[
'IBLOCK_ID'];
250 $result[
'=ID'] = \CIBlockElement::SubQuery(
'PROPERTY_'.$this->catalog[
'SKU_PROPERTY_ID'], $subFilter);
254 if (!isset($result[
'ACTIVE']))
255 $result[
'ACTIVE'] =
'Y';
264 private function prepareSelectFields(array &$settings, array $select)
266 $settings[
'rawSelect'] = $select;
267 if (!empty($settings[
'order']))
269 foreach (array_keys($settings[
'order']) as $field)
271 if (!in_array($field, $select))
276 $productFields = $this->getProductFields();
277 foreach ($select as $index => $field)
279 if ($field ==
'PRICE')
281 $settings[
'prices'] =
true;
282 unset($select[$index]);
284 elseif (isset($productFields[$field]))
286 $settings[
'product'][$field] = $productFields[$field];
287 unset($select[$index]);
289 elseif (strncmp($field,
'PROPERTY_', 9) == 0)
291 $propertyId = (int)mb_substr($field, 9);
293 $settings[
'element_properties'][$propertyId] = $propertyId;
294 unset($select[$index]);
298 unset($index, $field);
299 unset($productFields);
301 $settings[
'element'] = $select;
309 private static function prepareOrder(array &$settings, array $order)
311 if (!isset($order[
'by']) || !isset($order[
'order']))
313 $rawOrder = [$order[
'by'] => $order[
'order']];
314 $settings[
'rawOrder'] = $rawOrder;
315 if (isset($rawOrder[
'PRICE']))
317 $settings[
'sortByPrice'] =
true;
318 $settings[
'prices'] =
true;
319 unset($rawOrder[
'PRICE']);
321 $settings[
'order'] = $rawOrder;
327 protected function getProductFields()
329 if ($this->productFields ===
null)
332 if ($this->catalogIncluded)
334 $result = Catalog\ProductTable::getEntity()->getScalarFields();
335 unset($result[
'ID']);
336 $result = array_fill_keys(array_keys($result),
true);
338 $this->productFields = $result;
341 return $this->productFields;
344 private function getFilterFieldsDescription()
354 'quickSearch' => true
367 $result[
'SECTION_ID'] = [
368 'id' =>
'SECTION_ID',
374 $result[
'INCLUDE_SUBSECTIONS'] = [
375 'id' =>
'INCLUDE_SUBSECTIONS',
376 'type' =>
'checkbox',
381 $result[
'ACTIVE'] = [
388 $result[
'XML_ID'] = [
402 $result[
'ACTIVE_FROM'] = [
403 'id' =>
'ACTIVE_FROM',
410 $result[
'ACTIVE_TO'] = [
419 if ($this->catalogIncluded)
427 'params' => [
'multiple' =>
'Y']
429 $result[
'BUNDLE'] = [
436 $result[
'AVAILABLE'] = [
444 if ($this->iblockId > 0)
446 $properties = $this->getFilterProductPropertiesDescription();
447 if (!empty($properties))
448 $result = array_merge($result, $properties);
449 $properties = $this->getFilterOfferPropertiesDescription();
450 if (!empty($properties))
451 $result = array_merge($result, $properties);
458 private static function getFilterPropertiesDescription(array $filter)
463 Iblock\PropertyTable::TYPE_STRING => [
467 Iblock\PropertyTable::TYPE_NUMBER => [
474 Iblock\PropertyTable::TYPE_LIST => [
477 Iblock\PropertyTable::TYPE_ELEMENT => [
480 Iblock\PropertyTable::TYPE_SECTION => [
485 $iterator = Iblock\PropertyTable::getList([
487 'ID',
'IBLOCK_ID',
'NAME',
'SORT',
'PROPERTY_TYPE',
488 'MULTIPLE',
'LINK_IBLOCK_ID',
'FILTRABLE',
'VERSION',
489 'USER_TYPE',
'USER_TYPE_SETTINGS_LIST'
492 'order' => [
'SORT' =>
'ASC',
'NAME' =>
'ASC']
494 while ($row = $iterator->fetch())
496 $row[
'USER_TYPE'] = (string)$row[
'USER_TYPE'];
497 $row[
'PROPERTY_USER_TYPE'] = ($row[
'USER_TYPE'] !==
'' ? \CIBlockProperty::GetUserType($row[
'USER_TYPE']) : []);
498 $row[
'USER_TYPE_SETTINGS'] = $row[
'USER_TYPE_SETTINGS_LIST'];
499 unset($row[
'USER_TYPE_SETTINGS_LIST']);
502 $id =
'PROPERTY_'.$row[
'ID'];
503 $row[
'USER_TYPE'] = (string)$row[
'USER_TYPE'];
504 $type = $row[
'PROPERTY_TYPE'];
505 $settings = $row[
'USER_TYPE_SETTINGS'];
507 $row[
'USER_TYPE'] !==
''
509 && is_array($settings)
512 $row[
'PROPERTY_USER_TYPE'] = \CIBlockProperty::GetUserType($row[
'USER_TYPE']);
514 isset($row[
'PROPERTY_USER_TYPE'][
'GetUIFilterProperty'])
515 && is_callable($row[
'PROPERTY_USER_TYPE'][
'GetUIFilterProperty'])
528 if (isset($operators[$row[
'PROPERTY_TYPE']]))
529 $field[
'operators'] = $operators[$row[
'PROPERTY_TYPE']];
530 call_user_func_array(
531 $row[
'PROPERTY_USER_TYPE'][
'GetUIFilterProperty'],
534 [
'VALUE' => $id,
'FORM_NAME' =>
'main-ui-filter'],
539 case Iblock\PropertyTable::TYPE_STRING:
542 'operators' => $operators[Iblock\PropertyTable::TYPE_STRING],
545 case Iblock\PropertyTable::TYPE_NUMBER:
548 'operators' => $operators[Iblock\PropertyTable::TYPE_NUMBER]
551 case Iblock\PropertyTable::TYPE_LIST:
554 'operators' => $operators[Iblock\PropertyTable::TYPE_LIST]
557 case Iblock\PropertyTable::TYPE_ELEMENT:
560 'operators' => $operators[Iblock\PropertyTable::TYPE_ELEMENT]
563 case Iblock\PropertyTable::TYPE_SECTION:
566 'operators' => $operators[Iblock\PropertyTable::TYPE_SECTION]
574 $result[$id] = $field;
577 unset($row, $iterator);
582 private function getFilterProductPropertiesDescription()
584 return self::getFilterPropertiesDescription([
585 '=IBLOCK_ID' => $this->iblockId,
588 '!=PROPERTY_TYPE' => Iblock\PropertyTable::TYPE_FILE
592 private function getFilterOfferPropertiesDescription()
595 if (!empty($this->catalog))
597 $result = self::getFilterPropertiesDescription([
598 '=IBLOCK_ID' => $this->catalog[
'IBLOCK_ID'],
599 '!=ID' => $this->catalog[
'SKU_PROPERTY_ID'],
602 '!=PROPERTY_TYPE' => Iblock\PropertyTable::TYPE_FILE
606 foreach (array_keys($result) as $index)
607 $result[$index][
'entity'] =
'offer';
618 private function getElementsInternal(array $settings)
622 $iterator = \CIBlockElement::GetList(
626 ($settings[
'limit'] > 0 ? [
'nTopCount' => $settings[
'limit']] : false),
629 if (!empty($settings[
'templates'][
'detailPageUrl']))
630 $iterator->SetUrlTemplates($settings[
'templates'][
'detailPageUrl']);
632 $needPreview = in_array(
'PREVIEW_PICTURE', $settings[
'element']);
633 $needDetailPicture = in_array(
'DETAIL_PICTURE', $settings[
'element']);
635 $loadSeo = ($needPreview || $needDetailPicture || $settings[
'loadSeo']);
637 if (!empty($settings[
'element_properties']))
639 $settings[
'element_properties'] = $this->getAllowedPropertyId(
640 $settings[
'filter'][
'IBLOCK_ID'],
641 $settings[
'element_properties'],
642 ($settings[
'mode'] ==
'list'
643 ? Iblock\Model\PropertyFeature::FEATURE_ID_LIST_PAGE_SHOW
644 : Iblock\Model\PropertyFeature::FEATURE_ID_DETAIL_PAGE_SHOW
649 while ($row = $iterator->GetNext())
651 $id = (int)$row[
'ID'];
654 Iblock\InheritedProperty\ElementValues::queue($row[
'IBLOCK_ID'], $row[
'ID']);
657 $row[
'PROPERTIES'] = [];
661 unset($row, $iterator);
665 $needDiscountCache =
false;
666 if ($this->catalogIncluded && $settings[
'prices'])
668 $needDiscountCache = \CIBlockPriceTools::SetCatalogDiscountCache(
669 $this->getPriceTypes(),
670 $this->getUserGroups()
673 $loadProperties = (!empty($settings[
'element_properties']) || $needDiscountCache);
676 $propertyFilter = [];
677 if (!$needDiscountCache)
679 $propertyFilter = [
'ID' => $settings[
'element_properties']];
682 \CIBlockElement::GetPropertyValuesArray(
684 $settings[
'filter'][
'IBLOCK_ID'],
685 [
'ID' => array_keys($result)],
687 [
'USE_PROPERTY_ID' =>
'Y']
689 unset($propertyFilter);
691 if ($needDiscountCache)
693 $elementIds = array_keys($result);
694 foreach ($elementIds as $itemId)
696 \CCatalogDiscount::SetProductPropertiesCache($itemId, $result[$itemId][
'PROPERTIES']);
698 Catalog\Discount\DiscountManager::preloadPriceData($elementIds, $this->getPriceTypes());
699 Catalog\Discount\DiscountManager::preloadProductDataToExtendOrder($elementIds, $this->getUserGroups());
700 \CCatalogDiscount::SetProductSectionsCache($elementIds);
703 foreach ($result as &$row)
707 $ipropValues =
new Iblock\InheritedProperty\ElementValues($row[
'IBLOCK_ID'], $row[
'ID']);
708 $row[
'IPROPERTY_VALUES'] = $ipropValues->getValues();
710 if ($needPreview || $needDetailPicture)
712 Iblock\Component\Tools::getFieldImageData(
714 [
'PREVIEW_PICTURE',
'DETAIL_PICTURE'],
715 Iblock\Component\Tools::IPROPERTY_ENTITY_ELEMENT,
720 if (!empty($row[
'PREVIEW_PICTURE']))
722 $row[
'PREVIEW_PICTURE'] = [
723 'src' => $row[
'PREVIEW_PICTURE'][
'SRC'],
724 'alt' => $row[
'PREVIEW_PICTURE'][
'ALT'],
728 if ($needDetailPicture)
730 if (!empty($row[
'DETAIL_PICTURE']))
732 $row[
'DETAIL_PICTURE'] = [
733 'src' => $row[
'DETAIL_PICTURE'][
'SRC'],
734 'alt' => $row[
'DETAIL_PICTURE'][
'ALT'],
740 if (!empty($settings[
'element_properties']))
742 foreach ($settings[
'element_properties'] as $propertyId)
744 $row[
'PROPERTY_'.$propertyId] =
null;
745 if (isset($row[
'PROPERTIES'][$propertyId]))
747 $row[
'PROPERTY_'.$propertyId] = self::getPropertyValue($row[
'PROPERTIES'][$propertyId]);
752 unset($row[
'PROPERTIES']);
754 if ($settings[
'prices'])
756 $row[
'PRICE'] =
null;
757 $row[
'SORT_ORDER'] =
null;
760 if ($settings[
'loadSeo'])
762 $this->fillElementSeo($row);
767 unset($row[
'IPROPERTY_VALUES']);
773 if ($this->catalogIncluded)
775 if (!empty($settings[
'product']))
777 self::loadProduct($result, $settings[
'product']);
780 if ($settings[
'prices'])
782 $this->loadPrices($result);
783 if (!empty($settings[
'sortByPrice']))
785 $sortPrice = mb_strtoupper($settings[
'sortByPrice']);
786 $sortPrice = ($sortPrice ===
'ASC' ? SORT_ASC : SORT_DESC);
787 Main\Type\Collection::sortByColumn($result, [
'SORT_ORDER' => $sortPrice]);
789 foreach ($result as &$item)
791 unset($item[
'SORT_ORDER']);
794 if ($needDiscountCache)
796 \CCatalogDiscount::ClearDiscountCache(array(
812 private static function getPropertyValue(array $property)
816 $multiValue = is_array($property[
'VALUE']);
818 ($multiValue && !empty($property[
'VALUE'])) ||
819 (!$multiValue && (
string)$property[
'VALUE'] !==
'')
822 if ($property[
'PROPERTY_TYPE'] == Iblock\PropertyTable::TYPE_FILE)
826 foreach ($property[
'VALUE'] as $value)
828 $file = \CFile::GetFileArray($value);
832 'src' => $file[
'SRC'],
841 $file = \CFile::GetFileArray($property[
'VALUE']);
845 'src' => $file[
'SRC'],
853 $result = \CIBlockFormatProperties::GetDisplayValue([
'NAME' =>
''], $property,
'');
859 elseif (is_array($result))
861 if (empty($result[
'DISPLAY_VALUE']))
863 elseif (is_array($result[
'DISPLAY_VALUE']))
864 $result = implode(
', ', $result[
'DISPLAY_VALUE']);
866 $result = $result[
'DISPLAY_VALUE'];
874 private static function loadProduct(array &$result, array $fields)
879 foreach ($fields as $index => $item)
883 if (isset($item[
'COMPILE']))
885 $select = array_merge($select, $item[
'COMPILE'][
'FIELDS']);
889 $select[] = $item[
'ID'];
891 $descr[$index] = $item;
893 elseif (is_bool($item))
896 $descr[$index] = [
'ID' => $index];
900 $iterator = Catalog\ProductTable::getList([
902 'filter' => [
'@ID' => array_keys($result)]
904 while ($row = $iterator->fetch())
906 $id = (int)$row[
'ID'];
907 foreach ($descr as $item)
909 $fieldId = $item[
'ID'];
910 $result[$id][$fieldId] =
null;
911 if (isset($item[
'COMPILE']))
914 foreach ($item[
'COMPILE'][
'FIELDS'] as $index)
916 if ($row[$index] ===
null)
918 $value[] = $row[$index];
921 $result[$id][$fieldId] = implode($item[
'COMPILE'][
'SEPARATOR'], $value);
925 if ($fieldId ==
'AVAILABLE')
926 $row[$fieldId] = ($row[$fieldId] ==
'Y'
928 : Loc::getMessage(
'PRODUCT_FIELD_STATUS_NO')
930 $result[$id][$fieldId] = $row[$fieldId];
934 unset($row, $iterator);
937 private function loadPrices(array &$result)
939 $priceTypes = $this->getPriceTypes();
940 if (!empty($priceTypes))
942 $productIds = array_keys($result);
945 '@PRODUCT_ID' => array_keys($result),
946 '@CATALOG_GROUP_ID' => $priceTypes,
949 '<=QUANTITY_FROM' => 1,
950 '=QUANTITY_FROM' => null
954 '>=QUANTITY_TO' => 1,
955 '=QUANTITY_TO' => null
959 $iterator = Catalog\PriceTable::getList([
960 'select' => [
'ID',
'PRODUCT_ID',
'CATALOG_GROUP_ID',
'PRICE',
'CURRENCY'],
961 'filter' => $priceFilter
964 while ($price = $iterator->fetch())
966 $id = (int)$price[
'PRODUCT_ID'];
967 $priceTypeId = (int)$price[
'CATALOG_GROUP_ID'];
968 $prices[$id][$priceTypeId] = $price;
969 unset($priceTypeId, $id);
971 unset($price, $iterator);
973 $calculationConfig = [
974 'CURRENCY' => Currency\CurrencyManager::getBaseCurrency(),
975 'USE_DISCOUNTS' =>
true,
976 'RESULT_WITH_VAT' =>
true,
977 'RESULT_MODE' => Catalog\Product\Price\Calculation::RESULT_MODE_COMPONENT
980 if ($this->saleIncluded)
982 $saleDiscountOnly = (string)Main\
Config\Option::get(
'sale',
'use_sale_discount_only') ==
'Y';
983 if ($saleDiscountOnly)
984 $calculationConfig[
'PRECISION'] = (int)Main\
Config\Option::get(
'sale',
'value_precision');
986 Catalog\Product\Price\Calculation::pushConfig();
987 Catalog\Product\Price\Calculation::setConfig($calculationConfig);
988 unset($calculationConfig);
990 foreach ($productIds as $id)
992 $minimalPrice = \CCatalogProduct::GetOptimalPrice(
1001 $result[$id][
'SORT_ORDER'] = 0;
1002 if (!empty($minimalPrice))
1004 $minimalPrice = $minimalPrice[
'RESULT_PRICE'];
1005 $result[$id][
'PRICE'] = \CCurrencyLang::CurrencyFormat(
1006 $minimalPrice[
'DISCOUNT_PRICE'],
1007 $minimalPrice[
'CURRENCY'],
1010 if ($minimalPrice[
'BASE_PRICE'] > $minimalPrice[
'DISCOUNT_PRICE'])
1012 $result[$id][
'PRICE'] .=
' <span style="text-decoration: line-through;">'.
1013 \CCurrencyLang::CurrencyFormat(
1014 $minimalPrice[
'BASE_PRICE'],
1015 $minimalPrice[
'CURRENCY'],
1020 $result[$id][
'SORT_ORDER'] = $minimalPrice[
'DISCOUNT_PRICE'];
1022 unset($minimalPrice);
1025 Catalog\Product\Price\Calculation::popConfig();
1030 private function fillElementSeo(array &$row)
1032 $iproperty = (!empty($row[
'IPROPERTY_VALUES']) ? $row[
'IPROPERTY_VALUES'] : []);
1033 $row[
'SEO_PROPERTIES'] = [
1034 Source\Seo::TITLE => [isset($iproperty[
'ELEMENT_PAGE_TITLE']) && $iproperty[
'ELEMENT_PAGE_TITLE'] !=
''
1035 ? $iproperty[
'ELEMENT_PAGE_TITLE']
1040 if (!empty($iproperty))
1043 Source\Seo::BROWSER_TITLE =>
'ELEMENT_META_TITLE',
1044 Source\Seo::KEYWORDS =>
'ELEMENT_META_KEYWORDS',
1045 Source\Seo::DESCRIPTION =>
'ELEMENT_META_DESCRIPTION'
1048 foreach ($entity as $seoItem => $meta)
1050 if (!empty($iproperty[$meta]))
1052 $row[
'SEO_PROPERTIES'][$seoItem] = $iproperty[$meta];
1053 if (is_array($row[
'SEO_PROPERTIES'][$seoItem]))
1055 $row[
'SEO_PROPERTIES'][$seoItem] = implode(
' ', $row[
'SEO_PROPERTIES'][$seoItem]);
1059 unset($entity, $seoItem, $meta);
1070 private function getAllowedPropertyId($iblockId, array $propertyIds, $mode)
1075 case Iblock\Model\PropertyFeature::FEATURE_ID_LIST_PAGE_SHOW:
1076 $list = Iblock\Model\PropertyFeature::getListPageShowPropertyCodes($iblockId);
1078 case Iblock\Model\PropertyFeature::FEATURE_ID_DETAIL_PAGE_SHOW:
1079 $list = Iblock\Model\PropertyFeature::getDetailPageShowPropertyCodes($iblockId);
1087 $result = array_intersect_key(
1088 array_fill_keys($propertyIds,
true),
1089 array_fill_keys($list,
true)
1092 return (!empty($result) ? array_keys($result) : []);
1100 protected function getUserGroups()
1105 if (isset($USER) && $USER instanceof \CUser)
1107 $result = $USER->GetUserGroupArray();
1108 Main\Type\Collection::normalizeArrayValuesByInt($result,
true);
1116 protected function getPriceTypes()
1118 if ($this->priceTypes ===
null)
1120 $this->priceTypes = [];
1121 $iterator = Catalog\GroupAccessTable::getList([
1122 'select' => [
'CATALOG_GROUP_ID'],
1123 'filter' => [
'=GROUP_ID' => 2]
1125 while ($row = $iterator->fetch())
1127 $id = (int)$row[
'CATALOG_GROUP_ID'];
1128 $this->priceTypes[$id] = $id;
1130 unset($row, $iterator);
1132 return $this->priceTypes;
static loadMessages($file)
static getMessage($code, $replace=null, $language=null)