Injetando dependências em um modelo CRUD / Abstract do Magento 2

12

É possível injetar uma dependência em um modelo CRUD do Magento 2?

Isso é - Magento 2 tem uma base de classe modelo abstrato: Magento\Framework\Model\AbstractModel. Se você deseja criar um objeto de modelo simples Criar, Ler, Atualizar, Excluir, estende essa classe com sua própria classe.

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

É possível injetar dependências no __constructmétodo do seu modelo ? Quando tento, acabo recebendo o seguinte erro.

Erro fatal: Não é possível instanciar a classe abstrata Magento \ Framework \ Model \ ResourceModel \ AbstractResource

O culpado parece ser o 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 = []
) {

Existem duas dicas de tipo neste construtor ( Magento\Framework\Model\ResourceModel\AbstractResource, Magento\Framework\Data\Collection\AbstractDb) que não são interfaces do gerenciador de objetos Magento. São aulas abstratas. Quando estendo essa classe e tento adicionar minha dependência injetada

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);

    }
}

O Magento falha quando o gerenciador de objetos tenta instanciar as classes abstratas.

Eu posso "consertar" isso movendo minha dependência de objeto na frente das classes abstratas

    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 = [],
    ) {  

No entanto, isso mudou a ordem dos argumentos. Em uma classe totalmente gerenciada por objetos, isso não seria um problema. No entanto, o fato de que essas dicas de tipo de classe abstrata existem implica que há partes do sistema Magento que instanciam manualmente (ou seja, não através do gerenciador de objetos ou DI) instanciam objetos CRUD e passam objetos que estão em conformidade com as dicas de tipo nessa ordem específica .

Isso é seguro? ou seja, essas classes abstratas no construtor de um modelo abstrato são apenas código legado e não são usadas? Ou partes do sistema ainda as usarão, o que significa que não é possível injetar dependências em um modelo CRUD?

Alan Storm
fonte

Respostas:

9

Primeiro de tudo, o construtor é a API privada da classe. A função construtora tem um significado especial e não requer que tenha a mesma lista / ordem de argumentos da classe pai.

É possível injetar uma dependência em um modelo CRUD do Magento 2?

Sim, claro.

Isso é seguro?

Sim, mas o Magento Object Manager assume que todos os parâmetros opcionais são colocados no final da lista e os parâmetros necessários após o opcional não serão resolvidos.

Os argumentos $ resource, $ resourceCollection são herdados, mas ainda são amplamente utilizados nas classes Model. A maior parte do modelo usa código como este para inicializar a classe de recurso e coleção.

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

É por isso que esses parâmetros são opcionais. Mas, por exemplo, no teste de unidade, passamos simulação de recurso ou coleção no construtor para permitir a realização da substituição.

KAndy
fonte
@Kanday O departamento de engenharia / arquitetura do Magento já fez uma declaração pública de que a ordem dos construtores para as classes principais é irrelevante? Ou isso é apenas a esperança da maioria das pessoas que trabalham nisso?
Alan Storm
Não chamarei de "irrelevante". Apenas o OM passará os argumentos necessários para o seu construtor e isso não depende da ordem na classe pai. Além disso, em uso nomes de parâmetros, então agora é melhor não mudar seu (é diferente da linguagem PHP, onde você pode alterar nomes de parâmetros como quiser)
Kandy
Não sei se entendi o que você está dizendo. Você está dizendo que, em algum momento no futuro, o código principal do sistema Magento poderá começar a tratar a ordem de argumento / parâmetro como significativa novamente?
Alan Storm
Eu acredito que não
KAndy 15/01
obrigado novamente! FWIW, e para os googlers, isso parece ser uma coisa segura a se fazer. Pelo que sei, não há código do sistema Magento que instancia cegamente automaticamente um modelo assumindo a ordem dos parâmetros do construtor.
Alan Storm
6

Parece seguro. Pelo menos, o magento está fazendo isso em vários lugares. Consulte os métodos __construct na seguinte lista (não exclusiva) de classes para obter exemplos

  • \ Magento \ Tema \ Modelo \ Tema \ Arquivo
  • \ Magento \ Tema \ Modelo \ Design
  • \ Magento \ Vendas \ Modelo \ Pedido \ Creditmemo

Infelizmente, não consigo responder à outra parte da sua pergunta.

Nathan Toombs
fonte
4
  1. Como você usa seu modelo?
  2. No seu caso $mineé um exigido parâmetro, enquanto $resource, $resourceCollectione $datasão opcionais . Parâmetros opcionais sempre devem durar, caso contrário, é impossível trabalhar com eles como com o opcional. Portanto, parece-me bom que você especifique $mineantes de qualquer parâmetro opcional.
BuskaMuza
fonte
Exceto que os parâmetros Abstratos não são parâmetros injetados por dependência, e se o código do sistema principal do Magento espera que eles estejam lá, se mover $minepara a frente da fila criará erros. Se o código do sistema principal do Magento não os utiliza, por que eles estão lá? Essa é a pergunta que estou tentando chegar ao fundo. Só porque eu posso usar meu modelo com o parâmetro movido não o torna seguro.
Alan Storm
Alguns modelos ainda podem usar esses parâmetros opcionais para passar um modelo de recurso customizado. Por exemplo, github.com/magento/magento2/blob/develop/app/code/Magento/…
#
Magento usa reflexão para determinar se o parâmetro é opcional ou não. E o PHP considera todos os parâmetros anteriores ao parâmetro obrigatório, conforme necessário . Portanto, se você mover $mineantes dos parâmetros opcionais, eles se tornam realmente opcionais e o Magento passa apenas os valores padrão ( null, array()). Se você colocar um parâmetro necessário após os opcionais, o PHP considerará os parâmetros opcionais como necessários e o Magento tentará instancia-los (mas não há preferências para eles).
BuskaMuza
Embora eu concorde que pareça confuso e talvez possamos configurar uma preferência para as classes abstratas em vez de manipulá-la dentro da classe model. Portanto, um objeto real é sempre injetado.
BuskaMuza 11/01