Quoi et pourquoi est la bonne façon de charger un modèle

9

J'ai beaucoup d'expérience avec Magento mais je me suis rendu compte que je ne comprends pas quelle manière de charger un modèle est la bonne et pourquoi. J'ai lu tout ce que je pouvais sur le sujet, mais les gens qui expliquent des choses comme ça ne vont jamais assez loin pour expliquer pourquoi utiliser cette méthode spécifique au lieu d'une autre. Supposons qu'il n'y ait pas de référentiel pour le modèle que je souhaite charger.

Jusqu'à maintenant, j'utilisais toujours le modèle dans le constructeur, puis je le chargeais simplement.

public function __construct(
    \Vendor\Module\Model\Something $somethingModel
) {
    $this->somethingModel = $somethingModel;
}

public function getTestById($id) {
    return $this->somethingModel->load($id);
}

Et cela a toujours fonctionné comme prévu, je suis aussi presque sûr qu'il est ou du moins a été utilisé couramment dans le noyau.

Mais j'ai vu un de mes collègues utiliser

modelFactory->create()->load($id)

Pour autant que je sache, les usines sont utilisées pour créer une nouvelle entité, par exemple, si je voulais créer un nouveau produit, je peux créer l'usine, la remplir avec des données, puis l'enregistrer. Mais là encore, j'ai commencé à faire des recherches sur le sujet et j'ai vu l'exemple de Fabian Schmengler ( Quand devrions-nous utiliser un référentiel et une usine dans Magento 2? ) Qui chargeait le modèle de cette façon et a également découragé les autres de simplement charger les modèles, il ne l'a pas fait. t expliquer pourquoi, en plus de dire que cela «ne fait pas partie du contrat de service». Pour autant que je sache, les référentiels font partie des contrats de service, donc je ne vois aucune connexion ici quand il s'agit de charger des modèles qui ne sont pas disponibles via un référentiel.

Pour ajouter encore plus de confusion, j'ai également trouvé un moyen de charger le modèle en obtenant le resourceModel à partir de modelFactory créé, il a été présenté par Vinai Kopp ( Comment implémenter un contrat de service pour un module personnalisé dans Magento 2? ) Et maintenant je suis complètement perdu car j'ai toujours lu que je ne devais pas utiliser directement les modèles de ressources.

Alors oui, quelqu'un pourrait-il me dire quelle est la bonne façon et pourquoi je devrais l'utiliser à la place de toutes les autres méthodes?

czs
la source
Je lie littéralement ce fil comme contenant un exemple déroutant, avez-vous même lu mon message?
czs
1
Bonne question, je vais essayer de trouver le temps de répondre en détail plus tard. Je peux déjà vous en dire beaucoup: c'est un cas différent si vous chargez vos propres modèles (par exemple Vinai) ou des modèles de modules de base ou tiers (ma réponse). En outre, l'injection du modèle via le constructeur vous donnera la même instance à chaque fois, ce qui peut entraîner des effets secondaires indésirables.
Fabian Schmengler

Réponses:

12

Eh bien, la première étape que vous devez vérifier pour le modèle en question est la suivante: existe-t-il un contrat de service de référentiel? Si tel est le cas, utilisez-le, car les contrats de service sont liés au contrôle de version sémantique et continueront de se comporter comme ils le devraient jusqu'à la sortie de Magento 3.x. Inutile de dire que lorsque vous créez vos propres modules avec des modèles qui nécessitent de la persistance, vous devez également écrire le référentiel pour cela.

public function __construct(
    \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
) {
    $this->productRepository = $productRepository;
    /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
    $this->productRepository->save($product);
}

Si aucun référentiel n'est présent, utilisez le modèle de ressource . Notez que les modèles de ressources ne contiennent pas d'état: ils utilisent la persistance pour leurs modèles «réguliers». Par conséquent, vous n'êtes pas obligé de les inclure dans une usine:

public function __construct(
    \Magento\Catalog\Model\ResourceModel\Product $productResource,
    \Magento\Catalog\Model\ProductFactory $productFactory
) {
    $this->productResource = $productResource;
    $this->productFactory = $productFactory;
    ...
    /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
    $product = $this->productFactory->create();
    $this->productResource->save($product);
}

"Alors, quel avantage apporte un contrat de service / référentiel sur un modèle de ressource?" vous pourriez demander. Eh bien, en théorie, un modèle de ressource ne devrait être responsable que de la persistance d'un modèle de données , tandis qu'un référentiel prend également en compte les tâches supplémentaires impliquées lors de l'enregistrement d'une entité. Pensez à mettre à jour les index, à créer des relations avec d'autres entités, etc. C'est la théorie, bien que dans la vie réelle ces lignes aient tendance à s'estomper assez souvent. Mais c'est bon pour vous de garder cela à l'esprit.

Vous ne devriez pas utiliser les modèles directs save(), load()etc. -méthodes. Ils sont obsolètes parce que c'est sémantique incorrect. Pensez-y de manière SOLIDE:

  • (Données) Les modèles ne devraient être responsables que de la conservation des données.
  • Les modèles de ressources devraient être responsables de la persistance de ces données.
  • Les référentiels doivent être responsables de la communication à l'intérieur et à l' extérieur du module pour les actions de persistance.

Et c'est ce dernier point qui fait la différence: lors de la communication avec d'autres modules, dans un monde idéal, on ne devrait jamais avoir à s'appuyer sur la logique persistante interne de ces modules (ou sur l'une de ses méthodes publiques d'ailleurs, mais c'est une autre discussion), mais utilisez uniquement la fonctionnalité fournie par les contrats de service des modules .

En conclusion

Pour répondre à votre question: par ordre de préférence. La bonne façon de charger un modèle est:

  • S'il existe un référentiel, chargez-le à l'aide du référentiel.
  • Seulement s'il n'y a pas de référentiel, utilisez le modèle de ressource (en combinaison avec une usine).
Giel Berkers
la source
1
Ok, donc si je suis correctement - quand je veux modifier / ajouter de nouvelles données et les enregistrer dans la base de données, alors je dois utiliser le modèle de ressource et quand je veux charger des données en mémoire, alors je dois utiliser Factory? Y a-t-il donc une situation dans laquelle je devrais utiliser directement un modèle standard (comme dans l'utilisation d'une classe Model dans un constructeur)?
czs
@czs Vous avez raison, j'ai ajouté un exemple plus descriptif pour le chargement de modèle pour le même.
Milind Singh
2
  • ModelsLes interfaces de données sont utilisées pour conserver uniquement les données dans les objets, c'est-à-dire vers setet les getdonnées d'une ligne.
  • ResourceModelssont un mécanisme qui est responsable de la persistance de ces données, c'est-à-dire exécuter la requête SQL vers réellement saveou des loaddonnées dans l' Modelobjet.

La façon correcte loadet savedevrait être de créer un référentiel ou de charger à partir d'une ressource comme suit:

namespace MyVendor\MyModule\Model;

class QueueRepository impliments \MyVendor\MyModule\Api\QueueRepositoryInterface
{

    /** @var \MyVendor\MyModule\Model\ResourceModel\Queue  */
    public $resource;

    /** @var \MyVendor\MyModule\Model\QueueFactory  */
    public $modelFactory;

    public function __construct(
        \MyVendor\MyModule\Model\ResourceModel\Queue $resource,
        \MyVendor\MyModule\Model\QueueFactory $modelFactory
    ) {
        $this->resource = $resource;
        $this->modelFactory = $modelFactory;
    }

    /**
     * Save
     * @param \MyVendor\MyModule\Api\Data\QueueInterface $queue
     * @return $queue
     * @throws \Exception
     */
    public function save(\MyVendor\Integrator\Api\Data\QueueInterface $queue)
    {
        $this->resource->save($queue);
        return $queue;
    }

    /**
     * Save
     * @param \MyVendor\MyModule\Api\Data\QueueInterface $queue
     * @param int $id
     * @return $queue
     * @throws \Exception
     */
    public function load(\MyVendor\MyModule\Api\Data\QueueInterface $queue, $id)
    {
        $this->resource->load($queue, $id);
        return $queue;
    }

    public function getById($id)
    {
        $queue = $this->modelFactory->create();
        $this->resource->load($queue, $id);
        return $queue;
    }
}

Ici, \MyVendor\MyModule\Api\Data\QueueInterfaceest implémenté par QueueModel.

Donc, dans les coulisses, nous créons en fait un Modelobjet puis loadingpar l' ResourceModelobjet. C'est la bonne façon de charger ou d'enregistrer.

        $queue = $this->modelFactory->create();
        $this->resource->load($queue, $id);
        return $queue;
Milind Singh
la source