Meilleure façon de charger un modèle personnalisé dans Magento 2


15

Parce qu'il était difficile pour moi de trouver le bon chemin, vous pouvez trouver ci-dessous la meilleure pratique que j'ai faite. Profitez-en, corrigez mon anglais si nécessaire et dites-moi que je me trompe si je le suis. :)

Edit: ... et j'ai découvert que j'avais tort sur certains aspects. J'ai donc mis à jour le message d'origine après que les réponses de Raphaël m'ont aidé à mieux comprendre. Grâce à lui !

Concept utilisé ci - dessous :

Il vous sera plus facile de comprendre les codes et les explications ci-dessous si vous êtes à l'aise avec ces concepts:

  • Dépendance à l'injection (car toutes les $this->variablevariables des codes sont injectées)
  • Contrat de service et référentiel
  • Usine

Contexte :

Juste pour avoir plus de contexte, imaginez que nous avons un module correctement construit avec:

  • une classe de bloc CustomBlock contenant une méthode getCustomModel($id),
  • cette méthode renvoie un objet CustomModel basé sur l'ID passé dans param,
  • Le type CustomModel correspond au modèle dans \Vendor\Module\Model\CustomModel
  • Ce modèle est livré avec son modèle de ressource (en \Vendor\Module\Model\ResourceModel\CustomModel)
  • et avec son référentiel (en \Vendor\Module\Model\CustomModelRepository).

Question :

  • Quelle est la meilleure pratique pour laisser le tout charger un objet CustomModel?

Vous ne pouvez pas utiliser le à load()partir d'un objet CustomModel car cette méthode est déconseillée.

La bonne pratique indique que vous devez utiliser le contrat de service CustomModel. Les contrats de service sont des interfaces de données (par exemple CustomModelInterface) et des interfaces de service (par exemple CustomModelRepositoryInterface). Donc, mon bloc ressemble à ceci:

/ ** @var SlideRepositoryInterface * /
$ slideRepository protégé;

/ **
 * Constructeur CustomBlock
 * ...
 * @param CustomModelRepositoryInterface $ customModelRepository
 * ...
 * /
fonction publique __construct (
...
CustomModelRepositoryInterface $ customModelRepository
...
) {
    $ this-> customModelRepository = $ customModelRepository;
}

fonction publique getCustomModel ($ id) {
    return $ this-> customModelRepository-> get ($ id);
}

Tout d'abord, nous injectons l' CustomModelRepositoryInterfaceobjet dans le constructeur et nous l'utilisons dans notre getCustomModel()méthode.

Dans la classe, Api\CustomModelRepositoryInterfaceil n'y en a pas beaucoup. En général (mais rien ne vous empêche de le faire différemment) vous déclarer des méthodes de base: get, getList, save, delete, deleteById. Aux fins de cette rubrique, voici simplement la getdéclaration de méthode:

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

Ok, mais si mon interface CustomModel est appelée par injection de dépendance dans mon constructeur de bloc, où est le code? Pour répondre à cette question, vous devez expliquer à Magento où trouver la classe implémentant cette interface. Dans le fichier etc / di.xml du module, vous devez ajouter:

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

La CustomModelRepositoryInterfaceclasse est donc une interface de service. En l'implémentant, vous devrez implémenter également des interfaces de données (au moins Vendor\Module\Api\Data\CustomModelInterfaceet Vendor\Module\Api\Data\CustomModelSearchResultsInterface). Votre modèle devra implémenter Vendor\Module\Api\Data\CustomModelInterfaceet ajouter des <preference ... />lignes pour chacune de vos interfaces. Enfin, à chaque fois que vous utilisez un contrat de service, ne pensez mySomethingInterfaceplus mySomething: laissez magento utiliser le di.xmlmécanisme des préférences.

Ok, qu'est-ce qui vient ensuite? Lorsque nous injectons CustomModelRepositoryInterfacedans le constructeur de blocs, nous obtenons un CustomModelRepositoryobjet. CustomModelRepositorydoit implémenter la méthode declare in CustomModelRepositoryInterface. Nous avons donc ceci dans Vendor\Module\Model\CustomModelRepository:

fonction publique get ($ id) {
    $ customModel = $ this-> customModelFactory-> create ();
    $ customModel-> load ($ id);
    if (! $ customModel-> getId ()) {
      lever une nouvelle NoSuchEntityException (__ ('CustomModel avec l'ID "% 1" n'existe pas.', $ id));
    }
    return $ customModel;
}

Que faisons-nous? Nous créons un CustomModelobjet vide grâce à l'usine. Ensuite, nous chargeons les données en CustomModelutilisant la méthode du modèle de charge. Ensuite, nous renvoyons un NoSuchEntityExceptionsi nous n'avons pas réussi à charger CustomModell'identifiant dans les paramètres. Mais si tout va bien, nous retournons l'objet modèle et la vie continue.

Mais wow ...! Dans cet exemple, qu'est-ce que c'est?

$customModel->load($id);

N'est-ce pas la même loadméthode obsolète qu'au début? Oui, ça l'est. Je pense que c'est dommage, mais vous devez l'utiliser car dans cette méthode load () il y a des événements distribués et le développeur pourrait les écouter (voir la réponse de Raphael ci-dessous).

À l'avenir, nous serons enregistrés par Entity Manager. C'est une autre histoire en tant que nouveau concept Magento 2, mais si vous voulez jeter un œil, Entity Manager est déjà implémenté dans le modèle de ressource de la page 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;
}

Réponses:


16

Bonne pratique: via le contrat de service

La meilleure pratique consiste à toujours utiliser le contrat de service chaque fois que cela est possible. Vous pouvez trouver la liste des raisons ici: Magento 2: quels sont les avantages de l'utilisation des contrats de service?

Pour plus de détails sur la façon d'implémenter un contrat de service, je vous suggère de vérifier cette rubrique: Comment implémenter un contrat de service pour un module personnalisé dans Magento 2?

Si aucun contrat de service n'est disponible

Si aucun contrat de service n'est disponible, vous devez utiliser la getméthode de référentiel modèle . En utilisant cette méthode, vous bénéficiez du système de mise en cache magento par exemple pour la 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éthode obsolète

Magento 2 s'éloigne lentement du système CRUD standard en supprimant le système d'héritage et en l'implémentant via la composition à l'aide du nouveau EntityManager 2.1, vous pouvez trouver des détails ici: Magento 2.1: à l'aide du gestionnaire d'entités

Je vous suggère également de lire ce sujet intéressant sur les méthodes CRUD obsolètes: méthodes d' enregistrement et de chargement obsolètes dans le modèle abstrait

Pourquoi ne pas utiliser la charge du modèle de ressource

La raison principale est que si vous utilisez la loadméthode du modèle de ressource , vous allez ignorer une partie importante du système de chargement qui est implémentée dans la loadméthode du modèle , voir 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;
}

L'appel loaddirect de la méthode du modèle de ressource aura l'impact suivant:

  • _beforeLoad n'est pas appelé: ainsi la charge du modèle avant que les événements ne soient distribués
  • _afterLoad n'est pas appelé: ainsi la charge du modèle après que les événements ne soient pas distribués
  • les données stockées ne sont pas mises à jour, ce qui peut entraîner divers problèmes (par exemple si vous appelez prepareDataForUpdatedepuis Magento\Framework\Model\ResourceModel\Db\AbstractDb)

Merci Raphael, tout ce que tu dis a du sens et complète mes connaissances. Mais je ne comprends pas pourquoi KAndy commente (sous sa réponse) que Marius peut utiliser la méthode load () de son modèle de ressource de module personnalisé? C'est dans [ magento.stackexchange.com/questions/114929/… les méthodes de sauvegarde et de chargement dans le modèle abstrait). Des idées ?
Nicolas PERNOT

@NicolasPERNOT essentiellement KAndy explique que l'objectif est d'avoir SL (Service Layer) pour chaque module et que c'est ce qui doit être utilisé chaque fois que vous avez besoin de charger une entité. Je vous suggère de commenter en le mentionnant peut-être qu'il pourra vous éclairer car il est un employé de Magento Inc je pense
Raphael au Digital Pianism

Eh bien, j'ai finalement mis à jour mon message d'origine. Merci Raphael pour ton aide.
Nicolas PERNOT

Je vois qu'au moins dans Magento 2.2 cet important est inclus dans la charge de ResourceModel, donc ce n'est pas OK d'utiliser les méthodes ResourceModel directement, non?
Jānis Elmeris

Actuellement, nous pouvons charger le modèle en toute sécurité à l'aide de la load()méthode du modèle de ressource . Le modèle de ressource appelle les méthodes du modèle à partir de sa propre load()méthode: $model->beforeLoad() { $this->_beforeLoad() }et$model->afterLoad() { $this->_afterLoad() }
sergei.sss

-2

Je pense que la déclaration suivante n'est pas valable maintenant.

Why not using the resource model load

nous pouvons trouver le Magento\Framework\EntityManager\Observerdossier de tous les événements.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.