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?
2. What is SearchCriteria used for?
3. How do extension attributes differ from custom attributes?
4. What method returns total count from search results?
Flashcards
Question
What does a repository provide?
Click to reveal answer
Answer
Clean CRUD API (get, getList, save, delete)
Question
What is SearchCriteria?
Click to reveal answer
Answer
Flexible query builder with filters, sorting, and pagination
Question
How do you get total count?
Click to reveal answer
Answer
$results->getTotalCount()
Question
What are extension attributes?
Click to reveal answer
Answer
Custom data via PHP interfaces without modifying core
Question
What is the repository method for lists?
Click to reveal answer
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