Yandex Metrika
sanches.free 19 просмотров

Корзина Sale в Битрикс D7: загрузка, строки позиций и ORM

Введение и модуль sale

Торговое ядро «1С‑Битрикс» уже много версий живёт поверх объектного слоя D7. Большинство задач корзины решаются классами в пространстве Bitrix\Sale, без смешения с устаревающими API на C. Перед любым примером нужно включить модуль: \Bitrix\Main\Loader::includeModule('sale') и при работе с валютой — при необходимости 'currency'/'catalog'.

Загрузка Basket

Для активного интернет-посетителя корзину обычно создают из пары значений «F_USER» и символьного кода сайта. Код сайта вытягивают из текущего контекста приложения — имейте в виду ограничения зоны: в некоторых backend-вывозках Context::getCurrent()->getSite() недоступен так же предсказуемо, как на витрине.

use Bitrix\Main\Context;
use Bitrix\Sale;

$siteSlug = Context::getCurrent()->getSite();
$guestCartOwnerId = Sale\Fuser::getId();

$guestCartBasket = Sale\Basket::loadItemsForFUser($guestCartOwnerId, $siteSlug);

$orderPk = 2048;
$basketOwnedByOrder = Sale\Order::load($orderPk)->getBasket();

// Если заказ уже в переменной:
// $basketOwnedByOrder = Sale\Basket::loadItemsForOrder($issuedOrderEntity);

Агрегаты и массовые операции

Суммы и вес не обязательно собирать вручную: ->getPrice() уже учитывает скидки, ->getBasePrice() показывает расчёт до их применения, ->getWeight() агрегирует строки.

Строку с известным внутренним идентификатором убирайте методом удаления записи перед сохранением корзины:

$guestCartBasket->getItemById($basketPrimaryKey)->delete(); затем $guestCartBasket->save();

Срез доступных для оформления позиций: ->getOrderableItems(). Для писем и отладочного текстового дампа есть getListOfFormatText(), для быстрых пересчётов количества — getQuantityList() в связке с array_sum или count.

Добавление или изменение SKU из каталога

Схема близка к логике старого CSaleBasket::Add: ищем существующую строку источника 'catalog' и идентификатора товара через getExistsItem. Если нашли — увеличиваем число штук, иначе создаём createItem и задаём базовые поля: количество, валюта, сайт и класс-провайдер для учёта остатков.

use Bitrix\Currency\CurrencyManager;
use Bitrix\Main\Context;

$skuElementKey = 405;      // элемент каталога
$unitsPacked = 3;

if ($existingLine = $guestCartBasket->getExistsItem('catalog', $skuElementKey)) {
    $existingLine->setField(
        'QUANTITY',
        $existingLine->getQuantity() + $unitsPacked
    );
} else {
    $draftLine = $guestCartBasket->createItem('catalog', $skuElementKey);
    $draftLine->setFields([
        'QUANTITY'   => $unitsPacked,
        'CURRENCY'   => CurrencyManager::getBaseCurrency(),
        'LID'        => Context::getCurrent()->getSite(),
        'PRODUCT_PROVIDER_CLASS' => 'CCatalogProductProvider',
    ]);
    /*
    Своя ставка без пересчёта прайса:
    $draftLine->setFields([
        'QUANTITY'       => $unitsPacked,
        'CURRENCY'       => CurrencyManager::getBaseCurrency(),
        'LID'            => Context::getCurrent()->getSite(),
        'PRICE'          => $negotiatedAmount,
        'CUSTOM_PRICE'   => 'Y',
    ]);
    */
}

$guestCartBasket->save();

Отдельно существует «коробочное» добавление после Loader::includeModule('catalog'): \Bitrix\Catalog\Product\Basket::addProduct(['PRODUCT_ID' => …, 'QUANTITY' => …, 'PROPS' => …]). Результат — объект с успехами/ошибками (isSuccess(), getErrorMessages()): проверки остатка и аккуратное слияние дубликатов встроены, зато жёстко зафиксированная вручную цена через этот путь не задаётся.

Строка корзины и свойства

Каждый элемент — экземпляр Sale\BasketItem; полный массив выдаёт ->getBasketItems(), а сам объект Basket поддерживает итерацию и доступ как к коллекции — можно писать foreach ($guestCartBasket as $lineEntity).

$lineCollection = $guestCartBasket->getBasketItems();
$singleLine = $lineCollection[0];

$singleLine->getId();
$singleLine->getProductId();
$singleLine->getPrice();
$singleLine->getQuantity();
$singleLine->getFinalPrice();
$singleLine->getWeight();
$singleLine->getField('NAME');
$singleLine->canBuy();
$singleLine->isDelay();

foreach ($guestCartBasket as $lineEntity) {
    echo $lineEntity->getField('NAME') . ' — ' . $lineEntity->getQuantity() . "\n";
}

$propBag = $singleLine->getPropertyCollection();
$rowValues = $propBag->getPropertyValues();

$propBag->setProperty([
    [
        'NAME' => 'Габарит',
        'CODE' => 'PACKAGE_SIZE',
        'VALUE' => '600×400',
        'SORT' => 120,
    ],
]);
$propBag->save();

$parentCart = $singleLine->getCollection();

Чтобы удалить одно свойство, обходят коллекцию $propBag, сравнивают код и вызывают ->delete() у найденного элемента, после чего снова сохраняют контейнер.

Прямые запросы: Internals\BasketTable

Для сводной аналитики без поднятия бизнес-объекта удобен ORM-класс Sale\Internals\BasketTable; список значений свойств по строкам читают из Sale\Internals\BasketPropertyTable, фильтруя полем BASKET_ID.

use Bitrix\Sale;

$listHandle = Sale\Internals\BasketTable::getList([
    'filter' => [
        'FUSER_ID' => Sale\Fuser::getId(),
        'ORDER_ID' => null,
        'LID' => SITE_ID,
        'CAN_BUY' => 'Y',
    ],
]);

while ($dbRow = $listHandle->fetch()) {
    // обработать $dbRow
}

$rollupRow = Sale\Internals\BasketTable::getList([
    'filter' => [
        'FUSER_ID' => Sale\Fuser::getId(),
        'ORDER_ID' => null,
        'LID' => SITE_ID,
        'CAN_BUY' => 'Y',
    ],
    'select' => ['CNT_LINES', 'MONEY_TOTAL'],
    'runtime' => [
        new \Bitrix\Main\Entity\ExpressionField('CNT_LINES', 'COUNT(*)'),
        new \Bitrix\Main\Entity\ExpressionField(
            'MONEY_TOTAL',
            'SUM(PRICE * QUANTITY)'
        ),
    ],
])->fetch();

Вывод для разработчика

  • Держите единственный объект Basket на сценарий и завершайте изменения общим save().
  • Не смешивайте объектный слой со старым CSale*, если хотите сохранять инварианты скидок и свойств строк.
  • ORM нужен для массовых отчётов, UI и бизнес-правил — там, где нужны события и провайдеры, всё равно возвращайтесь к объектам Sale.

Не хотите копаться сами?

Починю за 1-3 дня. Без предоплаты — оплата по результату.

15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии