Magento 2 Configurable product получить минимальную цену товара

Magento 2 Configurable product получить минимальную цену товара

Использование конфигурируемых товаров в интернет-магазинах на платформе Magento – частое явление, особенно если вы продаете товары по конкретно выбранному атрибуту или атрибутам, например цвет или/и размер. На этот случай в магазине предусмотрен тип товара – конфигурируемый, который содержит в себе набор простых товаров объединенных по каким то атрибутам. Так же итоговая стоимость товара может изменятся  от выбранной расцветки или размера, что вполне может быть логичным, например, цена за комплект постельного белья может изменятся в зависимости от выбранного размера или материала.

Поскольку в конфигурируемый товар входит разное количество простых товаров у которых может быть разная цена, то в категории/каталоге, где товар отображается необходимо отображать текущую цену, но какую цену отображать? Если следовать логике сортировки и фильтрации товаров в интернет-магазине, то при выборе сортировки по умолчанию (обычно это от дешевых к дорогим) требуется отображать цену самого дешевого товара, который входит в набор, как ее получить смотрите ниже.

В этом примере мы рассматриваем только получение цены самого дешевого товара. Для корректной и полноценной картины, так же потребуется получать цену самого дорого товара, а так же при выборе в фильтре конкретного размера или цвета, отображать цену простого товара, который соответствует выбранному атрибуту. Если внешний вид вашего фильтра предусматривает отображение количества товара по какому то атрибуту, то это тоже необходимо учесть.

 

Предположим, что мы уже выбрали метод и путь по которому мы будем переписывать цену для конфигурируемого товара. Один из наиболее распространенных это использование плагина (plugin) для перехвата функции, что бы выполнить свой код, до, после или вместо какой то функции.

 

В качестве примера напишем плагин (interceptor), который будет использовать Arround Method для функции resolvePrice  класса vendor/magento/module-configurable-product/Pricing/Price/ConfigurablePriceResolver.php, который будет вызываться до и после функции и таким образом полностью перепишет ее.

Более подробно, о том функции какого типа можно перехватывать можно ознакомиться в официальной документации Mgento 2 там же можно почитать и за другие настройки – возможности плагина.

 

Пример класса плагина

<?php
/**
 * Class description
 * 
 * SYSINT Team <team@sysint.net>
 */

namespace Sysint\Product\Pricing;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Model\ResourceModel\Product\BaseSelectProcessorInterface;
use Magento\Framework\Pricing\PriceCurrencyInterface;
use Magento\Framework\DB\Select;

class ConfigurablePrice
{
    
    /**
     * Customer session
     * @var \Magento\Customer\Model\Session
     */
    private $customerSession;

    /**
     * Metadata pool
     * @var \Magento\Framework\EntityManager\MetadataPool
     */
    private $metadataPool;

    /**
     * Resource
     * @var \Magento\Framework\App\ResourceConnection
     */
    private $resource;

    /**
     * @var PriceCurrencyInterface
     */
    protected $priceCurrency;

    public function __construct(
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Framework\EntityManager\MetadataPool $metadataPool,
        \Magento\Framework\App\ResourceConnection $resourceConnection,
        \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency,
        \Magento\Store\Model\StoreManager $storeManager,
    )
    {
        
        $this->_storeManager = $storeManager;

        $this->resource = $resourceConnection;
        $this->customerSession = $customerSession;
        $this->metadataPool = $metadataPool;

        $this->priceCurrency = $priceCurrency;
    }

    /**
     * Retrieve minimal price
     * @param \Magento\Framework\Pricing\SaleableInterface|\Magento\Catalog\Model\Product $product
     * @return float
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function aroundResolvePrice($subject, \Closure $proceed,\Magento\Framework\Pricing\SaleableInterface $product)
    {
        //get parent product id
        $productId = (isset($product['entity_id'])) ? $product['entity_id'] : null;

        if (is_numeric($productId)){

            $linkField = $this->metadataPool->getMetadata(ProductInterface::class)->getLinkField();
            $productTable = $this->resource->getTableName('catalog_product_entity');

            $priceSelect = $this->resource->getConnection()->select()
                ->from(['parent' => $productTable], '')
                ->joinInner(
                    ['link' => $this->resource->getTableName('catalog_product_relation')],
                    "link.parent_id = parent.$linkField",
                    []
                )->joinInner(
                    [BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS => $productTable],
                    sprintf('%s.entity_id = link.child_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS),
                    []
                )->joinInner(
                    ['t' => $this->resource->getTableName('catalog_product_index_price')],
                    sprintf('t.entity_id = %s.entity_id', BaseSelectProcessorInterface::PRODUCT_TABLE_ALIAS),
                    ['min_price']
                )->where('parent.entity_id = ?', $productId)
                ->where('t.website_id = ?', $this->_storeManager->getStore()->getWebsiteId())
                ->where('t.customer_group_id = ?', $this->customerSession->getCustomerGroupId())
                ->order('t.min_price ' . Select::SQL_ASC)
                ->limit(1);

            $price = $this->resource->getConnection()->fetchOne($priceSelect);

            return $this->priceCurrency->convert($price);
        }
    }

}

 

Информация о минимальной цене будет взята из индексной таблицы, без использования паттерна lazy loading, это на много быстрее по времени и менее затратно по ресурсам.

Пример выше, всего лишь маленькая часть того, что необходимо реализовать для корректной и быстрой работы метода получения минимальной цены конфигурируемого товара. Так же стоит помнить, что любые изменения несут последствия и порой безобидная функция может создать приличную нагрузку. По этому нужно ко всему подходить расчетливо.

Перед тем как приступить к реализации, мы проектируем будущий функционал практически на все случаи жизни, ну или хотя бы на те, что обязательно или с большой долей вероятности могут случиться. Тесты, профилирование, нагрузка, сем раз отмерять один раз приклеить – наше все.