Melhor maneira de carregar um modelo personalizado no Magento 2

15

Como era difícil para mim encontrar o caminho certo, abaixo você encontrava a melhor prática que fiz. Aproveite, corrija meu inglês, se necessário, e diga que estou errado, se estiver. :)

Edit: ... e eu descobri que estava errado em algum aspecto. Atualizei o post original depois que as respostas de Raphael me ajudaram a entender mais. Graças a ele !

Conceito usado abaixo :

Será mais fácil para você entender códigos e explicações abaixo se estiver familiarizado com estes conceitos:

  • Dependência de injeção (como todas as $this->variablevariáveis ​​nos códigos são injetadas)
  • Contrato de Serviço e Repositório
  • Fábrica

Contexto :

Apenas para ter mais contexto, imagine que temos um módulo construído corretamente com:

  • uma classe de bloco CustomBlock contendo um método getCustomModel($id),
  • esse método retorna um objeto CustomModel com base no ID passado em param,
  • O tipo CustomModel corresponde ao modelo em \Vendor\Module\Model\CustomModel
  • Este modelo vem com seu modelo de recursos (in \Vendor\Module\Model\ResourceModel\CustomModel)
  • e com seu repositório (in \Vendor\Module\Model\CustomModelRepository).

Pergunta :

  • Qual é a melhor prática para permitir que tudo carregue um objeto CustomModel?

Você não pode usar o load()objeto de CustomModel, pois esse método está obsoleto.

A boa prática diz que você precisa usar o contrato de serviço CustomModel. Os contratos de serviço são interfaces de dados (por exemplo, CustomModelInterface) e interfaces de serviço (por exemplo, CustomModelRepositoryInterface). Então, meu bloco fica assim:

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

/ **
 * Construtor CustomBlock
 * ...
 * @param CustomModelRepositoryInterface $ customModelRepository
 * ...
 * /
função pública __construct (
...
CustomModelRepositoryInterface $ customModelRepository
...
) {
    $ this-> customModelRepository = $ customModelRepository;
}

função pública getCustomModel ($ id) {
    retornar $ this-> customModelRepository-> get ($ id);
}

Primeiro, injetamos o CustomModelRepositoryInterfaceobjeto no construtor e o usamos em nosso getCustomModel()método.

Na classe Api\CustomModelRepositoryInterfacenão há muito. Geralmente (mas nada impedi-lo de fazer diferente), você irá declarar métodos básicos: get, getList, save, delete, deleteById. Para os fins deste tópico, abaixo está apenas a getdeclaração do método:

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

Ok, mas se minha interface CustomModel é chamada por injeção de dependência no meu construtor de blocos, onde está o código? Para responder a esta pergunta, você precisa explicar ao Magento onde encontra a classe que implementa essa interface. No arquivo etc / di.xml do módulo, você deve adicionar:

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

Então CustomModelRepositoryInterfaceclasse é uma interface de serviço. Ao implementá-lo, você precisará implementar também interfaces de dados (pelo menos Vendor\Module\Api\Data\CustomModelInterfacee Vendor\Module\Api\Data\CustomModelSearchResultsInterface). Seu modelo precisará implementar Vendor\Module\Api\Data\CustomModelInterfacee adicionar <preference ... />linhas para cada uma de suas interfaces. Finalmente, sempre que você usar um contrato de serviço, mySomethingInterfacenão pense mais em mySomething: deixe o magento usar o di.xmlmecanismo de preferências.

Ok, o que vem a seguir? Quando injetamos CustomModelRepositoryInterfaceno construtor do bloco, obtemos um CustomModelRepositoryobjeto. CustomModelRepositorydeve implementar o método declare in CustomModelRepositoryInterface. Então, temos isso em Vendor\Module\Model\CustomModelRepository:

função pública get ($ id) {
    $ customModel = $ this-> customModelFactory-> create ();
    $ customModel-> load ($ id);
    if (! $ customModel-> getId ()) {
      lança nova NoSuchEntityException (__ ('CustomModel com o ID "% 1" não existe.', $ id));
    }
    return $ customModel;
}

O que estamos fazendo ? Criamos um CustomModelobjeto vazio graças à fábrica. Em seguida, carregamos dados no CustomModelmétodo usando o modelo de carregamento. Em seguida, retornamos a NoSuchEntityExceptionse não conseguimos carregar o CustomModelcom o id nos parâmetros. Mas se estiver tudo bem, retornamos o objeto modelo e a vida continua.

Mas uau ...! Neste exemplo, o que é isso?

$customModel->load($id);

Não é o mesmo loadmétodo obsoleto do que no começo? Sim, ele é. Acho uma pena, mas você deve usá-lo, pois neste método load () existem alguns eventos despachados e o desenvolvedor pode ouvi-los (consulte a resposta de Raphael abaixo).

No futuro, seremos salvos pelo Entity Manager. É outra história como um novo conceito Magento 2, mas se você quiser dar uma olhada, o Entity Manager já está implementado no Modelo de Recursos da 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
fonte

Respostas:

15

Prática recomendada: através do contrato de serviço

A melhor prática é sempre usar o contrato de serviço sempre que possível. Você pode encontrar a lista de razões aqui: Magento 2: quais são os benefícios do uso de contratos de serviço?

Para obter detalhes sobre como implementar um contrato de serviço, sugiro que você verifique este tópico: Como implementar um contrato de serviço para um módulo personalizado no Magento 2?

Se não houver contrato de serviço disponível

Se não houver contrato de serviço disponível, você deve usar o getmétodo de repositório do modelo . Usando este método, você se beneficia do sistema de cache magento, por exemplo, para a CategoryRepositoryclasse:

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 preterido

O Magento 2 está se afastando lentamente do sistema CRUD padrão, descartando o sistema de herança e implementando-o via composição usando o novo 2.1 EntityManager. Você pode encontrar detalhes aqui: Magento 2.1: usando o gerenciador de entidades

Também sugiro que você leia este tópico interessante sobre os métodos CRUD descontinuados: Métodos obsoletos de salvar e carregar no Modelo Abstrato

Por que não usar a carga do modelo de recurso

O principal motivo é que, se você usar o loadmétodo do modelo de recursos , pulará uma parte importante do sistema de carregamento implementada no loadmétodo do 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;
}

Chamar o loadmétodo do modelo de recurso diretamente terá o seguinte impacto:

  • _beforeLoad não é chamado: portanto, o modelo é carregado antes que os eventos não sejam despachados
  • _afterLoad não é chamado: assim, o modelo é carregado depois que os eventos não são despachados
  • os dados armazenados não são atualizados, o que pode causar vários problemas (por exemplo, se você ligar prepareDataForUpdatede Magento\Framework\Model\ResourceModel\Db\AbstractDb)
Raphael na Digital Pianism
fonte
Obrigado Raphael, tudo o que você diz faz sentido e completa meu conhecimento. Mas não entendo por que KAndy comenta (sob a resposta dele) que Marius pode usar o método load () de seu modelo de recurso de módulo personalizado? Está em [ magento.stackexchange.com/questions/114929/… métodos de salvar e carregar no modelo abstrato). Alguma ideia ?
Nicolas PERNOT 27/10/16
@NicolasPERNOT Basicamente, KAndy explica que o objetivo é ter SL (Camada de Serviço) para cada módulo e que é isso que deve ser usado toda vez que você precisar carregar uma entidade. Eu sugiro que você comentário por mencioná-lo talvez ele vai ser capaz de esclarecê-lo como ele é um empregado Magento Inc Eu acho
Raphael em Digital pianismo
Bem, finalmente atualizei meu post original. Obrigado Raphael por sua ajuda.
Nicolas PERNOT 8/11
Vejo que, pelo menos no Magento 2.2, isso é importante incluído na carga do ResourceModel, então não há problema em usar métodos ResourceModel diretamente, certo?
Jānis Elmeris
Atualmente, podemos carregar o Model com segurança usando o load()método do Modelo de Recurso . Modelo de Recursos chama métodos do modelo de próprio load()método: $model->beforeLoad() { $this->_beforeLoad() }e$model->afterLoad() { $this->_afterLoad() }
sergei.sss
-1

Eu acho que a seguinte declaração não é válida agora.

Why not using the resource model load

podemos encontrar Magento\Framework\EntityManager\Observerpasta todos os eventos.

Siva Kumar Koduru
fonte