Encodera

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

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

Оглавление

Интерфейсы данных

Сервисный контракт — набор интерфейсов PHP, через которые другие модули работают с вашими данными. Интерфейс обещает: эти методы не изменятся, даже если таблица или модель внутри будут переписаны. Контракт состоит из двух частей: интерфейсы данных описывают запись, а репозитории — действия с ней. Этот урок посвящён первой части.

Где лежат интерфейсы

Все интерфейсы контракта находятся в папке Api модуля, интерфейсы данных — в Api/Data. Файлы создают вручную.

app/code/Acme/Hello/
├── Api/
│   └── Data/
│       └── ReviewInterface.php     новый файл
├── Model/
│   └── Review.php                  изменён
└── etc/
    └── di.xml                      изменён

Интерфейс данных

Интерфейс перечисляет методы чтения и записи для каждого поля:

// app/code/Acme/Hello/Api/Data/ReviewInterface.php
namespace Acme\Hello\Api\Data;
 
interface ReviewInterface
{
    /** @return int|null */
    public function getId();
 
    /** @return int */
    public function getProductId();
 
    /**
     * @param int $productId
     * @return $this
     */
    public function setProductId($productId);
 
    /** @return string */
    public function getAuthor();
 
    /**
     * @param string $author
     * @return $this
     */
    public function setAuthor($author);
 
    /** @return int */
    public function getRating();
 
    /**
     * @param int $rating
     * @return $this
     */
    public function setRating($rating);
}

Комментарии с @param и @return здесь обязательны: по ним Magento превращает объект в JSON и обратно, когда контракт открывают через API. Поле content добавляется такой же парой методов.

Реализация в модели

Модель из прошлого урока объявляет, что выполняет контракт, и получает явные методы:

// app/code/Acme/Hello/Model/Review.php
namespace Acme\Hello\Model;
 
use Acme\Hello\Api\Data\ReviewInterface;
use Magento\Framework\Model\AbstractModel;
 
class Review extends AbstractModel implements ReviewInterface
{
    protected function _construct(): void
    {
        $this->_init(ResourceModel\Review::class);
    }
 
    public function getProductId()
    {
        return (int) $this->getData('product_id');
    }
 
    public function setProductId($productId)
    {
        return $this->setData('product_id', (int) $productId);
    }
 
    public function getAuthor()
    {
        return (string) $this->getData('author');
    }
 
    public function setAuthor($author)
    {
        return $this->setData('author', (string) $author);
    }
 
    public function getRating()
    {
        return (int) $this->getData('rating');
    }
 
    public function setRating($rating)
    {
        return $this->setData('rating', (int) $rating);
    }
}

Метод getId() уже есть в AbstractModel. Остальные читают и записывают значения столбцов через getData() и setData().

Предпочтение

Чтобы Magento знал, какой класс стоит за интерфейсом, добавьте строку в etc/di.xml:

<!-- app/code/Acme/Hello/etc/di.xml -->
<preference for="Acme\Hello\Api\Data\ReviewInterface" type="Acme\Hello\Model\Review"/>

Выполните bin/magento cache:clean. На странице ничего не изменится — изменился способ, которым остальной код обращается к отзыву.

Использование

Теперь код зависит от интерфейса, а не от модели. Новый пустой отзыв даёт сгенерированная фабрика интерфейса:

// app/code/Acme/Hello/Model/ReviewService.php
use Acme\Hello\Api\Data\ReviewInterfaceFactory;
 
public function __construct(private ReviewInterfaceFactory $reviewFactory) {}
 
public function draft(int $productId, string $author): \Acme\Hello\Api\Data\ReviewInterface
{
    return $this->reviewFactory->create()
        ->setProductId($productId)
        ->setAuthor($author)
        ->setRating(5);
}

Метод возвращает заполненный, но ещё не сохранённый отзыв. Сохранять его будет репозиторий из следующего урока.

Что это даёт

Без контрактаС контрактом
Чужой код вызывает getData('author') и зависит от имени столбцаЧужой код вызывает getAuthor() из интерфейса
Изменение таблицы ломает другие модулиТаблицу можно менять, пока интерфейс прежний
Модель нельзя отдать через APIИнтерфейс подключается к REST одной записью в XML

Частая ошибка

В интерфейсе данных опускают комментарии или пишут в них array и mixed. В коде PHP всё работает, но при вызове через REST Magento не может определить тип поля и возвращает ошибку. Указывайте точные типы: int, string, float, bool, имя интерфейса или string[] для списка.

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

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