Introduction
Sometime we need to load Category List
while developing something for Magento 2.
And there are a few ways to do it.
But in this small chapter we will use the right way.
Solution
The right way to load Category List
includes two things:
-
use
CategoryListInterface
to get the list of categories -
use
Search Criteria
to build a query
Here is the basic code to load Category List
. This code will load All Categories
, becuase we are using empty Search Criteria
.
<?php
declare(strict_types=1);
namespace Vendor\ModuleName\ViewModel;
use Magento\Catalog\Api\CategoryListInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
class SomeClass
{
/**
* @var CategoryListInterface
*/
private CategoryListInterface $categoryList;
/**
* @var SearchCriteriaBuilder
*/
private SearchCriteriaBuilder $searchCriteriaBuilder;
/**
* @apram CategoryListInterface $categoryList
* @param SearchCriteriaBuilder $searchCriteriaBuilder
*/
public function __construct(
CategoryListInterface $categoryList,
SearchCriteriaBuilder $searchCriteriaBuilder
)
{
$this->categoryList = $categoryList;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
}
public function apply()
{
$searchCriteria = $this->searchCriteriaBuilder->create();
$categoryList = $this->categoryList->getList($searchCriteria);
foreach ($categoryList->getItems() as $category) {
/**
* Here you can do with the $category
* the things you want.
*
* For example:
*
* if (!$category->getIsActive()) {
* continue;
* }
*
* Or
*
* $category->setStoreId(0);
* $category->setAvailableSortBy(['price']);
*
*/
}
}
}
Load Category List filtered by params
To do it we should use a few new components like:
- FilterBuilder
- SortOrderBuilder
Let’s look at the code and then I will explain what happens here.
<?php
declare(strict_types=1);
namespace Vendor\ModuleName\ViewModel;
use Magento\Catalog\Api\CategoryListInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\SortOrderBuilder;
class SomeClass
{
/**
* @var CategoryListInterface
*/
private CategoryListInterface $categoryList;
/**
* @var SearchCriteriaBuilder
*/
private SearchCriteriaBuilder $searchCriteriaBuilder;
/**
* @var FilterBuilder
*/
private FilterBuilder $filterBuilder;
/**
* @var SortOrderBuilder
*/
private SortOrderBuilder $sortOrderBuilder;
/**
* @apram CategoryListInterface $categoryList
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @param FilterBuilder $filterBuilder
* @param SortOrderBuilder $sortOrderBuilder
*/
public function __construct(
CategoryListInterface $categoryList,
SearchCriteriaBuilder $searchCriteriaBuilder,
FilterBuilder $filterBuilder,
SortOrderBuilder $sortOrderBuilder
)
{
$this->categoryList = $categoryList;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->filterBuilder = $filterBuilder;
$this->sortOrderBuilder = $sortOrderBuilder;
}
public function apply()
{
$filter1 = $this->filterBuilder->setField('level')
->setValue(3)
->setConditionType('eq')
->create();
$filter2 = $this->filterBuilder->setField('children_count')
->setValue(200)
->setConditionType('gt')
->create();
$sortOrder = $this->sortOrderBuilder->setField('entity_id')
->setDirection('DESC')
->create();
$this->searchCriteriaBuilder->addFilters([$filter1, $filter2]);
$this->searchCriteriaBuilder->addSortOrder($sortOrder);
$searchCriteria = $this->searchCriteriaBuilder->create();
$categoryList = $this->categoryList->getList($searchCriteria);
foreach ($categoryList->getItems() as $category) {
/**
* Here you can do with the $category
* the things you want.
*
* For example:
*
* if (!$category->getIsActive()) {
* continue;
* }
*
* Or
*
* $category->setStoreId(0);
* $category->setAvailableSortBy(['price']);
*
*/
}
}
}
As you can see this code is similar to the basic code in the Solution
section.
The only filters
and sort order
was added.
I think the $filter
’s variables are clear to you. Buuut… let’s take a look at $filter1
, for example.
- First, we set
field name
by which we want to filter on.
->setField('level')
- Then we set the
value
of the field by which we want to filter.
->setValue(3)
- And last step is to add
condition type
->setConditionType('eq')
This filter reads as: “I want to select Categories
with the level
equals (eq
) to the 3
.”
Then we add sort order
by the entity_id
field in the DESC
direction.
$sortOrder = $this->sortOrderBuilder->setField('entity_id')
->setDirection('DESC')
->create();
****
$this->searchCriteriaBuilder->addSortOrder($sortOrder);
It was simple and clear things. But …
The most interesting thing is this line of code:
$this->searchCriteriaBuilder->addFilters([$filter1, $filter2]);
If you navigate to the \Magento\Catalog\Model\CategoryList::getList
method and check what query is generated to get list of categories, you will see something like
SELECT `e`.* FROM `catalog_category_entity` AS `e`
WHERE ((`e`.`level` = 3) OR (`e`.`children_count` > 200))
ORDER BY `e`.`entity_id` DESC
As you can see in the WHERE
part our filters are generated using OR
statement.
So, if you put array of Filters
into the addFilters
method - all of these filters will be combined with OR
statement.
But if you want to use AND
statement, you should use addFilter
method of SearchCriteriaBuilder
object.
In our example we will replace the line:
$this->searchCriteriaBuilder->addFilters([$filter1, $filter2]);
with
$this->searchCriteriaBuilder->addFilter([$filter1]);
$this->searchCriteriaBuilder->addFilter([$filter2]);
And now the sql query would be:
SELECT `e`.* FROM `catalog_category_entity` AS `e`
WHERE ((`e`.`level` = 3)) AND ((`e`.`children_count` > 200))
ORDER BY `e`.`entity_id` DESC
Also you can use addFilter
method of SearchCriteriaBuilder
object to get WHERE
clause with the OR
statement.
Read more about Search Criteria
and how to configure it in the official Magento page.