How to get Filterable Attributes from a category in Magento 2

11.8k views Asked by At

I have created category "Bag" in Magento 2. having filter attribute:

  1. color
  2. Size

I'm trying to get Filterable Attributes from category "Bag".

I have already done this in Magento 1.9:

Mage::app()->setCurrentStore($store);
$layer = Mage::getModel("catalog/layer");
$category = Mage::getModel("catalog/category")->load($categoryid);  
$layer->setCurrentCategory($category);
$attributes = $layer->getFilterableAttributes();

But it does not seem to work for 2.x

2

There are 2 answers

9
Sergii Ivashchenko On

I faced the same problem recently.

I documented my investigation here.

I was not able to find framework api to provide filterable attributes for specific category, however I will share workarounds.

Basically all filterable attributes in Magento 2 can be retrived from FilterableAttributeList:

$filterableAttributes = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Category\FilterableAttributeList::class);
$attributes = $filterableAttributes->getList();

Please use DI instead of ObjectManager::getInstance(). I used it just to have more compact example :)

Retrieving filters involved in layered navigation is a bit more tricky.

$filterableAttributes = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Category\FilterableAttributeList::class);

$appState = ObjectManager::getInstance()->get(\Magento\Framework\App\State::class);
$layerResolver = ObjectManager::getInstance()->get(\Magento\Catalog\Model\Layer\Resolver::class);
$filterList = ObjectManager::getInstance()->create(
\Magento\Catalog\Model\Layer\FilterList::class,
    [
        'filterableAttributes' => $filterableAttributes
    ]
);

$category = 1234;

$appState->setAreaCode('frontend');
$layer = $layerResolver->get();
$layer->setCurrentCategory($category);
$filters = $filterList->getFilters($layer);

However, this is not the final result. To be sure that filters are actual, it is required to check number of items for each filters. (that check is actually performed during core layered navigation rendering)

$finalFilters = [];
foreach ($filters as $filter) {
    if ($filter->getItemsCount()) {
        $finalFilters[] = $filter;
    }
}

Then you can get filter names and values. ie:

$name = $filter->getName();
foreach ($filter->getItems() as $item) {
    $value = $item->getValue();
}

Finally, I would like to add alternative solution, that is a bit brutal, thought :)

$categoryId = 1234;

$resource = ObjectManager::getInstance()->get(\Magento\Framework\App\ResourceConnection::class);
$connection = $resource->getConnection();

$select = $connection->select()->from(['ea' => $connection->getTableName('eav_attribute')], 'ea.attribute_id')
->join(['eea' => $connection->getTableName('eav_entity_attribute')], 'ea.attribute_id = eea.attribute_id')
->join(['cea' => $connection->getTableName('catalog_eav_attribute')], 'ea.attribute_id = cea.attribute_id')
->join(['cpe' => $connection->getTableName('catalog_product_entity')], 'eea.attribute_set_id = cpe.attribute_set_id')
->join(['ccp' => $connection->getTableName('catalog_category_product')], 'cpe.entity_id = ccp.product_id')
->where('cea.is_filterable = ?', 1)
->where('ccp.category_id = ?', $categoryId)
->group('ea.attribute_id');

$attributeIds = $connection->fetchCol($select);

Then it is possible to use attribute ids to load collection.

 /** @var $collection \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection */
$collection = $this->collectionFactory->create();
$collection->setItemObjectClass('Magento\Catalog\Model\ResourceModel\Eav\Attribute')
        ->addStoreLabel($this->storeManager->getStore()->getId());
$collection->addFieldToFilter('attribute_id', ['in' => $attributeIds]);
0
Cotton On

If you know how to build module then you can take help from 'FiltersProvider.php' from 'module-catalog-graph-ql\Model\Resolver\Layer'.

use Magento\Catalog\Model\Layer\Category\FilterableAttributeList as CategoryFilterableAttributeList;
use Magento\Catalog\Model\Layer\FilterListFactory;
use Magento\Catalog\Model\Layer\Resolver;
use Magento\Framework\UrlInterface;

public function __construct(
    Resolver $layerResolver,
    FilterListFactory $filterListFactory,
    CategoryFilterableAttributeList $categoryFilterableAttributeList,
    UrlInterface $urlBuilder
) {
    $this->_navigation = $navigation;
    $this->layerResolver = $layerResolver;
    $this->filterListFactory = $filterListFactory;
    $this->urlBuilder = $urlBuilder;
    $this->_categoryFilterableAttributeList = $categoryFilterableAttributeList;
}
public function getCatMenu($catid)
{
    $fill_arr = [];

    $filterList = $this->filterListFactory->create(['filterableAttributes' => $this->_categoryFilterableAttributeList]);

    $layer = clone $this->layerResolver->get();
    $layer->setCurrentCategory($catid);
    $filters =  $filterList->getFilters($layer);

    return $fill_arr;
}