Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Репозитории

Репозиторий — класс, через который остальной код сохраняет, находит и удаляет записи. Он прячет модель, ресурсную модель и коллекцию за несколькими понятными методами. Репозиторий — вторая часть сервисного контракта: его интерфейс лежит в папке 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, не сработают. Вне репозитория работайте с записями только через его интерфейс.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.