Magento 2 : Add category filter in producy grid
سؤال
In admin, I want to add column of category and also add category filter in product grid.
How to do this?
Please help me.
Thanks.
المحلول
To add category column in product grid, add this below code in your
Use default Magento class for complete category tree:
Magento\Catalog\Ui\Component\Product\Form\Categories\Options
For any custom category list use the custom class as below:
Vendor\Module\Model\Category\CategoryList
Reference : Add Category Filter to Product Grid in Magento 2 Admin
product_listing.xml file :
<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<listingToolbar name="listing_top">
<filters name="listing_filters">
<filterSelect name="category_id" provider="${ $.parentName }" component="Magento_Ui/js/form/element/ui-select" template="ui/grid/filters/elements/ui-select">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filterOptions" xsi:type="boolean">true</item>
<item name="levelsVisibility" xsi:type="number">1</item>
</item>
</argument>
<settings>
<options class="Magento\Catalog\Ui\Component\Product\Form\Categories\Options"/>
<caption translate="true">– Please Select a Category –</caption>
<label translate="true">Categories</label>
<dataScope>category_id</dataScope>
<imports>
<link name="visible">componentType = column, index = ${ $.index }:visible</link>
</imports>
</settings>
</filterSelect>
</filters>
</listingToolbar>
<columns name="product_columns" class="Magento\Catalog\Ui\Component\Listing\Columns">
<column name="category_id" class="Vendor\Module\Ui\Component\Listing\Column\Category">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="label" xsi:type="string" translate="true">Categories</item>
<item name="sortOrder" xsi:type="number">35</item>
</item>
</argument>
</column>
</columns>
</listing>
Now, we need to override prepareDataSource() method. To get category values in the prepareDataSource() method like below code :
Vendor\Module\Ui\Component\Listing\Column\Category.php
<?php
namespace Vendor\Module\Ui\Component\Listing\Column;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
class Category extends \Magento\Ui\Component\Listing\Columns\Column
{
/**
* @var \Magento\Catalog\Model\ProductFactory
*/
private $productFactory;
/**
* @var \Magento\Catalog\Model\CategoryFactory
*/
private $categoryFactory;
/**
* [__construct description]
* @param ContextInterface $context [description]
* @param UiComponentFactory $uiComponentFactory [description]
* @param array $components [description]
* @param array $data [description]
* @param \Magento\Catalog\Model\ProductFactory $productFactory [description]
* @param \Magento\Catalog\Model\CategoryFactory $categoryFactory [description]
*/
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
array $components = [],
array $data = [],
\Magento\Catalog\Model\ProductFactory $productFactory,
\Magento\Catalog\Model\CategoryFactory $categoryFactory
) {
parent::__construct($context, $uiComponentFactory, $components, $data);
$this->productFactory = $productFactory;
$this->categoryFactory = $categoryFactory;
}
/**
* Prepare date for category column
* @param array $dataSource [description]
* @return array
*/
public function prepareDataSource(array $dataSource)
{
$fieldName = $this->getData('name');
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as &$item) {
$productId = $item['entity_id'];
$product = $this->productFactory->create()->load($productId);
$categoryIds = $product->getCategoryIds();
$categories = [];
if (count($categoryIds)) {
foreach ($categoryIds as $categoryId) {
$categoryData = $this->categoryFactory->create()->load($categoryId);
$categories[] = $categoryData->getName();
}
}
$item[$fieldName] = implode(',', $categories);
}
}
return $dataSource;
}
}
create di.xml file for override class :
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Ui\DataProvider\Product\ProductDataProvider" type="Vendor\Module\Ui\DataProvider\Product\ProductDataProvider" />
</config>
Now, add method addFilter() to Vendor\Module\Ui\DataProvider\Product\ProductDataProvider.php
<?php
namespace Vendor\Module\Ui\DataProvider\Product;
class ProductDataProvider extends \Magento\Catalog\Ui\DataProvider\Product\ProductDataProvider
{
/**
* For filter grid according to category
* @param \Magento\Framework\Api\Filter $filter
*/
public function addFilter(\Magento\Framework\Api\Filter $filter)
{
if ($filter->getField() == 'category_id') {
$this->getCollection()->addCategoriesFilter(['in' => $filter->getValue()]);
} elseif (isset($this->addFilterStrategies[$filter->getField()])) {
$this->addFilterStrategies[$filter->getField()]
->addFilter(
$this->getCollection(),
$filter->getField(),
[$filter->getConditionType() => $filter->getValue()]
);
} else {
parent::addFilter($filter);
}
}
}
And last to add Options to the category filter dropdown :
Vendor\Module\Model\Category\CategoryList.php
<?php
namespace Vendor\Module\Model\Category;
use Magento\Catalog\Model\Category as CategoryModel;
class CategoryList implements \Magento\Framework\Option\ArrayInterface
{
/**
* @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory
*/
private $categoryCollectionFactory;
/**
* [__construct description]
* @param \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $collectionFactory [description]
*/
public function __construct(
\Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $collectionFactory
) {
$this->categoryCollectionFactory = $collectionFactory;
}
/**
* Get list of categories
* @return array
*/
public function toOptionArray()
{
$collection = $this->categoryCollectionFactory->create();
$collection->addAttributeToSelect(['name', 'is_active', 'parent_id']);
$categoryById = [
CategoryModel::TREE_ROOT_ID => [
'value' => CategoryModel::TREE_ROOT_ID,
'optgroup' => null,
],
];
foreach ($collection as $category) {
foreach ([$category->getId(), $category->getParentId()] as $categoryId) {
if (!isset($categoryById[$categoryId])) {
$categoryById[$categoryId] = ['value' => $categoryId];
}
}
$categoryById[$category->getId()]['is_active'] = $category->getIsActive();
$categoryById[$category->getId()]['label'] = $category->getName();
$categoryById[$category->getParentId()]['optgroup'][] = &$categoryById[$category->getId()];
}
return $categoryById[CategoryModel::TREE_ROOT_ID]['optgroup'];
}
}
Hope, It will helpful for you.