La mejor manera de cargar un modelo personalizado en Magento 2

15

Debido a que fue difícil para mí encontrar el camino correcto, a continuación podría encontrar la mejor práctica que hice para mí. Disfruta, corrige mi inglés si es necesario y dime que estoy equivocado si lo estoy. :)

Editar: ... y descubrí que estaba equivocado en algún aspecto. Así que actualicé la publicación original después de que las respuestas de Raphael me ayudaron a entender más. Gracias a él !

Concepto utilizado a continuación :

Le resultará más fácil comprender los códigos y explicaciones a continuación si se siente cómodo con estos conceptos:

  • Dependencia de inyección (ya $this->variableque se inyectan todas las variables en los códigos)
  • Contrato de servicio y repositorio
  • Fábrica

Contexto :

Solo para tener más contexto, imagine que tenemos un módulo construido correctamente con:

  • una clase de bloque CustomBlock que contiene un método getCustomModel($id),
  • este método devuelve un objeto CustomModel basado en la identificación pasada en param,
  • El tipo de modelo personalizado corresponde al modelo en \Vendor\Module\Model\CustomModel
  • Este modelo viene con su modelo de recurso (en \Vendor\Module\Model\ResourceModel\CustomModel)
  • y con su repositorio (en \Vendor\Module\Model\CustomModelRepository).

Pregunta :

  • ¿Cuál es la mejor práctica para dejar que todo cargue un objeto CustomModel?

No puede usar load()desde un objeto CustomModel ya que este método está en desuso.

La buena práctica dice que debe usar el contrato de servicio CustomModel. Los contratos de servicio son interfaces de datos (por ejemplo, CustomModelInterface) e interfaces de servicio (por ejemplo, CustomModelRepositoryInterface). Entonces mi bloque se ve así:

/ ** @var SlideRepositoryInterface * /
protegido $ slideRepository;

/ **
 * Constructor CustomBlock
 * ...
 * @param CustomModelRepositoryInterface $ customModelRepository
 * ...
 * /
función pública __construct (
...
CustomModelRepositoryInterface $ customModelRepository
...
) {
    $ this-> customModelRepository = $ customModelRepository;
}

función pública getCustomModel ($ id) {
    devuelve $ this-> customModelRepository-> get ($ id);
}

En primer lugar, inyectamos el CustomModelRepositoryInterfaceobjeto en el constructor y lo usamos en nuestro getCustomModel()método.

En la clase Api\CustomModelRepositoryInterfaceno hay mucho. Por lo general (pero nada impide que usted pueda hacer de manera diferente) que se declare métodos básicos: get, getList, save, delete, deleteById. A los efectos de este tema, a continuación se muestra solo la getdeclaración del método:

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

Ok, pero si mi interfaz CustomModel se llama por inyección de dependencia en mi constructor de bloques, ¿dónde está el código? Para responder a esta pregunta, debe explicarle a Magento dónde encontrar la clase que implementa esta interfaz. En el archivo etc / di.xml del módulo, debe agregar:

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

Entonces CustomModelRepositoryInterfaceclase es una interfaz de servicio. Al implementarlo, deberá implementar también interfaces de datos (al menos Vendor\Module\Api\Data\CustomModelInterfacey Vendor\Module\Api\Data\CustomModelSearchResultsInterface). Su modelo tendrá que implementar Vendor\Module\Api\Data\CustomModelInterfacey agregar <preference ... />líneas para cada una de sus interfaces. Finalmente, en cualquier momento que use el contrato de servicio, piense en mySomethingInterfaceno más en mySomething: deje que magento use el di.xmlmecanismo de preferencias.

Ok, que viene después? A medida que inyectamos CustomModelRepositoryInterfaceen el constructor de bloques, obtenemos un CustomModelRepositoryobjeto. CustomModelRepositorytiene que implementar el método declarar en CustomModelRepositoryInterface. Entonces tenemos esto en Vendor\Module\Model\CustomModelRepository:

función pública get ($ id) {
    $ customModel = $ this-> customModelFactory-> create ();
    $ customModel-> load ($ id);
    if (! $ customModel-> getId ()) {
      lanzar una nueva NoSuchEntityException (__ ('CustomModel con id "% 1" no existe.', $ id));
    }
    return $ customModel;
}

Qué estamos haciendo ? Creamos un CustomModelobjeto vacío gracias a la fábrica. A continuación, cargamos datos CustomModelutilizando el método del modelo de carga. A continuación, devolvemos a NoSuchEntityExceptionsi no pudimos cargar CustomModelel id con los parámetros. Pero si todo está bien, devolvemos el objeto modelo y la vida continúa.

Pero wow ...! En este ejemplo, ¿qué es eso?

$customModel->load($id);

¿No es el mismo loadmétodo obsoleto que al principio? Sí lo es. Creo que es una pena, pero debe usarlo ya que en este método load () hay algunos eventos enviados y el desarrollador podría escucharlos (vea la respuesta de Raphael a continuación).

En el futuro, seremos salvados por Entity Manager. Es otra historia como un nuevo concepto de Magento 2, pero si desea echar un vistazo, Entity Manager ya está implementado en el Modelo de recursos de la página CMS (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;
}
Nicolas PERNOT
fuente

Respuestas:

16

Mejores prácticas: a través del contrato de servicio

La mejor práctica es siempre usar el contrato de servicio siempre que sea posible. Puede encontrar la lista de razones aquí: Magento 2: ¿cuáles son los beneficios de usar contratos de servicio?

Para obtener detalles sobre cómo implementar un contrato de servicio, le sugiero que consulte este tema: ¿Cómo implementar un contrato de servicio para un módulo personalizado en Magento 2?

Si no hay contrato de servicio disponible

Si no hay un contrato de servicio disponible, debe usar el getmétodo de repositorio de modelos . Con este método, se beneficia del sistema de almacenamiento en caché de magento, por ejemplo, para la CategoryRepositoryclase:

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()Método obsoleto

Magento 2 se está alejando lentamente del sistema CRUD estándar al soltar el sistema de herencia e implementarlo a través de la composición usando el nuevo EntityManager 2.1. Puede encontrar detalles aquí: Magento 2.1: usando el administrador de la entidad

También le sugiero que lea este interesante tema sobre los métodos CRUD en desuso: Métodos de guardar y cargar en desuso en Modelo abstracto

¿Por qué no utilizar la carga del modelo de recursos?

La razón principal es que si usa el loadmétodo del modelo de recurso , omitirá alguna parte importante del sistema de carga que se implementa en el loadmétodo del modelo , consulte 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;
}

Llamar loaddirectamente al método del modelo de recurso tendrá el siguiente impacto:

  • _beforeLoad no se llama: por lo tanto, la carga del modelo antes de que los eventos no se envíen
  • _afterLoad no se llama: por lo tanto, la carga del modelo después de que los eventos no se envían
  • los datos almacenados no se actualizan, lo que puede causar varios problemas (por ejemplo, si llama prepareDataForUpdatedesde Magento\Framework\Model\ResourceModel\Db\AbstractDb)
Raphael en Digital Pianism
fuente
Gracias Raphael, todo lo que dices tiene sentido y completa mi conocimiento. Pero no entiendo por qué KAndy comenta (bajo su respuesta) que Marius puede usar el método load () de su modelo de recurso de módulo personalizado. Está en [ magento.stackexchange.com/questions/114929/… métodos de guardar y cargar en el Modelo abstracto). Algunas ideas ?
Nicolas PERNOT
@NicolasPERNOT básicamente KAndy explica que el objetivo es tener SL (capa de servicio) para cada módulo y que esto es lo que debe usarse cada vez que necesite cargar una entidad. Sugiero que comentes al mencionarlo, tal vez él pueda iluminarte, ya que es un empleado de Magento Inc, creo
Raphael en Digital Pianism
Bueno, finalmente actualicé mi publicación original. Gracias Raphael por tu ayuda.
Nicolas PERNOT
Veo que al menos en Magento 2.2 esa importancia está incluida en la carga de ResourceModel, por lo que no está bien usar los métodos de ResourceModel directamente, ¿verdad?
Jānis Elmeris
Actualmente, podemos cargar el modelo de manera segura utilizando el load()método del modelo de recurso Resource Model llama a los métodos del modelo desde su propio load()método: $model->beforeLoad() { $this->_beforeLoad() }y$model->afterLoad() { $this->_afterLoad() }
sergei.sss
-2

Creo que la siguiente declaración no es válida ahora.

Why not using the resource model load

Podemos encontrar la Magento\Framework\EntityManager\Observercarpeta de todos los eventos.

Siva Kumar Koduru
fuente