Лучший способ загрузить пользовательскую модель в Magento 2

15

Поскольку мне было трудно найти правильный путь, ниже вы могли найти лучшую практику, которую я сделал своей. Наслаждайтесь, поправьте мой английский, если это необходимо, и скажите, что я ошибаюсь, если это так :)

Изменить: ... и я узнал, что я был неправ в некоторых аспектах. Поэтому я обновил исходный пост после того, как ответы Рафаэля помогли мне понять больше. Спасибо ему !

Концепция используется ниже :

Вам будет легче понять коды и пояснения ниже, если вы знакомы с этими понятиями:

  • Зависимость внедрения (поскольку все $this->variableпеременные в кодах вводятся)
  • Договор на обслуживание и хранилище
  • завод

Контекст :

Просто чтобы иметь больше контекста, представьте, что у нас есть модуль, правильно сконструированный с помощью:

  • класс блока CustomBlock, содержащий метод getCustomModel($id),
  • этот метод возвращает объект CustomModel на основе идентификатора, переданного в param,
  • Тип CustomModel соответствует модели в \Vendor\Module\Model\CustomModel
  • Эта модель поставляется с моделью ресурсов (в \Vendor\Module\Model\ResourceModel\CustomModel)
  • и с его хранилищем (в \Vendor\Module\Model\CustomModelRepository).

Вопрос :

  • Как лучше всего разрешать загрузку объекта CustomModel?

Вы не можете использовать load()объект из CustomModel, поскольку этот метод устарел.

Хорошая практика говорит о том, что вы должны использовать контракт на обслуживание CustomModel. Сервисные контракты - это интерфейсы данных (например, CustomModelInterface) и сервисные интерфейсы (например, CustomModelRepositoryInterface). Итак, мой блок выглядит так:

/ ** @var SlideRepositoryInterface * /
защищенный $ slideRepository;

/ **
 * Конструктор CustomBlock
 * ...
 * @param CustomModelRepositoryInterface $ customModelRepository
 * ...
 * /
публичная функция __construct (
...
CustomModelRepositoryInterface $ customModelRepository
...
) {
    $ this-> customModelRepository = $ customModelRepository;
}

публичная функция getCustomModel ($ id) {
    return $ this-> customModelRepository-> get ($ id);
}

Прежде всего, мы вводим CustomModelRepositoryInterfaceобъект в конструктор и используем его в нашем getCustomModel()методе.

В классе Api\CustomModelRepositoryInterfaceне так много. Как правило (но ничто не мешает вам делать по- другому) вы объявите основные методы: get, getList, save, delete, deleteById. Для целей этого раздела ниже приведено только getобъявление метода:

/**
 * Get info by id
 *
 * @param int $id
 * @return Data\CustomModelInterface
 * @throws \Magento\Framework\Exception\NoSuchEntityException
 */
public function get($id);

Хорошо, но если мой CustomModel Interface вызывается путем внедрения зависимостей в мой блочный конструктор, где находится код? Для ответа на этот вопрос вы должны объяснить Magento, где найти класс, реализующий этот интерфейс. В файле etc / di.xml модуля вы должны добавить:

<preference for="Vendor\Module\Api\CustomModelRepositoryInterface" type="Vendor\Module\Model\CustomModelRepository" />

Так что CustomModelRepositoryInterfaceкласс - это сервисный интерфейс. При его реализации вам придется реализовать также интерфейсы данных (как минимум, Vendor\Module\Api\Data\CustomModelInterfaceи Vendor\Module\Api\Data\CustomModelSearchResultsInterface). Ваша модель должна будет реализовать Vendor\Module\Api\Data\CustomModelInterfaceи добавить <preference ... />строки для каждого из ваших интерфейсов. Наконец, в любое время, когда вы используете сервисный контракт, не думайте mySomethingInterfaceбольше mySomething: позвольте magento использовать di.xmlмеханизм предпочтений.

Хорошо, что будет дальше? Когда мы внедряем CustomModelRepositoryInterfaceв конструктор блока, мы получаем CustomModelRepositoryобъект. CustomModelRepositoryдолжен реализовать метод объявить в CustomModelRepositoryInterface. Итак, мы имеем это в Vendor\Module\Model\CustomModelRepository:

публичная функция get ($ id) {
    $ customModel = $ this-> customModelFactory-> create ();
    $ CustomModel-> нагрузка ($ ID);
    if (! $ customModel-> getId ()) {
      выбросить новое NoSuchEntityException (__ ('CustomModel с идентификатором "% 1" не существует. ", $ id));
    }
    return $ customModel;
}

Что мы делаем? Мы создаем пустой CustomModelобъект благодаря фабрике. Далее мы загружаем данные с CustomModelиспользованием метода load model. Затем мы возвращаем a, NoSuchEntityExceptionесли нам не удалось загрузить CustomModelидентификатор с параметрами. Но если все в порядке, мы возвращаем модель объекта и жизнь продолжается.

Но вау ... В этом примере что это?

$customModel->load($id);

Разве не такой же устаревший loadметод, как в начале? Да, это так. Я думаю, что это позор, но вы должны использовать его, поскольку в этом методе load () отправляются некоторые события, и разработчик может их прослушать (см. Ответ Рафаэля ниже).

В будущем мы будем сохранять Entity Manager. Это еще одна история, связанная с новой концепцией Magento 2, но если вы захотите взглянуть на нее, Entity Manager уже реализован в Resource Model of CMS Page (v2.1):

public function load(AbstractModel $object, $value, $field = null)
{
    $pageId = $this->getPageId($object, $value, $field);
    if ($pageId) {
        $this->entityManager->load($object, $pageId);
    }
    return $this;
}
Николас Перно
источник

Ответы:

16

Лучшая практика: через сервисный контракт

Лучше всего всегда использовать контракт на обслуживание, когда это возможно. Вы можете найти список причин здесь: Magento 2: каковы преимущества использования сервисных контрактов?

Для получения подробной информации о том, как реализовать контракт на обслуживание, я предлагаю вам проверить эту тему: Как реализовать контракт на обслуживание для пользовательского модуля в Magento 2?

Если нет контракта на обслуживание

Если контракт на обслуживание недоступен, вам следует использовать getметод репозитория моделей . Используя этот метод, вы получаете пользу от системы кэширования magento, например, для CategoryRepositoryкласса:

public function get($categoryId, $storeId = null)
{
    $cacheKey = null !== $storeId ? $storeId : 'all';
    if (!isset($this->instances[$categoryId][$cacheKey])) {
        /** @var Category $category */
        $category = $this->categoryFactory->create();
        if (null !== $storeId) {
            $category->setStoreId($storeId);
        }
        $category->load($categoryId);
        if (!$category->getId()) {
            throw NoSuchEntityException::singleField('id', $categoryId);
        }
        $this->instances[$categoryId][$cacheKey] = $category;
    }
    return $this->instances[$categoryId][$cacheKey];
}

Устаревший load()метод

Magento 2 медленно отходит от стандартной системы CRUD, отбросив систему наследования и внедрив ее через композицию с использованием нового EntityManager 2.1, подробности которого можно найти здесь: Magento 2.1: с помощью диспетчера сущностей

Также я предлагаю вам прочитать эту интересную тему об устаревших методах CRUD: Устаревшие методы сохранения и загрузки в абстрактной модели

Почему бы не использовать загрузку модели ресурса

Основная причина в том, что если вы используете loadметод модели ресурсов , вы пропустите какую-то важную часть системы загрузки, реализованную в loadметоде модели , см Magento\Framework\Model\AbstractModel.

public function load($modelId, $field = null)
{
    $this->_beforeLoad($modelId, $field);
    $this->_getResource()->load($this, $modelId, $field);
    $this->_afterLoad();
    $this->setOrigData();
    $this->_hasDataChanges = false;
    $this->updateStoredData();
    return $this;
}

loadНепосредственный вызов метода модели ресурсов будет иметь следующие последствия:

  • _beforeLoad не вызывается: при этом загрузка модели до событий не отправляется
  • _afterLoad не вызывается: при этом загрузка модели после событий не отправляется
  • сохраненные данные не обновляются, что может вызвать различные проблемы (например, если вы звоните prepareDataForUpdateиз Magento\Framework\Model\ResourceModel\Db\AbstractDb)
Рафаэль в цифровом пианизме
источник
Спасибо, Рафаэль, все, что ты говоришь, имеет смысл и дополняет мои знания. Но я не понимаю, почему Канди комментирует (под своим ответом), что Мариус может использовать метод load () своей модели ресурсов собственного модуля? Он находится в [ magento.stackexchange.com/questions/114929/… методы сохранения и загрузки в абстрактной модели). Есть идеи ?
Николас ПЕРНО
@NicolasPERNOT в основном KAndy объясняет, что цель состоит в том, чтобы иметь SL (Service Layer) для каждого модуля, и это то, что должно использоваться каждый раз, когда вам нужно загрузить сущность. Я предлагаю вам прокомментировать, упомянув его, может быть, он сможет просветить вас, поскольку он, я думаю, сотрудник Magento Inc.
Рафаэль из Digital Pianism
Ну, я наконец обновил свой оригинальный пост. Спасибо Рафаэль за вашу помощь.
Николас ПЕРНО
Я вижу, что по крайней мере в Magento 2.2 это важно, что включено в загрузку ResourceModel, так что не стоит использовать методы ResourceModel напрямую, верно?
Янис Элмерис
В настоящее время мы можем безопасно загрузить модель, используя метод Resource Model load(). Модель ресурсов вызывает методы модели из собственного load()метода: $model->beforeLoad() { $this->_beforeLoad() }и$model->afterLoad() { $this->_afterLoad() }
sergei.sss
-2

Я думаю, что следующее утверждение не является действительным сейчас.

Why not using the resource model load

мы можем найти Magento\Framework\EntityManager\Observerпапку все события.

Шива Кумар Кодуру
источник