Поля товара
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Дата последнего изменения: 13.11.2023
Как вы знаете, метод CIBlockElement::GetList модуля Информационные блоки может работать с данными товара (при наличии модуля Торговый каталог). Это подробно описано в документации и активно используется как в публичных компонентах, так и на административных страницах и скриптах. Однако архитектурные особенности реализации, равно как и неправильное использование этих возможностей, приводят к резкому падению производительности.
Давайте сделаем разные вызовы CIBlockElement::GetList и посмотрим, какие запросы будут выполнены в итоге.
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
Запрос, который пойдет в базу:
SELECT BE.ID as ID,BE.NAME as NAME,BE.IBLOCK_ID as IBLOCK_ID FROM b_iblock B INNER JOIN b_lang L ON B.LID=L.LID INNER JOIN b_iblock_element BE ON BE.IBLOCK_ID = B.ID WHERE 1=1 AND ( ((((BE.IBLOCK_ID = '2')))) ) AND (((BE.WF_STATUS_ID=1 AND BE.WF_PARENT_ELEMENT_ID IS NULL)))
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, 'CATALOG_AVAILABLE' => 'Y'), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
Это приводит к запросу:
SELECT BE.ID as ID,BE.NAME as NAME,BE.IBLOCK_ID as IBLOCK_ID ,CAT_PR.QUANTITY as CATALOG_QUANTITY, IF (CAT_PR.QUANTITY_TRACE = 'D', 'Y', CAT_PR.QUANTITY_TRACE) as CATALOG_QUANTITY_TRACE, CAT_PR.QUANTITY_TRACE as CATALOG_QUANTITY_TRACE_ORIG, CAT_PR.WEIGHT as CATALOG_WEIGHT, CAT_PR.VAT_ID as CATALOG_VAT_ID, CAT_PR.VAT_INCLUDED as CATALOG_VAT_INCLUDED, IF (CAT_PR.CAN_BUY_ZERO = 'D', 'N', CAT_PR.CAN_BUY_ZERO) as CATALOG_CAN_BUY_ZERO, CAT_PR.CAN_BUY_ZERO as CATALOG_CAN_BUY_ZERO_ORIG, CAT_PR.PURCHASING_PRICE as CATALOG_PURCHASING_PRICE, CAT_PR.PURCHASING_CURRENCY as CATALOG_PURCHASING_CURRENCY, CAT_PR.QUANTITY_RESERVED as CATALOG_QUANTITY_RESERVED, IF (CAT_PR.SUBSCRIBE = 'D', 'Y', CAT_PR.SUBSCRIBE) as CATALOG_SUBSCRIBE, CAT_PR.SUBSCRIBE as CATALOG_SUBSCRIBE_ORIG, CAT_PR.WIDTH as CATALOG_WIDTH, CAT_PR.LENGTH as CATALOG_LENGTH, CAT_PR.HEIGHT as CATALOG_HEIGHT, CAT_PR.MEASURE as CATALOG_MEASURE, CAT_PR.TYPE as CATALOG_TYPE, CAT_PR.AVAILABLE as CATALOG_AVAILABLE, CAT_PR.BUNDLE as CATALOG_BUNDLE, CAT_PR.PRICE_TYPE as CATALOG_PRICE_TYPE, CAT_PR.RECUR_SCHEME_LENGTH as CATALOG_RECUR_SCHEME_LENGTH, CAT_PR.RECUR_SCHEME_TYPE as CATALOG_RECUR_SCHEME_TYPE, CAT_PR.TRIAL_PRICE_ID as CATALOG_TRIAL_PRICE_ID, CAT_PR.WITHOUT_ORDER as CATALOG_WITHOUT_ORDER, CAT_PR.SELECT_BEST_PRICE as CATALOG_SELECT_BEST_PRICE, IF (CAT_PR.NEGATIVE_AMOUNT_TRACE = 'D', 'N', CAT_PR.NEGATIVE_AMOUNT_TRACE) as CATALOG_NEGATIVE_AMOUNT_TRACE, CAT_PR.NEGATIVE_AMOUNT_TRACE as CATALOG_NEGATIVE_AMOUNT_TRACE_ORIG, CAT_VAT.RATE as CATALOG_VAT FROM b_iblock B INNER JOIN b_lang L ON B.LID=L.LID INNER JOIN b_iblock_element BE ON BE.IBLOCK_ID = B.ID left join b_catalog_product as CAT_PR on (CAT_PR.ID = BE.ID) left join b_catalog_iblock as CAT_IB on ((CAT_PR.VAT_ID IS NULL or CAT_PR.VAT_ID = 0) and CAT_IB.IBLOCK_ID = BE.IBLOCK_ID) left join b_catalog_vat as CAT_VAT on (CAT_VAT.ID = IF((CAT_PR.VAT_ID IS NULL OR CAT_PR.VAT_ID = 0), CAT_IB.VAT_ID, CAT_PR.VAT_ID)) WHERE 1=1 AND ( ((((BE.IBLOCK_ID = '2')))) AND ((((CAT_PR.AVAILABLE='Y')))) ) AND (((BE.WF_STATUS_ID=1 AND BE.WF_PARENT_ELEMENT_ID IS NULL)))
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2), false, false, array('ID', 'NAME', 'IBLOCK_ID', 'CATALOG_CATALOG_GROUP_ID_1') );
Видим, что стало еще хуже:
SELECT BE.ID as ID,BE.NAME as NAME,BE.IBLOCK_ID as IBLOCK_ID, CAT_P1.CATALOG_GROUP_ID as CATALOG_GROUP_ID_1, CAT_P1.ID as CATALOG_PRICE_ID_1, CAT_P1.PRICE as CATALOG_PRICE_1, CAT_P1.CURRENCY as CATALOG_CURRENCY_1, CAT_P1.QUANTITY_FROM as CATALOG_QUANTITY_FROM_1, CAT_P1.QUANTITY_TO as CATALOG_QUANTITY_TO_1, CAT_P1.EXTRA_ID as CATALOG_EXTRA_ID_1, 'Базовая цена' as CATALOG_GROUP_NAME_1, 'Y' as CATALOG_CAN_ACCESS_1, 'Y' as CATALOG_CAN_BUY_1, CAT_PR.QUANTITY as CATALOG_QUANTITY, IF (CAT_PR.QUANTITY_TRACE = 'D', 'Y', CAT_PR.QUANTITY_TRACE) as CATALOG_QUANTITY_TRACE, CAT_PR.QUANTITY_TRACE as CATALOG_QUANTITY_TRACE_ORIG, CAT_PR.WEIGHT as CATALOG_WEIGHT, CAT_PR.VAT_ID as CATALOG_VAT_ID, CAT_PR.VAT_INCLUDED as CATALOG_VAT_INCLUDED, IF (CAT_PR.CAN_BUY_ZERO = 'D', 'N', CAT_PR.CAN_BUY_ZERO) as CATALOG_CAN_BUY_ZERO, CAT_PR.CAN_BUY_ZERO as CATALOG_CAN_BUY_ZERO_ORIG, CAT_PR.PURCHASING_PRICE as CATALOG_PURCHASING_PRICE, CAT_PR.PURCHASING_CURRENCY as CATALOG_PURCHASING_CURRENCY, CAT_PR.QUANTITY_RESERVED as CATALOG_QUANTITY_RESERVED, IF (CAT_PR.SUBSCRIBE = 'D', 'Y', CAT_PR.SUBSCRIBE) as CATALOG_SUBSCRIBE, CAT_PR.SUBSCRIBE as CATALOG_SUBSCRIBE_ORIG, CAT_PR.WIDTH as CATALOG_WIDTH, CAT_PR.LENGTH as CATALOG_LENGTH, CAT_PR.HEIGHT as CATALOG_HEIGHT, CAT_PR.MEASURE as CATALOG_MEASURE, CAT_PR.TYPE as CATALOG_TYPE, CAT_PR.AVAILABLE as CATALOG_AVAILABLE, CAT_PR.BUNDLE as CATALOG_BUNDLE, CAT_PR.PRICE_TYPE as CATALOG_PRICE_TYPE, CAT_PR.RECUR_SCHEME_LENGTH as CATALOG_RECUR_SCHEME_LENGTH, CAT_PR.RECUR_SCHEME_TYPE as CATALOG_RECUR_SCHEME_TYPE, CAT_PR.TRIAL_PRICE_ID as CATALOG_TRIAL_PRICE_ID, CAT_PR.WITHOUT_ORDER as CATALOG_WITHOUT_ORDER, CAT_PR.SELECT_BEST_PRICE as CATALOG_SELECT_BEST_PRICE, IF (CAT_PR.NEGATIVE_AMOUNT_TRACE = 'D', 'N', CAT_PR.NEGATIVE_AMOUNT_TRACE) as CATALOG_NEGATIVE_AMOUNT_TRACE, CAT_PR.NEGATIVE_AMOUNT_TRACE as CATALOG_NEGATIVE_AMOUNT_TRACE_ORIG, CAT_VAT.RATE as CATALOG_VAT FR OM b_iblock B INNER JOIN b_lang L ON B.LID=L.LID INNER JOIN b_iblock_element BE ON BE.IBLOCK_ID = B.ID left join b_catalog_price as CAT_P1 on (CAT_P1.PRODUCT_ID = BE.ID and CAT_P1.CATALOG_GROUP_ID = 1) left join b_catalog_product as CAT_PR on (CAT_PR.ID = BE.ID) left join b_catalog_iblock as CAT_IB on ((CAT_PR.VAT_ID IS NULL or CAT_PR.VAT_ID = 0) and CAT_IB.IBLOCK_ID = BE.IBLOCK_ID) left join b_catalog_vat as CAT_VAT on (CAT_VAT.ID = IF((CAT_PR.VAT_ID IS NULL OR CAT_PR.VAT_ID = 0), CAT_IB.VAT_ID, CAT_PR.VAT_ID)) WH ERE 1=1 AND ( ((((BE.IBLOCK_ID = '2')))) ) AND (((BE.WF_STATUS_ID=1 AND BE.WF_PARENT_ELEMENT_ID IS NULL)))
Подведем промежуточные итоги:
До выпуска связки обновлений catalog 18.6.100 + iblock 18.6.200 все вышеописанное относится, в том числе, к штатным компонентам и административным спискам модуля Информационные блоки (особенно в режиме совместного просмотра элементов и разделов, смотрите примечание ниже). После выхода данных обновлений в CIBlockElement::GetList доступны новые возможности работы с товарами.
С версии iblock 18.6.200 изменяются ключи метода. По всем ключам возможна фильтрация, сортировка, выборка.
Поля товара
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
С версии catalog 20.0.200 добавились поля товара
| |||||||||
---|---|---|---|---|---|---|---|---|---|
|
Теперь вызов метода с фильтрацией по доступности выглядит так:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '=AVAILABLE' => 'Y'), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
а в запросе только то, что просили, и join только один:
SELECT BE.ID as ID,BE.NAME as NAME,BE.IBLOCK_ID as IBLOCK_ID FROM b_iblock B INNER JOIN b_lang L ON B.LID=L.LID INNER JOIN b_iblock_element BE ON BE.IBLOCK_ID = B.ID left join b_catalog_product as PRD on (PRD.ID = BE.ID) WHERE 1=1 AND ( ((((BE.IBLOCK_ID = '2')))) AND ((((PRD.AVAILABLE='Y')))) ) AND (((BE.WF_STATUS_ID=1 AND BE.WF_PARENT_ELEMENT_ID IS NULL)))
Сделаем выборку размеров и веса доступных простых товаров:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '=AVAILABLE' => 'Y', '=TYPE' => 1), false, false, array('ID', 'NAME', 'IBLOCK_ID', 'WEIGHT', 'WIDTH', 'HEIGHT', 'LENGTH') );
Запрос получается следующим:
SELECT BE.ID as ID,BE.NAME as NAME,BE.IBLOCK_ID as IBLOCK_ID , PRD.WEIGHT as WEIGHT, PRD.WIDTH as WIDTH, PRD.HEIGHT as HEIGHT, PRD.LENGTH as LENGTH FROM b_iblock B INNER JOIN b_lang L ON B.LID=L.LID INNER JOIN b_iblock_element BE ON BE.IBLOCK_ID = B.ID left join b_catalog_product as PRD on (PRD.ID = BE.ID) WHERE 1=1 AND ( ((((BE.IBLOCK_ID = '2')))) AND ((((PRD.AVAILABLE='Y')))) AND ((((PRD.TYPE = '1')))) ) AND (((BE.WF_STATUS_ID=1 AND BE.WF_PARENT_ELEMENT_ID IS NULL)))
Поля цен (без привязки к конкретному типу цены)
| |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Поля цен (с указанием типа цены)
| ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Поля складов
| |||||||||
---|---|---|---|---|---|---|---|---|---|
|
Фильтрация по цене любого типа:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '=AVAILABLE' => 'Y', '=TYPE' => 1, '>PRICE' => 500), false, false, array('ID', 'NAME', 'IBLOCK_ID', 'WEIGHT', 'WIDTH', 'HEIGHT', 'LENGTH') );
Фильтрация для типа цены с кодом 1 (обычно это базовая цена):
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '=AVAILABLE' => 'Y', '=TYPE' => 1, '>PRICE' => 500, '=PRICE_TYPE' => 1), false, false, array('ID', 'NAME', 'IBLOCK_ID', 'WEIGHT', 'WIDTH', 'HEIGHT', 'LENGTH') );
Или в таком варианте:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '=AVAILABLE' => 'Y', '=TYPE' => 1, '>PRICE_1' => 500), false, false, array('ID', 'NAME', 'IBLOCK_ID', 'WEIGHT', 'WIDTH', 'HEIGHT', 'LENGTH') );
Теперь можно делать фильтры, ранее недоступные. Выбрать все товары, имеющие цены любого типа от 500 до 1000:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '=AVAILABLE' => 'Y', '=TYPE' => 1, '>=PRICE' => 500, '<=PRICE' => 1000), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
Или только цены типов с кодом 1,4,5:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '=AVAILABLE' => 'Y', '=TYPE' => 1, '>=PRICE' => 500, '<=PRICE' => 1000, '@PRICE_TYPE' => [1,4,5]), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
Выбрать все товары с ценами в любой валюте, эквивалентными диапазону от 100 до 200 USD:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '=AVAILABLE' => 'Y', '=TYPE' => 1, '>=PRICE' => 100, '<=PRICE' => 200, 'CURRENCY_FOR_SCALE' => 'USD'), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
Выбрать товары, которых на любом складе не больше 3:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '<=STORE_AMOUNT' => 3), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
Выбрать товары, которых на 17-м складе от 5 до 7:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '<=STORE_AMOUNT' => 7, '>=STORE_AMOUNT' => 5, 'STORE_NUMBER' => 17), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
Либо в таком виде:
$iterator = \CIBlockElement::GetList( array(), array('IBLOCK_ID' => 2, '<=STORE_AMOUNT_17' => 7, '>=STORE_AMOUNT_17' => 5), false, false, array('ID', 'NAME', 'IBLOCK_ID') );
Фильтрация товаров по наличию на определенных складах:
if(!empty($arParams['STORES'])){ $GLOBALS[$arParams['FILTER_NAME']]['@STORE_NUMBER'] => $arParams['STORES']; $GLOBALS[$arParams['FILTER_NAME']]['>STORE_AMOUNT'] = 0; }
Другой пример фильтрации товаров по наличию на определенных складах:
if(!empty($arParams['STORES'])){ $storesFilter = [ 'LOGIC'=>'OR' ]; foreach ($arParams['STORES'] as $store_id){ $storesFilter[] = ['STORE_NUMBER' => intval($store_id),'>STORE_AMOUNT'=>0]; } $GLOBALS[$arParams['FILTER_NAME']][] = $storesFilter; }
Заключение |
После установки обновлений catalog 18.6.100 + iblock 18.6.200 настоятельно рекомендуется перевести свои компоненты и скрипты на новые ключи. Увеличение производительности прямо пропорционально числу товаров в каталоге. Так, при тестах на разделе из 1,5 тысяч товаров прирост скорости выполнения составил порядка 30%. Штатные компоненты (
catalog.section
Компонент выводит список элементов раздела с указанным набором свойств.
Описание компонента «Элементы раздела» в пользовательской документации.
,
catalog.element
Компонент выводит детальную информацию по элементу каталога.
Описание компонента «Элемент каталога детально» в пользовательской документации.
,
catalog.top
Компонент выводит в таблице top элементов из всех разделов в соответствии с заданной сортировкой (используется как правило на главной странице сайта).
Описание компонента «top элементов каталога» в пользовательской документации.
), а также все компоненты наследники \Bitrix\Iblock\Component\Base переведены на новые фильтры.