Skip to content
intermediate Phase 35 · Models

Repository Pattern

CRUD via repositories, SearchCriteria, search results, and extension attributes.

45m
0 problems
Topic Progress 0%

Repository Interface and Implementation

Repositories provide a clean API for CRUD operations.

Repository interface:

<?php
namespace Vendor\Blog\Api;

use Magento\Framework\Api\SearchCriteriaInterface;
use Vendor\Blog\Api\Data\PostInterface;

interface PostRepositoryInterface
{
    public function get($id): PostInterface;
    public function getList(SearchCriteriaInterface $searchCriteria):
        \Magento\Framework\Api\SearchResultsInterface;
    public function save(PostInterface $post): PostInterface;
    public function delete(PostInterface $post): bool;
    public function deleteById($id): bool;
}

Repository implementation:

<?php
namespace Vendor\Blog\Model;

use Magento\Framework\Api\SearchCriteria\SearchCriteriaFactory;
use Magento\Framework\Api\SearchResultsFactory;
use Vendor\Blog\Api\Data\PostInterface;
use Vendor\Blog\Api\Data\PostInterfaceFactory;
use Vendor\Blog\Api\PostRepositoryInterface;
use Vendor\Blog\Model\ResourceModel\Post as PostResource;
use Vendor\Blog\Model\ResourceModel\Post\CollectionFactory;

class PostRepository implements PostRepositoryInterface
{
    public function __construct(
        private PostResource $resource,
        private PostInterfaceFactory $postFactory,
        private CollectionFactory $collectionFactory,
        private SearchResultsFactory $searchResultsFactory,
        private SearchCriteriaFactory $searchCriteriaFactory
    ) {}
    
    public function get($id): PostInterface
    {
        $post = $this->postFactory->create();
        $this->resource->load($post, $id);
        
        if (!$post->getId()) {
            throw new \Magento\Framework\Exception\NoSuchEntityException(
                __('Post with id %1 does not exist', $id)
            );
        }
        
        return $post;
    }
    
    public function save(PostInterface $post): PostInterface
    {
        $this->resource->save($post);
        return $post;
    }
    
    public function delete(PostInterface $post): bool
    {
        $this->resource->delete($post);
        return true;
    }
    
    public function deleteById($id): bool
    {
        return $this->delete($this->get($id));
    }
}

SearchCriteria for Complex Queries

SearchCriteria provides a flexible way to build queries.

Basic SearchCriteria:

$searchCriteria = $this->searchCriteriaFactory->create();

// Add filter
$searchCriteria->setFilterGroups([
    $this->filterGroupFactory->create()->setFilters([
        $this->filterFactory->create()
            ->setField('status')
            ->setConditionType('eq')
            ->setValue(1)
    ])
]);

// Add sort order
$searchCriteria->setSortOrders([
    $this->sortOrderFactory->create()
        ->setField('created_at')
        ->setDirection('DESC')
]);

// Add pagination
$searchCriteria->setPageSize(20)->setCurrentPage(1);

// Get list
$results = $this->postRepository->getList($searchCriteria);

Multiple filters (OR):

$searchCriteria->setFilterGroups([
    $this->filterGroupFactory->create()->setFilters([
        $this->filterFactory->create()->setField('status')->setValue(1),
        $this->filterFactory->create()->setField('featured')->setValue(1)
    ])
]);

Multiple filter groups (AND):

$searchCriteria->setFilterGroups([
    // Group 1: status = 1
    $this->filterGroupFactory->create()->setFilters([
        $this->filterFactory->create()->setField('status')->setValue(1)
    ]),
    // Group 2: category_id = 5
    $this->filterGroupFactory->create()->setFilters([
        $this->filterFactory->create()->setField('category_id')->setValue(5)
    ])
]);

Search Results

Search results contain the query results and pagination info.

Search results:

$results = $this->postRepository->getList($searchCriteria);

// Get items
$posts = $results->getItems();

// Get total count
$total = $results->getTotalCount();

// Pagination info
$page = $searchCriteria->getCurrentPage();
$pageSize = $searchCriteria->getPageSize();

Search results implementation:

public function getList(SearchCriteriaInterface $searchCriteria):
    \Magento\Framework\Api\SearchResultsInterface
{
    $collection = $this->collectionFactory->create();
    
    // Apply search criteria to collection
    $this->addFilterGroupToCollection($searchCriteria, $collection);
    $this->addSortOrderToCollection($searchCriteria, $collection);
    $this->addPaginationToCollection($searchCriteria, $collection);
    
    $collection->load();
    
    $searchResults = $this->searchResultsFactory->create();
    $searchResults->setSearchCriteria($searchCriteria);
    $searchResults->setItems($collection->getItems());
    $searchResults->setTotalCount($collection->getSize());
    
    return $searchResults;
}

private function addFilterGroupToCollection(
    SearchCriteriaInterface $searchCriteria,
    \Magento\Framework\Data\Collection $collection
): void {
    foreach ($searchCriteria->getFilterGroups() as $filterGroup) {
        $conditions = [];
        
        foreach ($filterGroup->getFilters() as $filter) {
            $conditions[] = $filter->getCondition();
        }
        
        $collection->addFieldToFilter($conditions);
    }
}

Extension Attributes

Extension attributes add custom data without modifying interfaces.

Extension interface:

<?php
namespace Vendor\Blog\Api\Data;

interface PostExtensionAttributesInterface
{
    public function getAuthorName(): ?string;
    public function setAuthorName(string $authorName): self;
    public function getTags(): ?array;
    public function setTags(array $tags): self;
}

Extension attributes in model:

<?php
namespace Vendor\Blog\Model;

class Post extends \Magento\Framework\Model\AbstractModel implements PostInterface
{
    public function getExtensionAttributes(): ?PostExtensionAttributesInterface
    {
        return $this->getData('extension_attributes');
    }
    
    public function setExtensionAttributes(
        PostExtensionAttributesInterface $extensionAttributes
    ): PostInterface {
        return $this->setData('extension_attributes', $extensionAttributes);
    }
}

Populating extension attributes:

public function get($id): PostInterface
{
    $post = $this->loadPost($id);
    
    // Populate extension attributes
    $extensionAttributes = $post->getExtensionAttributes() ?? $this->extensionFactory->create();
    
    $author = $this->authorRepository->get($post->getAuthorId());
    $extensionAttributes->setAuthorName($author->getName());
    
    $tags = $this->tagRepository->getByPostId($post->getId());
    $extensionAttributes->setTags($tags);
    
    $post->setExtensionAttributes($extensionAttributes);
    
    return $post;
}

Using extension attributes:

$post = $this->postRepository->get(1);

// Access extension attributes
$authorName = $post->getExtensionAttributes()->getAuthorName();
$tags = $post->getExtensionAttributes()->getTags();

Quiz

1. What does the repository pattern provide?

Question 1 options

2. What is SearchCriteria used for?

Question 2 options

3. How do extension attributes differ from custom attributes?

Question 3 options

4. What method returns total count from search results?

Question 4 options

Flashcards

Question

What does a repository provide?

Answer

Clean CRUD API (get, getList, save, delete)

Question

What is SearchCriteria?

Answer

Flexible query builder with filters, sorting, and pagination

Question

How do you get total count?

Answer

$results->getTotalCount()

Question

What are extension attributes?

Answer

Custom data via PHP interfaces without modifying core

Question

What is the repository method for lists?

Answer

getList(SearchCriteriaInterface)

Revision Notes

Key Takeaways

  • 1. Repositories provide CRUD API: get, getList, save, delete
  • 2. SearchCriteria builds queries with filters, sorting, pagination
  • 3. SearchResults contains items and total count
  • 4. Extension attributes add custom data via interfaces
  • 5. Filter groups with OR; multiple groups with AND
  • 6. Repositories use resource models internally

Interview Tips

  • Explain the repository pattern in Magento
  • Describe how to build SearchCriteria queries
  • Discuss extension attributes vs custom attributes
  • Know how repositories relate to resource models

Cheat Sheet

Repository Pattern Cheat Sheet

Interface:

interface PostRepositoryInterface {
    get($id);
    getList(SearchCriteria);
    save(Post);
    delete(Post);
    deleteById($id);
}

SearchCriteria:

$criteria = $this->searchCriteriaFactory->create();
$criteria->setFilterGroups([/* filters */]);
$criteria->setSortOrders([/* sort */]);
$criteria->setPageSize(20)->setCurrentPage(1);

Results:

$results = $repo->getList($criteria);
$results->getItems();
$results->getTotalCount();

Extension Attributes:

  • Use interfaces
  • Type-safe
  • No EAV overhead