Edit in GitHubLog an issue

Searching with repositories

Repositories give service requestors the ability to perform create, read, update, and delete (CRUD) operations on entities or a list of entities. A repository is an example of a service contract, and its implementation is part of the domain layer.

Repository state#

A repository should be stateless after instantiation. This means that every method call should not rely on previous calls nor should it affect later method calls. Any field contained in the repository class must also be stateless.

If your repository needs to provide functionality that requires state, such as for caching, use the registry pattern. A good example that uses this pattern is the CustomerRepository class.

Search criteria#

A Search Criteria is an implementation of the SearchCriteriaInterface class that allows you to build custom requests with different conditions.

Repositories use this class to retrieve entities based on a matching criteria.

Filter#

The Filter class is the smallest part of a Search Criteria. It allows you to add a custom field, value, and condition type to the criteria.

Example of how to define a Filter:

Copied to your clipboard
1$filter
2 ->setField("url")
3 ->setValue("%magento.com")
4 ->setConditionType("like");

This filter will find all urls with the suffix of "magento.com".

Filter group#

The FilterGroup class acts like a collection of Filters that apply one or more criteria to a search.

The boolean OR statement joins Filters inside a single Filter Group. The boolean AND statement joins Filter Groups inside a Search Criteria.

For example:

Copied to your clipboard
1$filter1
2 ->setField("url")
3 ->setValue("%magento.com")
4 ->setConditionType("like");
5
6$filter2
7 ->setField("store_id")
8 ->setValue("1")
9 ->setConditionType("eq");
10
11$filterGroup1->setFilters([$filter1, $filter2]);
12
13$filter3
14 ->setField("url_type")
15 ->setValue(1)
16 ->setConditionType("eq");
17
18$filterGroup2->setFilters([$filter3]);
19
20$searchCriteria->setFilterGroups([$filterGroup1, $filterGroup2]);

The code above creates a Search Criteria with the Filters put together in the following way: (url like %magento.com OR store_id eq 1) AND (url_type eq 1)

Sorting#

To apply sorting to the Search Criteria, use the SortOrder class.

Field and direction make up the two parameters that define a Sort Order object. The field is the name of the field to sort. The direction is the method of sorting whose value can be ASC or DESC.

The example below defines a Sort Order object that will sort the customer email in ascending order:

Copied to your clipboard
1$sortOrder
2 ->setField("email")
3 ->setDirection("ASC");
4
5$searchCriteria->setSortOrders([$sortOrder]);

Pagination#

The setPageSize function paginates the Search Criteria by limiting the amount of entities it retrieves:

Copied to your clipboard
$searchCriteria->setPageSize(20); //retrieve 20 or less entities

The setCurrentPage function sets the current page:

Copied to your clipboard
1$searchCriteria
2 ->setPageSize(20)
3 ->setCurrentPage(2); //show the 21st to 40th entity

Search result#

The getList(SearchCriteria $searchCriteria) method defined in your repository should return a Search Result object. This object is an instance of a class that implements the interface SearchResultInterface.

Search Result objects hold the Search Criteria object and the retrieved entities along with information about the total count of found entities regardless of any limitations set in the criteria.

The search engine determines the maximum number of results that a query can return. For Elasticsearch, the default value of 10000 is defined in the module's etc/di.xml file.

The example below uses getList with the ProductRepositoryInterface. We use the FilterBuilder and the SearchCriteriaBuilder to avoid shared instances.

Copied to your clipboard
1$filter = $this->filterBuilder
2 ->setField(ProductInterface::NAME)
3 ->setConditionType('like')
4 ->setValue('%hoodie%')
5 ->create();
6
7$this->searchCriteriaBuilder->addFilters([$filter]);
8$this->searchCriteriaBuilder->setPageSize(20);
9
10$searchCriteria = $this->searchCriteriaBuilder->create();
11$productsItems = $this->productRepository->getList($searchCriteria)->getItems();

Search criteria unify processing#

A Collection Processor is an implementation of the CollectionProcessorInterface interface that unifies the application of custom filters, sorting, and paginating. It contains a one method process that applies a Search Criteria object to an abstract database collection.

You can use virtual typing in your di.xml file to specify the processors used in the Collection Processor.

Filter processor#

The FilterProcessor class applies Filter Groups and their filters to a collection.

Below is the code that applies filters to a collection. The method applies custom filters for some fields, otherwise it applies $collection->addFieldToFilter($fields, $conditions).

Copied to your clipboard
1/**
2 * Add FilterGroup to the collection
3 *
4 * @param FilterGroup $filterGroup
5 * @param AbstractDb $collection
6 */
7private function addFilterGroupToCollection(
8 FilterGroup $filterGroup,
9 AbstractDb $collection
10) {
11 $fields = [];
12 $conditions = [];
13 foreach ($filterGroup->getFilters() as $filter) {
14 $isApplied = false;
15 $customFilter = $this->getCustomFilterForField($filter->getField());
16 if ($customFilter) {
17 $isApplied = $customFilter->apply($filter, $collection);
18 }
19
20 if (!$isApplied) {
21 $condition = $filter->getConditionType() ? $filter->getConditionType() : 'eq';
22 $fields[] = $this->getFieldMapping($filter->getField());
23 $conditions[] = [$condition => $filter->getValue()];
24 }
25 }
26
27 if ($fields) {
28 $collection->addFieldToFilter($fields, $conditions);
29 }
30}

You can configure this class to use a specific custom field mapping and custom filter in the di.xml file. The example below uses dependency injection to create a virtual type from a Filter Processor that applies the module-specific ProductCategoryFilter on a particular field mapping.

Copied to your clipboard
1 <virtualType name="Magento\Customer\Model\Api\SearchCriteria\CollectionProcessor\GroupFilterProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor">
2 <arguments>
3 <argument name="customFilters" xsi:type="array">
4 <item name="category_id" xsi:type="object">Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\ProductCategoryFilter</item>
5 </argument>
6 <argument name="fieldMapping" xsi:type="array">
7 <item name="code" xsi:type="string">main_table.customer_group_code</item>
8 <item name="id" xsi:type="string">main_table.customer_group_id</item>
9 <item name="tax_class_name" xsi:type="string">tax_class_table.class_name</item>
10 </argument>
11 </arguments>
12 </virtualType>
Copied to your clipboard
1namespace Magento\Catalog\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor;
2
3use Magento\Catalog\Model\ResourceModel\Product\Collection;
4use Magento\Framework\Api\Filter;
5use Magento\Framework\Api\SearchCriteria\CollectionProcessor\FilterProcessor\CustomFilterInterface;
6use Magento\Framework\Data\Collection\AbstractDb;
7
8class ProductCategoryFilter implements CustomFilterInterface
9{
10 /**
11 * Apply category_id Filter to Product Collection
12 *
13 * @param Filter $filter
14 * @param AbstractDb $collection
15 * @return bool Whether the filter is applied
16 */
17 public function apply(Filter $filter, AbstractDb $collection)
18 {
19 $value = $filter->getValue();
20 $conditionType = $filter->getConditionType() ?: 'in';
21 $filterValue = [$value];
22 if (($conditionType === 'in' || $conditionType === 'nin') && is_string($value)) {
23 $filterValue = explode(',', $value);
24 }
25 $categoryFilter = [$conditionType => $filterValue];
26
27 /** @var Collection $collection */
28 $collection->addCategoriesFilter($categoryFilter);
29
30 return true;
31 }
32}
ArgumentDescription
customFiltersAn array of filters implementing the CustomFilterInterface. These filters allow you to apply custom logic to a particular abstract database collection.
fieldMappingMaps field names defined in the search Criteria to the names in an abstract database collection

Sorting processor#

The SortingProcessor class applies the sorting order of a search criteria to an abstract database collection.

Below is an example of how you can configure a Sorting Processor virtual type in the di.xml file to use a custom field mapping and default sorting orders.

Copied to your clipboard
1<virtualType name="Magento\Customer\Model\Api\SearchCriteria\CollectionProcessor\GroupSortingProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\SortingProcessor">
2 <arguments>
3 <argument name="fieldMapping" xsi:type="array">
4 <item name="code" xsi:type="string">customer_group_code</item>
5 <item name="id" xsi:type="string">customer_group_id</item>
6 <item name="tax_class_name" xsi:type="string">class_name</item>
7 </argument>
8 <argument name="defaultOrders" xsi:type="array">
9 <item name="id" xsi:type="string">ASC</item>
10 </argument>
11 </arguments>
12</virtualType>
ArgumentDescription
fieldMappingMaps field names defined in the search Criteria to the names in an abstract database collection
defaultOrdersThe ordering applied when there are none defined in a search criteria.

Pagination processor#

The PaginationProcessor class applies the current page and page size of the search criteria to an abstract database collection.

Join processor#

The JoinProcessor class allows you to join fields from other tables into an abstract database collection. To join a table, implement Magento\Framework\Api\SearchCriteria\CollectionProcessor\JoinProcessor\CustomJoinInterface::apply(AbstractDb $collection). Inside the class, use the $collection->join(…) method.

Below is an example of creating a Join Processor: The virtual type in the di.xml class named Magento\Tax\Model\Api\SearchCriteria\CollectionProcessor\TaxRuleJoinProcessor:

Copied to your clipboard
1<virtualType name="Magento\Tax\Model\Api\SearchCriteria\CollectionProcessor\TaxRuleJoinProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\JoinProcessor">
2 <arguments>
3 <argument name="customJoins" xsi:type="array">
4 <item name="rate.tax_calculation_rate_id" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\JoinProcessor\Rate</item>
5 <item name="rc.code" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\JoinProcessor\RateCode</item>
6 <item name="ctc.customer_tax_class_id" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\JoinProcessor\CustomerTaxClass</item>
7 <item name="ptc.product_tax_class_id" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\JoinProcessor\ProductTaxClass</item>
8 <item name="cd.customer_tax_class_id" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\JoinProcessor\CalculationData</item>
9 <item name="cd.product_tax_class_id" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\JoinProcessor\CalculationData</item>
10 </argument>
11 <argument name="fieldMapping" xsi:type="array">
12 <item name="id" xsi:type="string">tax_calculation_rule_id</item>
13 <item name="tax_rate_ids" xsi:type="string">tax_calculation_rate_id</item>
14 <item name="customer_tax_class_ids" xsi:type="string">cd.customer_tax_class_id</item>
15 <item name="product_tax_class_ids" xsi:type="string">cd.product_tax_class_id</item>
16 <item name="tax_calculation_rate_id" xsi:type="string">rate.tax_calculation_rate_id</item>
17 </argument>
18 </arguments>
19</virtualType>

The Join Processor aggregates the Custom Joins objects, implementing the interface CustomJoinInterface. Below is Magento\Tax\Model\Api\SearchCriteria\JoinProcessor\Rate as a Custom Join:

Copied to your clipboard
1namespace Magento\Tax\Model\Api\SearchCriteria\JoinProcessor;
2
3use Magento\Framework\Api\SearchCriteria\CollectionProcessor\JoinProcessor\CustomJoinInterface;
4use Magento\Framework\Data\Collection\AbstractDb;
5
6/**
7 * Class Rate
8 * @package Magento\Tax\Model\Api\SearchCriteria\JoinProcessor
9 **/
10class Rate implements CustomJoinInterface
11{
12 /**
13 * @param \Magento\Tax\Model\ResourceModel\Calculation\Rule\Collection $collection
14 * @return true
15 **/
16 public function apply(AbstractDb $collection)
17 {
18 $collection->joinCalculationData('rate');
19 return true;
20 }
21}

In the apply method the object calls joinCalculationData method of Magento\Tax\Model\ResourceModel\Calculation\Rule\Collection class.

Copied to your clipboard
1/**
2 * Join calculation data to result
3 *
4 * @param string $alias table alias
5 * @return \Magento\Tax\Model\ResourceModel\Calculation\Rule\Collection
6 */
7public function joinCalculationData($alias)
8{
9 $this->getSelect()->joinLeft(
10 [$alias => $this->getTable('tax_calculation')],
11 "main_table.tax_calculation_rule_id = {$alias}.tax_calculation_rule_id",
12 []
13 );
14 $this->getSelect()->group('main_table.tax_calculation_rule_id');
15
16 return $this;
17}

The rate is alias of table, the alias is used in the Join. In this case the joinCalculationData(...) is LEFT JOIN on tax_calculation_rule_id and group by tax_calculation_rule_id

The other case Magento\Tax\Model\Api\SearchCriteria\JoinProcessor\RateCode class provides additional LEFT JOIN except joinCalculationData

Copied to your clipboard
1/**
2 * @param AbstractDb $collection
3 * @return true
4 */
5public function apply(AbstractDb $collection)
6{
7 $taxCalculationTableAlias = 'tc';
8
9 $collection->joinCalculationData($taxCalculationTableAlias);
10
11 $collection->getSelect()->joinLeft(
12 ['rc' => $collection->getTable('tax_calculation_rate')],
13 "{$taxCalculationTableAlias}.tax_calculation_rate_id = rc.tax_calculation_rate_id",
14 []
15 );
16
17 return true;
18}

As result the processors will be used in the Magento\Tax\Model\TaxRuleRepository:

Copied to your clipboard
1<type name="Magento\Tax\Model\TaxRuleRepository">
2 <arguments>
3 <argument name="collectionProcessor" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\TaxRuleCollectionProcessor</argument>
4 </arguments>
5</type>

The Magento\Tax\Model\Api\SearchCriteria\TaxRuleCollectionProcessor:

Copied to your clipboard
1<virtualType name="Magento\Tax\Model\Api\SearchCriteria\TaxRuleCollectionProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor">
2 <arguments>
3 <argument name="processors" xsi:type="array">
4 <item name="joins" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\CollectionProcessor\TaxRuleJoinProcessor</item>
5 <item name="filters" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\CollectionProcessor\TaxRuleFilterProcessor</item>
6 <item name="sorting" xsi:type="object">Magento\Tax\Model\Api\SearchCriteria\CollectionProcessor\TaxRuleSortingProcessor</item>
7 <item name="pagination" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\PaginationProcessor</item>
8 </argument>
9 </arguments>
10</virtualType>

Using collection processors in repositories#

Below is an example of how the CustomerRepositoryInterface repository class uses a Collection Processor.

Copied to your clipboard
1 namespace Magento\Customer\Model\ResourceModel;
2 ...
3 /**
4 * Customer repository.
5 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
6 **/
7 class CustomerRepository implements \Magento\Customer\Api\CustomerRepositoryInterface
8 {
9 ...
10 /**
11 * @var \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface
12 **/
13 private $collectionProcessor;
14 ...
15 public function __construct(
16 ...
17 CollectionProcessorInterface $collectionProcessor = null
18 ) {
19 ...
20 $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor();
21 }
22 ...
23 /**
24 * {@inheritdoc}
25 **/
26 public function getList(SearchCriteriaInterface $searchCriteria)
27 {
28 ...
29 $this->getCollectionProcessor()->process($searchCriteria, $collection);
30 ...
31 }
32 ...
33 /**
34 * Retrieve collection processor
35 *
36 * @deprecated
37 * @return \Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface
38 **/
39 private function getCollectionProcessor()
40 {
41 if (!$this->collectionProcessor) {
42 $this->collectionProcessor = \Magento\Framework\App\ObjectManager::getInstance()->get(
43 'Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor'
44 );
45 }
46 return $this->collectionProcessor;
47 }
48 }

The di.xml configuration file excerpt below shows how you can create a virtual type for the Collection Processor by passing in a custom Filter Processor and a custom Sorting Processor.

Copied to your clipboard
1 <virtualType name="Magento\Customer\Model\Api\SearchCriteria\CollectionProcessor\CustomerFilterProcessor" type="Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor">
2 <arguments>
3 <argument name="customFilters" xsi:type="array">
4 <item name="category_id" xsi:type="object">Magento\Customer\Model\Api\SearchCriteria\CollectionProcessor\FilterProcessor\CustomerCategoryFilter</item>
5 </argument>
6 <argument name="fieldMapping" xsi:type="array">
7 <item name="code" xsi:type="string">customer_group_code</item>
8 <item name="id" xsi:type="string">customer_group_id</item>
9 <item name="tax_class_name" xsi:type="string">class_name</item>
10 </argument>
11 </arguments>
12 </virtualType>
13 <virtualType name="Magento\Customer\Model\Api\SearchCriteria\CollectionProcessor\CustomerSortingProcessor" type="Magento\Framework\Api\SearchCriteria\CollectionProcessor\SortingProcessor">
14 <arguments>
15 <argument name="fieldMapping" xsi:type="array">
16 <item name="code" xsi:type="string">customer_group_code</item>
17 <item name="id" xsi:type="string">customer_group_id</item>
18 <item name="tax_class_name" xsi:type="string">class_name</item>
19 </argument>
20 <argument name="defaultOrders" xsi:type="array">
21 <item name="id" xsi:type="string">ASC</item>
22 </argument>
23 </arguments>
24 </virtualType>
25 <virtualType name="Magento\Customer\Model\Api\SearchCriteria\CustomerCollectionProcessor" type="Magento\Eav\Model\Api\SearchCriteria\CollectionProcessor">
26 <arguments>
27 <argument name="processors" xsi:type="array">
28 <item name="filters" xsi:type="object">Magento\Customer\Model\Api\SearchCriteria\CollectionProcessor\CustomerFilterProcessor</item>
29 <item name="sorting" xsi:type="object">Magento\Customer\Model\Api\SearchCriteria\CollectionProcessor\CustomerSortingProcessor</item>
30 <item name="pagination" xsi:type="object">Magento\Framework\Api\SearchCriteria\CollectionProcessor\PaginationProcessor</item>
31 </argument>
32 </arguments>
33 </virtualType>
34 <type name="Magento\Customer\Model\ResourceModel\CustomerRepository">
35 <arguments>
36 <argument name="collectionProcessor" xsi:type="object">Magento\Customer\Model\Api\SearchCriteria\CustomerCollectionProcessor</argument>
37 </arguments>
38 </type>
Was this helpful?
  • Privacy
  • Terms of Use
  • Do not sell my personal information
  • AdChoices
Copyright © 2022 Adobe. All rights reserved.