Работа с элементами инфоблока, товаром, catalog.element и торговыми предложениями в Битрикс
Последние записи
Навигация по разделам:
- Работа с элементами инфоблоков
- Работа с торговым каталогом (цена\наличие на складах\остатки\резервы)
- Работа со свойствами товаров
- Работа с наборами и комплектами
- Работа со скидками
- Работа с налогами
- Работа с шаблоном
- Обмен с 1С
- Сложная ORM выборка для получения всех необходимых данных товара
- Проверка на детальную страницу в header
- Типовые запросы:
- Получить все элементы из раздела и вложенных подразделов
Типы товаров
Товар – добавляется самый обычный простой товар, без каких-либо дополнительных возможностей. Это тот тип товара, который наиболее употребим, и используется в данный момент повсеместно.
Торговые предложения – Данный товар (как товар хранящий список предложений) не имеет остатков, не имеет цены и других атрибутов, которые есть у обычного товара. Этот товар не является товаром. По сути, он содержит список «торговых предложений», которые и являются товарами (предложениями).
Набор - это список привязанных к основному товару товаров, которые магазин хочет порекомендовать к покупке. В набор могут быть добавлены именно товары: торговые предложения и/или простые товары. Товар с торговыми предложениями добавить в него нельзя.
Комплект – это список товаров, составляющих необходимую комплектацию основного товара. Сам комплект не имеет физического остатка, его остаток зависит от товаров, которые входят в этот комплект. В комплект могут быть добавлены именно товары: торговые предложения и/или простые товары. Товар с торговыми предложениями добавить в него нельзя.
Работа с элементами инфоблоков
Добавление элемента инфоблока на сайт
//Инициализируем соответствующий модуль
CModule::IncludeModule("iblock");
//Массив со свойствами элемента инфоблока, где ключами массива служат id нужного свойства
//(но можно использовать параметр CODE)
//Так же нужно помнить, что для списков и полей с привязкой к элементам или справочникам
//в качестве значения мы указываем id элемента списка, справочника или объекта
$PROP = array(
'342'=>'',
'315'=>''
);
//Cоздаем объект класса для работы
$el = new CIBlockElement;
//Заполняем массив с данными
$arLoadProductArray = Array(
"MODIFIED_BY" => 1, //указываем ID пользователя
"IBLOCK_SECTION" => $IBLOCK_SECTION_ID, //для привязки ко многим разделам
//"IBLOCK_SECTION_ID" => $IBLOCK_SECTION_ID, //для одиночного раздела
"IBLOCK_ID" => 20,
"IBLOCK_TYPE" => 'aspro_next_catalog',
"PROPERTY_VALUES" => $PROP,
"NAME" => $NAME,
"ACTIVE" => "Y",
"CODE" => $CODE,
//"PREVIEW_TEXT" => "текст для списка элементов",
//"DETAIL_TEXT" => "текст для детального просмотра",
//"DETAIL_PICTURE" => CFile::MakeFileArray($_SERVER["DOCUMENT_ROOT"]."/image.gif")
);
$res = $el->Add($arLoadProductArray);
if($res>0){
//Успешно добавили
//В $res хранится id добавленной записи
}else{
//Добавить не удалось
//Выводим сообщение с ошибкой
echo $el->LAST_ERROR;
}
Превращаем элемент инфоблока в товар
/* Делаем добавленный товар простым */
$productFileds = array(
"ID" => $res, //ID добавленного элемента инфоблока
"VAT_ID" => 1, //выставляем тип ндс (задается в админке)
"VAT_INCLUDED" => "Y", //НДС входит в стоимость
"TYPE " => \Bitrix\Catalog\ProductTable::TYPE_PRODUCT //Тип товара
);
//Возможные значения типа товара:
//const TYPE_PRODUCT = 1;
//const TYPE_SET = 2;
//const TYPE_SKU = 3;
//const TYPE_OFFER = 4;
//const TYPE_FREE_OFFER = 5;
//const TYPE_EMPTY_SKU = 6;
if(CCatalogProduct::Add($productFileds)){
//Элемент инфоблока превращен в товар
}else{
//Произошла ошибка
}
Получение списка свойств элемента инфоблока с помощью D7 (начиная с версии 19.0.0)
$res = \Bitrix\Iblock\ElementTable::getList(array(
"select" => array("ID", "*"),
"filter" => array("IBLOCK_ID" => $IBLOCK_ID, "ID" => $ELEMENT_ID),
"order" => array("ID" => "ASC")
));
while ($arItem = $res->fetch()) {
$propRes = \Bitrix\Iblock\ElementPropertyTable::getList(array(
"select" => array("ID", "*"),
"filter" => array("IBLOCK_ELEMENT_ID" => $arItem["ID"],),
"order" => array("ID" => "ASC")
));
while($prop = $propRes->Fetch())
$arItem["PROPERTIES"][$prop["IBLOCK_PROPERTY_ID"]] = $prop;
}
Битрикс получить ID элемента по его символьному коду
CIBlockFindTools::GetElementID($ELEMENT_ID, $ELEMENT_CODE, $SECTION_ID, $SECTION_CODE, $arFilter)
// $section_id - ID секции в которой лежит элемент
// $section_code - символьный код секции в которой лежит элемент
// $arFilter - массив дополнительных свойств для фильтрации
// пример использования
$objFindTools = new CIBlockFindTools();
$elementID = $objFindTools->GetElementID(false, "super_element", false, "super_section", array("IBLOCK_ID" => 1));
// метод возвращает ID элемента, если найдет его, и 0, если элемент не будет найден.
Работа с торговым каталогом (цена\наличие на складах\остатки\резервы)
Получаем список типов цен с помощью d7
$rsPrices = \Bitrix\Catalog\GroupTable::getList();
while($arPrice = $rsPrices->fetch()){
$PRICE_IDS[] = $arPrice['ID'];
}
Получаем цену и кол-во товара с помощью D7
$dbPrice = \Bitrix\Catalog\Model\Price::getList([
"filter" => array(
"PRODUCT_ID" => $id,
"CATALOG_GROUP_ID" => 1
)]);
if ($arPrice = $dbPrice->fetch()) {
$price = $arPrice['PRICE'];
}
Добавляем или обновляем цену товара
$arFieldsPrice = Array(
"PRODUCT_ID" => $ID, //ID добавленного товара
"CATALOG_GROUP_ID" => 1, //ID типа цены
"PRICE" => $item['price'], //значение цены
"CURRENCY" => !$currency ? "RUB" : $currency, // валюта
);
//Смотрим установлена ли цена адля данного товара
$dbPrice = \Bitrix\Catalog\Model\Price::getList([
"filter" => array(
"PRODUCT_ID" => $item['ID'],
"CATALOG_GROUP_ID" => 1
)
]);
if ($arPrice = $dbPrice->fetch()) {
//Если цена установлена, то обновляем
$result = \Bitrix\Catalog\Model\Price::update($arPrice["ID"], $arFieldsPrice);
if ($result->isSuccess()){
echo "Обновили цену у товара у элемента каталога " . $item['ID'] . " Цена " . $item['price'] . PHP_EOL;
} else {
echo "Ошибка обновления цены у товара у элемента каталога " . $item['ID'] . " Ошибка " . $result->getErrorMessages() . PHP_EOL;
}
}else{
//Если цены нет, то добавляем
$result = \Bitrix\Catalog\Model\Price::add($arFieldsPrice);
if ($result->isSuccess()){
echo "Добавили цену у товара у элемента каталога " . $item['ID'] . " Цена " . $item['price'] . PHP_EOL;
} else {
echo "Ошибка добавления цены у товара у элемента каталога " . $item['ID'] . " Ошибка " . $result->getErrorMessages() . PHP_EOL;
}
}
Добавляем к товару количество на складах
$arFields = Array(
"PRODUCT_ID" => $id, //ID товара
"STORE_ID" => $storeId, //ID склада
"AMOUNT" => $amount, //Количество
);
CCatalogStoreProduct::Add($arFields);
Более подробно о работе со складами вы можете почитать здесь Работа со складами и количеством товаров в Битрикс D7
Обновляем кол-во товара на складах
$rs = CCatalogStoreProduct::GetList(false, array(
'PRODUCT_ID'=> $id, //ID товара
'STORE_ID' => $storeId //ID склада
));
while($ar_fields = $rs->GetNext())
{
// Обновим значение остатка на складе из значения остатка количественного учёта
$arFields = Array(
"PRODUCT_ID" => $id, //ID товара
"STORE_ID" => $storeId, //ID склада
"AMOUNT" => $amount, //Количество
);
CCatalogStoreProduct::Update($ar_fields['ID'], $arFields);
}
Обновление остатков товаров с помощью D7
$existProduct = \Bitrix\Catalog\Model\Product::getCacheItem($arFields['ID'],true);
if(!empty($existProduct)){
\Bitrix\Catalog\Model\Product::update(intval($arFields['ID']),$arFields);
} else {
\Bitrix\Catalog\Model\Product::add($arFields);
}
Добавляем или обновляем общее кол-во товара (параметр "Доступное кол-во")
CCatalogProduct::Update(
$ID, //ID добавленного или обновляемого товара
array(
"QUANTITY" => $amount, //Кол-во товара
)
)
Добавляем товар в резерв
$provider = new \Bitrix\Catalog\Product\CatalogProvider;
$resReserve = $provider->reserve(array(
$productId => ["PRODUCT_ID" => $PRODUCT_ID, "QUANTITY" => 10]
));
Снятие резерва
$provider = new \Bitrix\Catalog\Product\CatalogProvider;
$resReserve = $provider->reserve(array(
$productId => ["PRODUCT_ID" => $PRODUCT_ID, "QUANTITY" => -10 ]
));
Или более кратко:
\Bitrix\Catalog\Product\CatalogProvider::ReserveProduct(array(
$productId => ["PRODUCT_ID" => $PRODUCT_ID, "QUANTITY" => -10 ]
))
Получаем measure
и ratio
товара на D7
\Bitrix\Catalog\ProductTable::getCurrentRatioWithMeasure($arResult['ID'])
Изменяем measure
\CCatalogProduct::Update(13194, array('MEASURE' => 6));
Изменяем ratio
$db_measure = CCatalogMeasureRatio::getList(array(), $arFilter = array('PRODUCT_ID' => 13194), false, false);
while ($ar_measure = $db_measure->Fetch()) {
$new_measure = CCatalogMeasureRatio::update($ar_measure['ID'], array("RATIO" => 5));
}
Работа со свойствами товаров
Получаем значение отдельного свойства товара в Битрикс с помощью D7
class Product
{
private $id;
public function __construct($id)
{
if (empty($id)) {
throw new \Bitrix\Main\ArgumentNullException('id');
}
$this->id = $id;
}
public function getFields()
{
return \Bitrix\Iblock\ElementTable::getById($this->id)->fetch();
}
public function getProperty($code)
{
$fields = $this->getFields();
if ($fields) {
$iblock = \Bitrix\Iblock\Iblock::wakeUp($fields['IBLOCK_ID']);
$element = $iblock->getEntityDataClass()::getByPrimary($this->id, ['select' => [$code]])->fetchObject();
$property = $element->__call('get', [$code]);
}
return $property ? $property->getValue() : null;
}
}
Обновить свойство у элемента по коду свойства и ID элемента
\CIBlockElement::SetPropertyValuesEx(PRODUCT_ID, false, array('PROPERTY_CODE' => $NEW_PROPERTY_VALUE));
Очистить значение множественного свойства
\CIBlockElement::SetPropertyValuesEx(PRODUCT_ID, false, ['PROPERTY_CODE' => [0 => ["VALUE" => "" , "DESCRIPTION" => ""]]]);
Документация Концепция и архитектура
Получаем значение отдельного свойства товара или ТП (старый способ)
$arFilter = Array("IBLOCK_ID"=>$kitOffer['IBLOCK_ID'], "ID"=>$kitOffer['ID']);
$arSelect = Array("SORT");
$res = CIBlockElement::GetList(Array(), $arFilter,false,false,$arSelect);
if ($ob = $res->GetNextElement()){;
$arFields = $ob->GetFields();
}
Получаем товары в Битрикс с помощью D7
//Фильтр стандартный, вы знаете как им пользоваться
$elementIterator = \Bitrix\Iblock\ElementTable::getList([
'select' => [
'ID',
],
'filter' => [
'=IBLOCK_ID' => 20,
'!=ID' => array(1,2,3),//Массив ID товаров которые нужно пропустить
]
]);
$elems = $elementIterator->fetchAll();
foreach ($elems as $element) {
// создаем объект класса для работы
$obElement = new CIBlockElement();
// обновляем элемент и делаем неактивным
$obElement->Update($element['ID'], Array("ACTIVE" => 'N'));
}
Получаем все свойства элемента инфоблока
CIBlockElement::GetByID($arResult['ID'])->GetNextElement()->GetProperties()
Вывод всех свойств товара
CCatalogProduct::GetByIDEx($arElement['ID'])
Как вариант можно воспользоваться отрицательной и положительной сортировкой свойств. Мы можем нужные для вывода свойства маркировать положительным числом сортировки, а ненужные отрицательным, тогда скрипт для вывода свойств получится совсем компактным
Идем в файл result_modifier.php
и добавляем в самый низ:
//==============================================//
// Показывать все свойства в DISPLAY_PROPERTIES //
//==============================================//
$arResult["DISPLAY_PROPERTIES"] = array();
foreach ($arResult["PROPERTIES"] as $pid => &$arProp)
{
// Не выводим для просмотра свойства с сортировкой меньше 0 (они будут у нас служебными)
if ($arProp["SORT"] < 0)
continue;
if((is_array($arProp["VALUE"]) && count($arProp["VALUE"])>0) ||
(!is_array($arProp["VALUE"]) && strlen($arProp["VALUE"])>0))
{
$arResult["DISPLAY_PROPERTIES"][$pid] = CIBlockFormatProperties::GetDisplayValue($arResult, $arProp);
}
}
Решение предложил Левый Иван с форума Битрикс
Получаем значения свойства товара типа список
$property_enums = CIBlockPropertyEnum::GetList(Array("DEF"=>"DESC", "SORT"=>"ASC"), Array("IBLOCK_ID"=>15, "CODE"=>"BREND_INTERNAL"));
while($enum_fields = $property_enums->GetNext()){
echo $enum_fields["ID"]." - ".$enum_fields["VALUE"]."<br>";
}
Добавить новое значение в свойство типа список
$ibpenum = new CIBlockPropertyEnum;
if($PropID = $ibpenum->Add(Array('PROPERTY_ID'=>$PROPERTY_ID, 'VALUE'=>'New Enum 1')))
echo 'New ID:'.$PropID;
или добавляем свойство на D7
\Bitrix\Main\Loader::includeModule('iblock');
$property = \CIBlockProperty::GetList(
[],
[
'IBLOCK_ID' => $iblockId,
'CODE' => $code'
]
)->Fetch();
$ibpenum = new \CIBlockPropertyEnum();
$valueId = $ibpenum->Add([
'PROPERTY_ID' => $property['ID'],
'VALUE' => $newValueText,
'XML_ID' => $newValueXmlId,
]);
if ((int) $valueId < 0) {
throw new \Exception('Unable to add a value');
}
Удалить значение из свойства типа список
CIBlockPropertyEnum::Delete(ID);
или удаляем свойство на D7
if (! \Bitrix\Main\Loader::includeModule('iblock')) {
throw new \Bitrix\Main\LoaderException('Unable to load IBLOCK module');
}
$property = \CIBlockProperty::GetList([], ['IBLOCK_ID' => $iblockId, 'CODE' => $propertyCode])->Fetch();
if (! $property) {
throw new \Exception('No such property');
}
$query = \CIBlockPropertyEnum::GetList(
[],
["IBLOCK_ID" => $iblockId, "XML_ID" => 6, "PROPERTY_ID" => $property['ID']]
);
$value = $query->GetNext();
if (! $value) {
throw new \Exception('No such value');
}
$delete = \CIBlockPropertyEnum::delete($value['ID']);
if (! $delete) {
throw new \Exception('Error while deleting the property value');
}
Работа с наборами и комплектами
Получаем состав наборов и комплектов для родительского товара
$arProducts = CCatalogProductSet::GetList(
array(), array( "TYPE" => array(1,2), "OWNER_ID" => $productID), false, false, array()
);
while($item = $arSets->Fetch() ){
if($item["OWNER_ID"]!=$item["ITEM_ID"] ){
$arComplects[] = $item["ITEM_ID"];
}
}
Получаем товары соседствующие с искомым в наборе или комплекте
\Bitrix\Main\Loader::includeModule('catalog');
$rItems = CCatalogProductSet::GetList(
array(),
array(
array(
'LOGIC' => 'OR',
'TYPE' => CCatalogProductSet::TYPE_GROUP,
'TYPE' => CCatalogProductSet::TYPE_SET
),
'ITEM_ID' => $arResult['ID']),
false,
false,
array('SET_ID', 'OWNER_ID', 'ITEM_ID', 'TYPE')
);
while ($item = $rItems->Fetch()) {
$arComplect[] = $item;
}
Получаем нужные свойства по массиву id торговых предложений
function getInform($arrOffersIDs){
$arrProductsIDs = array();
foreach($arrOffersIDs as $intElementID){
$mxResult = CCatalogSku::GetProductInfo(
$intElementID, 4
);
if (is_array($mxResult))
{
$arrProductsIDs[] = $mxResult["ID"];
}
}
return CCatalogSKU::getOffersList(
$arrProductsIDs,
$iblockID = 4,
$skuFilter = array('ID'=>$arrOffersIDs),
$fields = array('ID','NAME','DETAIL_PICTURE'),
$propertyFilter = array() );
}
print_r(getInform(array(45,87,98)));
Получаем нужные свойства всех торговых предложений
function getInform($arrOffersIDs){
$arrProductsIDs = array();
foreach($arrOffersIDs as $intElementID){
$mxResult = CCatalogSku::GetProductInfo(
$intElementID,
4 //ID каталога с товаром
);
if (is_array($mxResult))
{
$arrProductsIDs[] = $mxResult["ID"];
}
}
return CCatalogSKU::getOffersList(
$arrProductsIDs,
$iblockID = 4, //ID каталога с товаром
$skuFilter = array(),
$fields = array('NAME','ID','DETAIL_PICTURE','PREVIEW_PICTURE', "DETAIL_PAGE_URL", "CML2_MANUFACTURER", "DETAIL_TEXT"),//Получаемые свойства
$propertyFilter = array() );
}
print_r(getInform(array(45,87,98)));
Работа со скидками
Получаем скидки и правила работы с корзиной, которые были применены к товару
В файле result_modifier.php
компонента sale.basket.basket
получаем перечень примененных к товару скидок
/** @var \Bitrix\Sale\BasketBase $basket */
$basket = (\Bitrix\Sale\Basket\Storage::getInstance(
\Bitrix\Sale\Fuser::getId(),
\Bitrix\Main\Context::getCurrent()->getSite()))
->getOrderableBasket();
$order = $basket->getOrder();
$discountApplyResults = $order->getDiscount()->getApplyResult(false);
Автогенерации правила работы с корзиной, добавление купона и его применение
Код генерации
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
use Bitrix\Sale\Internals;
CModule::IncludeModule("catalog");
CModule::IncludeModule("iblock");
CModule::IncludeModule("sale");
global $APPLICATION;
$unixStart = strtotime(date("d.m.Y H:i:s"));
$unixEnd = $unixStart+43200; //12 часов
$xcount = 0;
$discountValue = rand(1,10); //Размер случайной скидки от 1 до 10 процентов
$Actions["CLASS_ID"] = "CondGroup";
$Actions["DATA"]["All"] = "AND";
$Actions["CLASS_ID"] = "CondGroup";
$Actions["CHILDREN"][0]["CLASS_ID"] = "ActSaleBsktGrp";
$Actions["CHILDREN"][0]["DATA"]["Type"] = "Discount";
$Actions["CHILDREN"][0]["DATA"]["Value"] = $discountValue;
$Actions["CHILDREN"][0]["DATA"]["Unit"] = "Perc";
$Actions["CHILDREN"][0]["DATA"]["All"] = "OR";
$DbParentEl = CIBlockElement::GetList(array(),array("SECTION_ID"=>array(10,11)),false,false,array("ID"));
while($ParentId = $DbParentEl->Fetch()){
//Массив товаров к которым будет применяться скидка
$Actions["CHILDREN"][0]["CHILDREN"][$xcount]["CLASS_ID"] = "CondIBElement";
$Actions["CHILDREN"][0]["CHILDREN"][$xcount]["DATA"]["logic"] = "Equal";
$Actions["CHILDREN"][0]["CHILDREN"][$xcount]["DATA"]["value"] = $ParentId["ID"];
$xcount++;
}
$Conditions["CLASS_ID"] = "CondGroup";
$Conditions["DATA"]["All"] = "AND";
$Conditions["DATA"]["True"] = "True";
$Conditions["CHILDREN"] = "";
//Массив для создания правила
$arFields = array(
"LID"=>"s1",
"NAME"=>$discountValue."% Скидки ".date("d.m.y"),
"CURRENCY"=>"RUB",
"ACTIVE"=>"Y",
"USER_GROUPS"=>array(1),
"ACTIVE_FROM"=>ConvertTimeStamp($unixStart, "FULL"),
"ACTIVE_TO"=>ConvertTimeStamp($unixEnd, "FULL"),
"CONDITIONS"=>$Conditions,
'ACTIONS' => $Actions
);
$ID = CSaleDiscount::Add($arFields); //Создаем правило корзины
$res = $ID>0;
if ($res) {
$codeCoupon = CatalogGenerateCoupon(); //Генирация купона
$fields["DISCOUNT_ID"] = $ID;
$fields["COUPON"] = $codeCoupon;
$fields["ACTIVE"] = "Y";
$fields["TYPE"] = 2;
$fields["MAX_USE"] = 0;
$dd = Internals\DiscountCouponTable::add($fields); //Создаем купон для этого правила
if (!$dd->isSuccess())
{
$err = $dd->getErrorMessages();
}else{
echo 'Купон на скидку: '.$codeCoupon;
}
}else{
$ex = $APPLICATION->GetException();
echo 'Ошибка: '.$ex->GetString();
}
Код вызова
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
use Bitrix\Main\Loader;
use Bitrix\Sale\DiscountCouponsManager;
CModule::IncludeModule("sale");
$check = DiscountCouponsManager::isExist($_POST['coupon']);
if($check["ID"]>0){
DiscountCouponsManager::add($_POST['coupon']);
echo "ok";
}else{
echo 'Такого купона не существует';
}
Оригинал решения
Работа с налогами
Для установки галочки "НДС включен в цену" при добавлении товара воспользуемся событием OnProductAdd
. Но не забываем, что событие устаревшее, чтобы оно работало - требуется включить "Включить поддержку устаревших событий" в настройках торгового каталога
AddEventHandler("catalog", "OnProductAdd", "OnProductAdd");
function OnProductAdd($ID, $FIELDS)
{
CCatalogProduct::Update($ID, ["VAT_INCLUDED"=>'Y']);
}
Для массового изменения значения "НДС включен в цену" можно выполнить sql запрос
UPDATE b_catalog_product SET VAT_INCLUDED = 'Y', VAT_ID='1' WHERE VAT_INCLUDED = 'N'
Работа с шаблоном
Изменение дополнительных полей торговых предложений в карточке товара "на лету" на примере PREVIEW_TEXT и DETAIL_TEXT
Идем в result_modifier.php
и добавляем:
foreach ($arResult['OFFERS'] as $pid => &$arProp)
{
$arResult['JS_OFFERS'][$pid]['PREVIEW_TEXT'] = $arProp['PREVIEW_TEXT'];//Добавляем в результирующий js массив предложения PREVIEW_TEXT
$arResult['JS_OFFERS'][$pid]['DETAIL_TEXT'] = $arProp['DETAIL_TEXT'];//Добавляем так же DETAIL_TEXT
}
Для начала подготавливаем контейнеры куда будем выводить новую информацию в шаблоне template.php
. В моем случае предполагается, что PREVIEW_TEXT
будет грузиться в блок с классом offerShortDescription
, DETAIL_TEXT
в блок с классом offerFullDescription
. Добавим блоки в требуемое место шаблона.
<div class="offerShortDescription"><?=$actualItem['PREVIEW_TEXT']?></div>
<div class="offerFullDescription"><?=$actualItem['DETAIL_TEXT']?></div>
Идем в файл script.js
, ищем функцию changeInfo
(~2463) и в после условия if (this.obSkuProps)
(~2553) добавляем:
if(this.offers[index].PREVIEW_TEXT && this.offers[index].DETAIL_TEXT){
$('.offerShortDescription').html(this.offers[index].PREVIEW_TEXT);
$('.offerFullDescription').html(this.offers[index].DETAIL_TEXT);
}
Не забываем обработать случаи когда поля с описанием у торговых предложений не заполнены. Предлагаю это сделать вам самим. Лично я просто добавил через result_modifier.php
в каждое торговое предложение дополнительные поля в описанием оригинального товара. Это не совсем красивое, но вполне рабочее решение.
Убираем смену слайда при наведении на превью в слайдере
Идем в script.js
и комментируем данные строки (они встречаются дважды на ~552 и ~607)
BX.bind(this.product.slider.ITEMS[j], 'mouseenter', BX.delegate(this.onSliderControlHover, this));
BX.bind(this.product.slider.ITEMS[j], 'mouseleave', BX.delegate(this.onSliderControlLeave, this));
Решение применялось на редакции Бизнес v18.5.0
Меняем активное торговое предложение
Идем в файл \bitrix\components\bitrix\catalog.element\templates\.default\result_modifier.php
Ищем строку foreach ($arResult['OFFERS'] as $keyOffer => $arOffer)
- где-то в районе 405 строки.
Там есть строка $intSelected = $keyOffer;
- вот $intSelected
это и есть порядковый(!) номер торгового предложения, выбранного по умолчанию. То есть, ключ элемента массива ТП, которые выводятся у конкретного товара.
Например, у меня была задача сделать активным по умолчанию предложение ID которого был в $_GET['TP_ID']
.
В цикл foreach ($arResult['OFFERS'] as $keyOffer => $arOffer)
вставляем:
if ($_GET['TP_ID']) {
if ($arOffer['ID']==$_GET['TP_ID']) {
$intSelected =$keyOffer;
}
}
Обмен с 1С
Костыль для пересчета остатков и кол-ва товаров при его обновлении из 1С
Будет полезен, если у вас не обновляются резервы при импорте из 1С, а в вашей редакции Битрикс нет кнопки очистить резервы
//При начале импорта из 1С устанавливаем сессионную переменную
AddEventHandler(
'catalog',
'OnBeforeCatalogImport1C',
function ()
{
$_SESSION["1C_UPDATE"] = true;
}
);
//При обновлении товара проверяем, что установлена сесионная переменная и видоизменяем данные
AddEventHandler(
'catalog',
'OnProductUpdate',
function ($id, $arFields)
{
if (isset($_SESSION["1C_UPDATE"]) && $_SESSION["1C_UPDATE"])
{
$_SESSION["1C_UPDATE"] = false; // борьба с зацикливанием, чтобы сам себя не вызывал. Так как при вызове CCatalogProduct::Update будет опять срабатывать OnProductUpdat
CCatalogProduct::Update(
$id,
array(
'QUANTITY' => $arFields['QUANTITY'] + $add /*новое количество товаров*/,
'QUANTITY_RESERVE' => $arFields['QUANTITY_RESERVE'] + $add /*новое количество зарезервированных товаров*/
)
);
$_SESSION["1C_UPDATE"] = true;
}
}
);
//При окончании импорта из 1С устанавливаем сессионную переменную
AddEventHandler(
'catalog',
'OnSuccessCatalogImport1C',
function ()
{
$_SESSION["1C_UPDATE"] = false;
}
);
Дополнительные решения возникающих проблем при обмене описываю здесь Битрикс: обмен с 1С
Сложная ORM выборка для получения всех необходимых данных товара
Достаточно часто возникает задача ускорить тот или иной компонент, вывести товары "как в каталоге" или получить все необходимые данные по элементу товара. Сделать это можно как с помощью обычных компонентов, так и с помощью ORM.
Делюсь примером ORM запроса сложного случая. На сайте клиента плевая выборка из 20 товаров на базе catalog.section
существенно грузила систему и отрабатывала в среднем за 1.5с. Объединив все выборки в один запрос смог ускорить выбор до 0.4-0.5с без кеширования, а с кешированием результатов до 0.1с
Данный ORM запрос выводит:
- обычные товары и товары с ТП.
- запрос получает несколько типов цен, включая базовую
- запрос получает товары из раздела включая подразделы
- запрос сортирует товары в случайном RAND порядке
- запрос корректно генерирует URL
- в данном запросе свойства товара хранятся в общей таблице (важно)
- запрос суммирует все значения остатков на складах
$IBLOCK_ID = 29;
//Свойства в общей таблице
$entityPropsSingle = \Bitrix\Main\Entity\Base::compileEntity(
'PROPS_COMMON',
[
'IBLOCK_ELEMENT_ID' => ['data_type' => 'integer'],
'IBLOCK_PROPERTY_ID' => ['data_type' => 'integer'],
'VALUE' => ['data_type' => 'string'] // там ещё есть столбцы типа VALUE_TYPE, VALUE_ENUM, VALUE_NUM, DESCRIPTION, но они нам тут не нужны, поэтому опускаем
],
[
'table_name' => 'b_iblock_element_property', // общая таблица со свойствами
]
);
/*Получаем 20 случайных товаров в разделе, включая вложенные*/
$elements = \Bitrix\Iblock\ElementTable::getList([
'filter' => [
'SECTION.ID' => $arResult['SECTION']['PATH'][0]['ID'], // Раздел, в котором ищем включая подразделы.
'==LINK.ADDITIONAL_PROPERTY_ID' => NULL, // Основная привязка элементов к группам.
'ACTIVE' => 'Y', // Активные элементы.
'!=ID' => [$arResult['ID']], //Исключаем из выборки ненужный элемент
'>base_price.PRICE' => 0, // Исключаем товары с нулевой базовой ценой
'=PROPS_335.IBLOCK_PROPERTY_ID' => 335, // id свойства артикул
'=PRODUCT.AVAILABLE' => 'Y', // Доступный к покупке
//'>PRODUCT.QUANTITY' => 0, // Есть остатки
//'!=PRODUCT.TYPE' => 1, // Товар с ТП
],
'select' => [
'ID',
'NAME',
'IBLOCK_ID', //Элемент построения урл
'IBLOCK_SECTION_ID', //Элемент построения урл
'CODE', //Элемент построения урл
"QUANTITY" => "PRODUCT.QUANTITY", //Количество товаров
//'QUANTITY_STORE', //Кол-во на складах
"TYPE" => "PRODUCT.TYPE", //Тип товара
'DETAIL_PAGE_URL' => 'IBLOCK.DETAIL_PAGE_URL', //маска урл
"PRICE_7" => "base_price.PRICE", //базовая цена
"PRICE_8" => "prop_PRICE_8.PRICE", //оптовая цена
'ART_NUMBER' => 'PROPS_335.VALUE', //Свойство Артикула с ID 335
'AVAILABLE' => 'PRODUCT.AVAILABLE', //Доступность
],
'order' => ['RAND' => 'ASC'],
//'group' => [ 'ID' ], //Группировка недоступна при указанной сортировке
'limit' => 20,
'runtime' => [
//Получаем элемента раздела
'LINK' => [
'data_type' => \Bitrix\Iblock\SectionElementTable::class,
'reference' => [
'=this.ID' => 'ref.IBLOCK_ELEMENT_ID',
],
'join_type' => 'inner',
],
//Получаем родительский раздел
'PARENT' => [
'data_type' => \Bitrix\Iblock\SectionTable::class,
'reference' => [
'=this.LINK.IBLOCK_SECTION_ID' => 'ref.ID',
],
'join_type' => 'inner',
],
//Подключаем таблицу товара
'PRODUCT' => [
'data_type' => \Bitrix\Catalog\ProductTable::class,
'reference' => [
'=this.ID' => 'ref.ID',
],
'join_type' => 'left'
],
//Подключаем таблицу разделов
'SECTION' => [
'data_type' => \Bitrix\Iblock\SectionTable::class,
'reference' => [
'=this.PARENT.IBLOCK_ID' => 'ref.IBLOCK_ID',
'<=ref.LEFT_MARGIN' => 'this.PARENT.LEFT_MARGIN',
'>=ref.RIGHT_MARGIN' => 'this.PARENT.RIGHT_MARGIN',
],
'join_type' => 'inner',
],
//Получаем значение базовой цены с ID 7
'base_price' => [
'data_type' => '\Bitrix\Catalog\PriceTable',
'reference' => [
'=this.ID' => 'ref.PRODUCT_ID'
],
'join_type' => 'inner'
],
//Получаем значение оптовой цены с ID 8
'prop_PRICE_8' => [
'data_type' => '\Bitrix\Catalog\PriceTable',
'reference' => [
'=this.ID' => 'ref.PRODUCT_ID'
],
'join_type' => 'inner'
],
//Получаем значение свойства 335 (Артикул)
'PROPS_335' => [
'data_type' => $entityPropsSingle->getDataClass(),
'reference' => [
'=this.ID' => 'ref.IBLOCK_ELEMENT_ID'
],
'join_type' => 'inner'
],
//Подключаем таблицу складов
/*'STORE' => [
'data_type' => \Bitrix\Catalog\StoreProductTable::class,
'reference' => [
'=this.ID' => 'ref.PRODUCT_ID'
],
'join_type' => 'inner'
],
//Суммируем остатки на всех доступных складах
'QUANTITY_STORE' => [
'data_type' => 'integer',
'expression' => ['sum(%s)', 'STORE.AMOUNT']
],*/
//Определяем кастомный порядок сортировку "Случайный"
'RAND'=>array('data_type' => 'float', 'expression' => array('RAND()')),
],
'cache' => array( // Кеш запроса. Сброс можно сделать методом \Bitrix\Iblock\ElementTable::getEntity()->cleanCache();
'ttl' => 86400, // Время жизни кеша сутки
'cache_joins' => true // Кешировать ли выборки с JOIN
),
]);
$elements = $elements->fetchAll();
foreach ($elements as &$element){
$element['DETAIL_PAGE_URL'] = \CIBlock::ReplaceDetailUrl($element['DETAIL_PAGE_URL'], $element, false, 'E');
$element['HAVE_OFFERS'] = $element['TYPE'] != 1 ? true : false;
$element['QUANTITY'] = $element['QUANTITY'] > $element['QUANTITY_STORE'] ? $element['QUANTITY'] : $element['QUANTITY_STORE'];
}
Для свойств в отдельной таблице можно воспользоваться другой декларацией entityPropsSingle
//Свойства в отдельной таблице
$entityPropsSingle = \Bitrix\Main\Entity\Base::compileEntity(
'PROPS_SINGLE_IB29',
[
'IBLOCK_ELEMENT_ID' => ['data_type' => 'integer'],
'PROPERTY_335' => ['data_type' => 'integer'],
],
[
'table_name' => 'b_iblock_element_prop_s29',
]
);
Где свойство хранится в инфоблоке с ID 29
, а само свойство имеет ID 335
Проверка на детальную страницу в header
Пример плохой, поскольку добавляет лишний запрос к базе данных при каждом хите, пользуйтесь в крайнем случае
public static function checkIsDetail() {
global $APPLICATION;
$IBLOCK_ID = 24;
if(!\CModule::IncludeModule("iblock")) return false;
$url = $APPLICATION->GetCurPage();
$code = array_pop(array_filter(explode( '/', $url)));
$rsSections = \CIBlockSection::GetList([], ['IBLOCK_ID' => $IBLOCK_ID, '=CODE' => $code]);
return ($rsSections->Fetch() !== false);
}
$is_section_page = self::checkIsDetail();
if(\CSite::InDir('/catalog/') && $APPLICATION->GetCurPage() !== '/catalog/' && $is_section_page){
//Мы на детальной странице каталога
}
Получить все элементы из раздела и вложенных подразделов
$SECTION_ID = 5;
$IBLOCK_ID = 8;
$section = \Bitrix\Iblock\SectionTable::getByPrimary($SECTION_ID, [
'filter' => ['IBLOCK_ID' => $IBLOCK_ID],
'select' => ['LEFT_MARGIN', 'RIGHT_MARGIN'],
])->fetch();
$arItems = \Bitrix\Iblock\ElementTable::getList([
'select' => ['ID', 'NAME', 'IBLOCK_ID'],
'filter' => [
'IBLOCK_ID' => $IBLOCK_ID,
'>=IBLOCK_SECTION.LEFT_MARGIN' => $section['LEFT_MARGIN'],
'<=IBLOCK_SECTION.RIGHT_MARGIN' => $section['RIGHT_MARGIN'],
],
]);
или так
$SECTION_ID = 5;
$IBLOCK_ID = 8;
$SECTION_IDS = getSubsections($SECTION_ID, $IBLOCK_ID);
function getSubsections(int $SECTION_ID, int $IBLOCK_ID): array
{
$SECTION_IDS = [];
$connection = \Bitrix\Main\Application::getConnection();
$sql = sprintf('SELECT cs.ID FROM %1$s AS ps
INNER JOIN %1$s AS cs
ON ps.LEFT_MARGIN <= cs.LEFT_MARGIN AND ps.RIGHT_MARGIN >= cs.RIGHT_MARGIN AND ps.IBLOCK_ID = cs.IBLOCK_ID
WHERE ps.ID = %2$d AND ps.IBLOCK_ID = %3$d',
\Bitrix\Iblock\SectionTable::getTableName(),
$SECTION_ID,
$IBLOCK_ID
);
$result = $connection->query($sql);
while ($section = $result->fetch()) {
$SECTION_IDS[] = $section['ID'];
}
return $SECTION_IDS;
}
$arItems = \Bitrix\Iblock\ElementTable::getList([
'select' => ['ID', 'NAME', 'IBLOCK_ID'],
'filter' => [
'IBLOCK_ID' => $IBLOCK_ID,
'IBLOCK_SECTION_ID' => $SECTION_IDS,
],
]);
Стоит ознакомиться:
Комментарии