Репозитории
Репозиторий — класс, через который остальной код сохраняет, находит и удаляет записи. Он прячет модель, ресурсную модель и коллекцию за несколькими понятными методами. Репозиторий — вторая часть сервисного контракта: его интерфейс лежит в папке Api, а реализация — в Model.
app/code/Acme/Hello/
├── Api/
│ ├── Data/ReviewInterface.php
│ └── ReviewRepositoryInterface.php новый файл
├── Model/
│ └── ReviewRepository.php новый файл
└── etc/
└── di.xml изменёнИнтерфейс
// app/code/Acme/Hello/Api/ReviewRepositoryInterface.php
namespace Acme\Hello\Api;
use Acme\Hello\Api\Data\ReviewInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
interface ReviewRepositoryInterface
{
/**
* @param \Acme\Hello\Api\Data\ReviewInterface $review
* @return \Acme\Hello\Api\Data\ReviewInterface
*/
public function save(ReviewInterface $review): ReviewInterface;
/**
* @param int $id
* @return \Acme\Hello\Api\Data\ReviewInterface
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getById(int $id): ReviewInterface;
/**
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return \Magento\Framework\Api\SearchResultsInterface
*/
public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface;
/**
* @param int $id
* @return bool
*/
public function deleteById(int $id): bool;
}Такой набор из четырёх методов принят во всех репозиториях ядра. В комментариях типы пишут полным именем класса.
Реализация
// app/code/Acme/Hello/Model/ReviewRepository.php
namespace Acme\Hello\Model;
use Acme\Hello\Api\Data\ReviewInterface;
use Acme\Hello\Api\ReviewRepositoryInterface;
use Acme\Hello\Model\ResourceModel\Review as ReviewResource;
use Acme\Hello\Model\ResourceModel\Review\CollectionFactory;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Api\SearchResultsInterface;
use Magento\Framework\Api\SearchResultsInterfaceFactory;
use Magento\Framework\Exception\NoSuchEntityException;
class ReviewRepository implements ReviewRepositoryInterface
{
public function __construct(
private ReviewFactory $reviewFactory,
private ReviewResource $resource,
private CollectionFactory $collectionFactory,
private CollectionProcessorInterface $collectionProcessor,
private SearchResultsInterfaceFactory $searchResultsFactory,
) {}
public function save(ReviewInterface $review): ReviewInterface
{
$this->resource->save($review);
return $review;
}
public function getById(int $id): ReviewInterface
{
$review = $this->reviewFactory->create();
$this->resource->load($review, $id);
if (!$review->getId()) {
throw new NoSuchEntityException(__('Отзыв %1 не найден.', $id));
}
return $review;
}
public function getList(SearchCriteriaInterface $searchCriteria): SearchResultsInterface
{
$collection = $this->collectionFactory->create();
$this->collectionProcessor->process($searchCriteria, $collection);
$results = $this->searchResultsFactory->create();
$results->setSearchCriteria($searchCriteria);
$results->setItems($collection->getItems());
$results->setTotalCount($collection->getSize());
return $results;
}
public function deleteById(int $id): bool
{
$this->resource->delete($this->getById($id));
return true;
}
}Репозиторий использует три класса из урока о моделях. Объект CollectionProcessorInterface из ядра переносит условия поиска на коллекцию: фильтры, сортировку и номер страницы.
Свяжите интерфейс с классом в etc/di.xml и выполните bin/magento cache:clean:
<!-- app/code/Acme/Hello/etc/di.xml -->
<preference for="Acme\Hello\Api\ReviewRepositoryInterface" type="Acme\Hello\Model\ReviewRepository"/>Поиск через SearchCriteria
Условия для getList() собирает SearchCriteriaBuilder. Перепишем модель представления с последними отзывами:
// app/code/Acme/Hello/ViewModel/LatestReviews.php
namespace Acme\Hello\ViewModel;
use Acme\Hello\Api\ReviewRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\View\Element\Block\ArgumentInterface;
class LatestReviews implements ArgumentInterface
{
public function __construct(
private ReviewRepositoryInterface $reviewRepository,
private SearchCriteriaBuilder $criteriaBuilder,
) {}
public function getReviews(): array
{
$criteria = $this->criteriaBuilder
->addFilter('rating', 4, 'gteq')
->setPageSize(5)
->create();
return $this->reviewRepository->getList($criteria)->getItems();
}
}Страница /hello покажет тот же список, но модель представления больше не знает о коллекциях и таблицах. Третий аргумент addFilter() — условие из таблицы прошлого урока; по умолчанию это eq.
Частая ошибка
Репозиторий создали, а в остальном коде продолжают вызывать ресурсную модель и коллекцию напрямую. Тогда плагины и проверки, которые другие модули повесят на ReviewRepositoryInterface, не сработают. Вне репозитория работайте с записями только через его интерфейс.