Inyección de dependencias en un Magento 2 CRUD / Modelo abstracto

12

¿Es posible inyectar una dependencia en un modelo Magento 2 CRUD?

Eso es - Magento 2 tiene una clase base abstracta modelo: Magento\Framework\Model\AbstractModel. Si desea crear un objeto modelo simple Crear, Leer, Actualizar, Eliminar, amplíe esta clase con su propia clase.

class Foo extends Magento\Framework\Model\AbstractModel
{
}

¿Es posible tener dependencias inyectadas en el __constructmétodo de su modelo ? Cuando lo intento, termino obteniendo el siguiente error.

Error fatal: no se puede crear una instancia de la clase abstracta Magento \ Framework \ Model \ ResourceModel \ AbstractResource

El culpable parece ser el AbstractModel's __constructmétodo.

public function __construct(
    \Magento\Framework\Model\Context $context,
    \Magento\Framework\Registry $registry,
    \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
    \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
    array $data = []
) {

Hay dos sugerencias de tipo en este constructor ( Magento\Framework\Model\ResourceModel\AbstractResource, Magento\Framework\Data\Collection\AbstractDb) que no son interfaces de administrador de objetos de Magento. Son clases abstractas. Cuando extiendo esta clase e intento agregar mi dependencia inyectada

class Foo extends Magento\Framework\Model\AbstractModel
{
    public function __construct(
        \Magento\Framework\Model\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
        \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
        array $data = [],
        \Package\Module\Model\Mine $mine,

    ) {
        //...
        parent::__construct($context, $registry, $resource, $resourceCollection, $data);

    }
}

Magento rescata cuando el administrador de objetos intenta instanciar las clases abstractas.

Puedo "arreglar" esto moviendo mi dependencia de objeto frente a las clases abstractas

    public function __construct(
        \Magento\Framework\Model\Context $context,
        \Magento\Framework\Registry $registry,

        \Package\Module\Model\Mine $mine,

        \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null,
        \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
        array $data = [],
    ) {  

Sin embargo, esto cambió el orden del argumento. En una clase que estaba completamente administrada por objetos, esto no sería un problema. Sin embargo, el hecho de que existan estas sugerencias de tipo de clase abstracta implica que hay partes del sistema Magento que crearán manualmente (es decir, no a través del administrador de objetos o DI) objetos CRUD y pasarán objetos que se ajusten a las sugerencias de tipo en ese orden específico .

¿Es esto seguro? es decir, ¿estas clases abstractas en el constructor de un modelo abstracto son solo código heredado y no se usan? ¿O algunas partes del sistema seguirán usándolas, lo que significa que no es posible inyectar dependencias en un modelo CRUD?

Alan Storm
fuente

Respuestas:

9

En primer lugar, el constructor es una API privada de clase. La función de constructor tiene un significado especial y no requiere tener la misma lista / orden de argumentos que en la clase padre.

¿Es posible inyectar una dependencia en un modelo Magento 2 CRUD?

Sí, por supuesto.

¿Es esto seguro?

Sí, pero Magento Object Manager supone que todos los parámetros opcionales se colocan al final de la lista y los parámetros requeridos después de opcional no se resolverán.

Los argumentos $ resource, $ resourceCollection son heredados pero aún se usan ampliamente en las clases Model. La mayoría del modelo usa código como este para inicializar recursos y clases de colección.

protected function _construct() { 
    $this->_init('Magento\AdminNotification\Model\Resource Model\Inbox'); 
}

Es por eso que estos parámetros son opcionales. Pero, por ejemplo, en la prueba unitaria, pasamos simulacros de recurso o colección en el constructor para permitir la realización de reemplazo.

Kandy
fuente
@Kanday ¿El departamento de ingeniería / arquitectura de Magento ha hecho alguna vez una declaración pública de que el orden del constructor para las clases principales es irrelevante? ¿O es solo la esperanza de la mayoría de las personas que trabajan en él?
Alan Storm
No lo llamaré "irrelevante". Solo OM pasará los argumentos necesarios a su constructor y no depende del orden en la clase principal. Además, IN usa nombres de parámetros, por lo que ahora es mejor no cambiarlos (es diferente del lenguaje php, donde puede cambiar los nombres de los parámetros que desee)
KAndy
No estoy seguro de entender lo que estás diciendo. ¿Está diciendo que, en algún momento en el futuro, el código central del sistema Magento podría comenzar a tratar el orden de argumento / parámetro como significativo nuevamente?
Alan Storm
Creo que no
KAndy
¡gracias de nuevo! FWIW, y para los Googlers, parece que debería ser algo seguro. Por lo que puedo decir, no hay código de sistema de Magento que crea automáticamente una instancia ciega de un modelo asumiendo el orden de los parámetros del constructor.
Alan Storm
6

Esto parece ser seguro. Al menos, Magento está haciendo esto en varios lugares. Vea los métodos __construct en la siguiente lista (no exclusiva) de clases para ver ejemplos

  • \ Magento \ Theme \ Model \ Theme \ File
  • \ Magento \ Theme \ Model \ Design
  • \ Magento \ Sales \ Model \ Order \ Creditmemo

Lamentablemente, no puedo responder la otra parte de tu pregunta.

Nathan Toombs
fuente
4
  1. ¿Cómo usas tu modelo?
  2. En su caso $minees un parámetro obligatorio , while $resource, $resourceCollectiony $datason opcionales . Los parámetros opcionales siempre deben ir al final, de lo contrario es imposible trabajar con ellos como con los opcionales. Por lo tanto, me parece bien que debe especificar $mineantes que cualquier parámetro opcional.
BuskaMuza
fuente
Excepto que los parámetros abstractos no son parámetros inyectados por dependencia, y si el código del sistema central de Magento espera que estén allí, pasar $mineal frente de la cola creará errores. Si el código del sistema central de Magento no los usa, ¿por qué están allí? Esa es la pregunta a la que intento llegar. El hecho de que pueda usar mi modelo con el parámetro movido no lo hace seguro.
Alan Storm
Algunos modelos aún pueden usar estos parámetros opcionales para pasar un modelo de recurso personalizado. Por ejemplo, github.com/magento/magento2/blob/develop/app/code/Magento/…
BuskaMuza
Magento usa la reflexión para determinar si el parámetro es opcional o no. Y PHP considera todos los parámetros que están delante del parámetro requerido según sea necesario . Entonces, si se mueve $mineantes de los parámetros opcionales, se vuelven realmente opcionales y Magento simplemente pasa los valores predeterminados ( null, array()). Si coloca un parámetro requerido después de los opcionales, PHP considera los parámetros opcionales como obligatorios y Magento intentó instanciarlos (pero no hay preferencias para ellos).
BuskaMuza
Aunque estoy de acuerdo en que parece confuso y tal vez podríamos configurar una preferencia por las clases abstractas en lugar de manejarlo dentro de la clase modelo. Entonces, siempre se inyecta un objeto real.
BuskaMuza