Magento 2 – Filter by store_id Using Product Repository

filtermagento2PHPrepository

In Magento 2, it's not possible to add a store_id filter to a product collection directly. Instead, you need to use the addStoreFilter method, which adds a special "product limitation filter". It appears that addFieldToFilter is meant for attributes specific to a particular collection, and the product limitation filters are meant for attributes that might be shared globally across collections (store_id, website_id, category_id, etc).

Given that, is is possible to create a filter for a product repository to get a list of products filtered by store_id? Attempting to use a filter like this with a repository

/* @var $filterBuilder \Magento\Framework\Api\FilterBuilder */
$this->filterBuilder->setField('store_id')
    ->setValue('1')
    ->setConditionType('eq')
    ->create();

produces the following error

Invalid attribute name: store_id

i.e. the repository attempts to create a filter on the underlying collection with the addFieldToFilter method.

Best Answer

Bug is created to add filtration capability by store ID, see this response.

The following plugin can be used as a workaround before the issue is fixed in the core. It allows to avoid hacks in the core code and will not break your installation even after this issue is fixed.

Add plugin declaration in the global area, VendorName/ModuleName/etc/di.xml

<type name="Magento\Catalog\Model\ResourceModel\Product\Collection">
    <plugin name="storeFilterFixer" type="VendorName\ModuleName\Model\Plugin\StoreFilterFixer" />
</type>

Create class VendorName/ModuleName/Model/Plugin/StoreFilterFixer.php

<?php
namespace VendorName\ModuleName\Model\Plugin;

use Magento\Catalog\Model\ResourceModel\Product\Collection as ProductCollection;

/**
 * This plugin fixes issue with filtration by store/website ID in \Magento\Catalog\Model\ProductRepository::getList()
 */
class StoreFilterFixer
{
    /**
     * Enable filtration by store_id or website_id. The only supported condition is 'eq'
     *
     * @param ProductCollection $subject
     * @param \Closure $proceed
     * @param array $fields
     * @param string|null $condition
     * @return ProductCollection
     */
    public function aroundAddFieldToFilter(ProductCollection $subject, \Closure $proceed, $fields, $condition = null)
    {
        if (is_array($fields)) {
            foreach ($fields as $key => $filter) {
                if ($filter['attribute'] == 'website_id' && isset($filter['eq'])) {
                    $subject->addWebsiteFilter([$filter['eq']]);
                    unset($fields[$key]);
                } else if ($filter['attribute'] == 'store_id' && isset($filter['eq'])) {
                    $subject->addStoreFilter($filter['eq']);
                    unset($fields[$key]);
                }
            }
        }
        /** Do not try to pass empty $fields to addFieldToFilter, it will cause exception */
        return $fields? $proceed($fields, $condition) : $subject;
    }
}