Или не сложная Регистрация
Печать
PDF

Вывести все товары со скидкой Bitrix

Была задача: показать все товары со скидкой, но при этом не положить сервер при 3500 товарных позиций.

В основном все делают таким образом: ставят товару свойство типа "Товар со скидкой" и потом раз в сутки через крон скрипт пробегают по всей базе, чтобы проставить значение всем товарам. А выводить то по свойству уже легко.

Я такой вариант сразу "отмел" у себя в голове и начал думать каким образом можно реализовать актуальную ситуацию при этом не сильно нагружая сервер. Ведь в предыдущей версии сервер просто "плачет" раз в сутки.

В итоге у меня получилась следующая конструкция:

 
<?php
use Bitrix\Main\Loader;
use Bitrix\Main\SystemException;
 
class AllProductDiscount{
    /**
     * @return XML_ID|array
     * @throws SystemException
     * @throws \Bitrix\Main\LoaderException
     */
    public static function getFull($arrFilter = array(), $arSelect = array()){
        if(!Loader::includeModule('sale')) throw new SystemException('Не подключен модуль Sale');
 
        //Все товары со скидкой!!!
    // Группы пользователей
    global $USER;
    $arUserGroups = $USER->GetUserGroupArray();
    if (!is_array($arUserGroups)) $arUserGroups = array($arUserGroups);
    // Достаем старым методом только ID скидок привязанных к группам пользователей по ограничениям
    $actionsNotTemp = \CSaleDiscount::GetList(array("ID" => "ASC"),array("USER_GROUPS" => $arUserGroups),false,false,array("ID"));
    while($actionNot = $actionsNotTemp->fetch()){
      $actionIds[] = $actionNot['ID'];
    }
    $actionIds=array_unique($actionIds); sort($actionIds);
    // Подготавливаем необходимые переменные для разборчивости кода
    global $DB;
    $conditionLogic = array('Equal'=>'=','Not'=>'!','Great'=>'>','Less'=>'<','EqGr'=>'>=','EqLs'=>'<=');
    $arSelect = array_merge(array("ID","IBLOCK_ID","XML_ID"),$arSelect);
    $city='MSK';
    // Теперь достаем новым методом скидки с условиями. P.S. Старым методом этого делать не нужно из-за очень высокой нагрузки (уже тестировал)
    $actions = \Bitrix\Sale\Internals\DiscountTable::getList(array(
      'select' => array("ID","ACTIONS_LIST"),
      'filter' => array("ACTIVE"=>"Y","USE_COUPONS"=>"N","DISCOUNT_TYPE"=>"P","LID"=>SITE_ID,
      "ID"=>$actionIds,
      array(
        "LOGIC" => "OR",
        array(
          "<=ACTIVE_FROM"=>$DB->FormatDate(date("Y-m-d H:i:s"),"YYYY-MM-DD HH:MI:SS",\CSite::GetDateFormat("FULL")),
          ">=ACTIVE_TO"=>$DB->FormatDate(date("Y-m-d H:i:s"),"YYYY-MM-DD HH:MI:SS",\CSite::GetDateFormat("FULL"))
        ),
        array(
          "=ACTIVE_FROM"=>false,
          ">=ACTIVE_TO"=>$DB->FormatDate(date("Y-m-d H:i:s"),"YYYY-MM-DD HH:MI:SS",\CSite::GetDateFormat("FULL"))
        ),
        array(
          "<=ACTIVE_FROM"=>$DB->FormatDate(date("Y-m-d H:i:s"),"YYYY-MM-DD HH:MI:SS",\CSite::GetDateFormat("FULL")),
          "=ACTIVE_TO"=>false
        ),
        array(
          "=ACTIVE_FROM"=>false,
          "=ACTIVE_TO"=>false
        ),
      ))
    ));
    // Перебираем каждую скидку и подготавливаем условия фильтрации для CIBlockElement::GetList
    while($action = $actions->fetch()){
      //$dopCheck=false;
      $arFilter = array_merge(array("ACTIVE_DATE"=>"Y", "CAN_BUY"=>"Y"),$arrFilter); //Набор предустановленных параметров
      //Магия генерации фильтра
      foreach($action['ACTIONS_LIST']['CHILDREN'] as $condition){
        foreach($condition['CHILDREN'] as $keyConditionSub=>$conditionSub){
          $cs=$conditionSub['DATA']['value']; //Значение условия
          $cls=$conditionLogic[$conditionSub['DATA']['logic']]; //Оператор условия
          //$arFilter["LOGIC"]=$conditionSub['DATA']['All']?:'AND';
          $CLASS_ID = explode(':',$conditionSub['CLASS_ID']);
 
          if($CLASS_ID[0]=='ActSaleSubGrp') {
            foreach($conditionSub['CHILDREN'] as $keyConditionSubElem=>$conditionSubElem){
              $cse=$conditionSubElem['DATA']['value']; //Значение условия
              $clse=$conditionLogic[$conditionSubElem['DATA']['logic']]; //Оператор условия
              //$arFilter["LOGIC"]=$conditionSubElem['DATA']['All']?:'AND';
              $CLASS_ID_EL = explode(':',$conditionSubElem['CLASS_ID']);
 
              if($CLASS_ID_EL[0]=='CondIBProp') {
                $arFilter["IBLOCK_ID"]=$CLASS_ID_EL[1];
                $arFilter[$clse."PROPERTY_".$CLASS_ID_EL[2]]=array_merge((array)$arFilter[$clse."PROPERTY_".$CLASS_ID_EL[2]],(array)$cse);
                $arFilter[$clse."PROPERTY_".$CLASS_ID_EL[2]]=array_unique($arFilter[$clse."PROPERTY_".$CLASS_ID_EL[2]]);
              }elseif($CLASS_ID_EL[0]=='CondIBName') {
                $arFilter[$clse."NAME"]=array_merge((array)$arFilter[$clse."NAME"],(array)$cse);
                $arFilter[$clse."NAME"]=array_unique($arFilter[$clse."NAME"]);
              }elseif($CLASS_ID_EL[0]=='CondIBElement') {
                $arFilter[$clse."ID"]=array_merge((array)$arFilter[$clse."ID"],(array)$cse);
                $arFilter[$clse."ID"]=array_unique($arFilter[$clse."ID"]);
              }elseif($CLASS_ID_EL[0]=='CondIBTags') {
                $arFilter[$clse."TAGS"]=array_merge((array)$arFilter[$clse."TAGS"],(array)$cse);
                $arFilter[$clse."TAGS"]=array_unique($arFilter[$clse."TAGS"]);
              }elseif($CLASS_ID_EL[0]=='CondIBSection') {
                $arFilter[$clse."SECTION_ID"]=array_merge((array)$arFilter[$clse."SECTION_ID"],(array)$cse);
                $arFilter[$clse."SECTION_ID"]=array_unique($arFilter[$clse."SECTION_ID"]);
              }elseif($CLASS_ID_EL[0]=='CondIBXmlID') {
                $arFilter[$clse."XML_ID"]=array_merge((array)$arFilter[$clse."XML_ID"],(array)$cse);
                $arFilter[$clse."XML_ID"]=array_unique($arFilter[$clse."XML_ID"]);
              //}elseif($CLASS_ID_EL[0]=='CondBsktAppliedDiscount') { //Условие: Были применены скидки (Y/N)
                //$dopCheck=true;
              }
            }
          }elseif($CLASS_ID[0]=='CondIBProp') {
            $arFilter["IBLOCK_ID"]=$CLASS_ID[1];
            $arFilter[$cls."PROPERTY_".$CLASS_ID[2]]=array_merge((array)$arFilter[$cls."PROPERTY_".$CLASS_ID[2]],(array)$cs);
            $arFilter[$cls."PROPERTY_".$CLASS_ID[2]]=array_unique($arFilter[$cls."PROPERTY_".$CLASS_ID[2]]);
          }elseif($CLASS_ID[0]=='CondIBName') {
            $arFilter[$cls."NAME"]=array_merge((array)$arFilter[$cls."NAME"],(array)$cs);
            $arFilter[$cls."NAME"]=array_unique($arFilter[$cls."NAME"]);
          }elseif($CLASS_ID[0]=='CondIBElement') {
            $arFilter[$cls."ID"]=array_merge((array)$arFilter[$cls."ID"],(array)$cs);
            $arFilter[$cls."ID"]=array_unique($arFilter[$cls."ID"]);
          }elseif($CLASS_ID[0]=='CondIBTags') {
            $arFilter[$cls."TAGS"]=array_merge((array)$arFilter[$cls."TAGS"],(array)$cs);
            $arFilter[$cls."TAGS"]=array_unique($arFilter[$cls."TAGS"]);
          }elseif($CLASS_ID[0]=='CondIBSection') {
            $arFilter[$cls."SECTION_ID"]=array_merge((array)$arFilter[$cls."SECTION_ID"],(array)$cs);
            $arFilter[$cls."SECTION_ID"]=array_unique($arFilter[$cls."SECTION_ID"]);
          }elseif($CLASS_ID[0]=='CondIBXmlID') {
            $arFilter[$cls."XML_ID"]=array_merge((array)$arFilter[$cls."XML_ID"],(array)$cs);
            $arFilter[$cls."XML_ID"]=array_unique($arFilter[$cls."XML_ID"]);
          //}elseif($CLASS_ID[0]=='CondBsktAppliedDiscount') { //Условие: Были применены скидки (Y/N)
            //$dopCheck=true;
          }
        }
      }
      //Делаем запрос по каждому из фильтров, т.к. один фильтр не полчится сделать из-за противоречий условий каждой скидки
      $res = \CIBlockElement::GetList(array(), $arFilter, false, array("nPageSize"=>50), $arSelect);
      while($ob = $res->GetNextElement()){
        $arFields = $ob->GetFields();
        $optimal = \CCatalogProduct::GetOptimalPrice($arFields['ID']);
        if($optimal['RESULT_PRICE']['PERCENT']>0){
          $poductsArray['ids'][] = $arFields["ID"];
        }
      }
    }
    $poductsArray['ids']=array_unique($poductsArray['ids']);
 
    //Конечная сортировка по скидкам
    usort($poductsArray['items'], function ($a, $b){
      if ($a['diff'] == $b['diff']) {
        return 0;
      }
      return ($a['diff'] > $b['diff']) ? -1 : 1;
    });
 
        return $poductsArray;
    }
}
 

В среднем время отработки у меня получалось меньше полсекунды. Но из-за многих факторов (кол-ва товаров со скидкой, настроек сервера и т.д.) может отрабатывать и больше. Не забываем про возможность кеширования и тогда вообще "огонь" по времени получится!

Одно ясно точно: информация будет актуальна! К тому же без особой нагрузки в сторону сервера.

Всем добра. Если заметите неточности пишите в комментах. Класс из боевого проекта вставил и чуть почистил, поэтому мог что то пропустить и/или недописать.

Комментарии  

#394
+1-1 0
Добрый день!

В конечно итоге можно просто взять ваш последний кусок кода - этого будет достаточно:

while($ob = $res->GetNextEl ement()){
$arFields = $ob->GetFields( );
$optimal = CCatalogProduct ::GetOptimalPri ce($arFields['I D']);
if($optimal['RE SULT_PRICE']['P ERCENT']>0){
$poductsArray[' ids'][] = $arFields["ID"] ;
}
}
Ответить | Ответить с цитатой | Цитировать
#395
+1-1 0
Добрый день!
Если у вас в каталоге только 100 товаров, то, возможно, вам будет достаточно... Но я написал класс, который работает быстрее примерно в 15-20 раз. И при этом не создает нагрузки на сервер, такой, как просто перебирание каждого товара.

Например у меня 3500 товаров и у меня такая простая конструкция будет сильно загружать сервер и в конечном итоге "положит" его. Тестируйте с microtime и сами увидите время отработки.
Ответить | Ответить с цитатой | Цитировать
#396
+1-1 0
Вы правы, скорость сильно выше!
Искал подобное решение уже очень давно, спасибо вам!!)))
Ответить | Ответить с цитатой | Цитировать
#397
+1-1 0
Могу еще похвастаться тем, что на данный момент подобных решений просто нет! =)
Ответить | Ответить с цитатой | Цитировать
#398
+1-1 0
А я знаю)) Я же и нашел в яндексе по дате с нового года и ничего путного нет))
Ответить | Ответить с цитатой | Цитировать

Если вы хотите разместить рекламу на сайте (= ПОЗИТИВ =), то пишите через форму обратной связи в разделе: Контакт с админом