什么以及为什么是加载模型的正确方法


9

我在Magento方面有很多经验,但是我意识到我不知道哪种加载模型的方法是正确的,为什么。我已经阅读了关于该主题的所有内容,但是人们对此类内容的解释实际上并没有深入到足以解释的原因,为什么要使用此特定方法而不是其他方法。假设没有要加载的模型的存储库。

直到现在,我一直在构造器中使用模型,然后简单地加载它。

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

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

而且它总是按预期工作,我也很确定它至少已在内核中使用。

但是后来我看到一位同事在使用

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

据我了解,工厂正被用来创建一个新的实体,例如,如果我想创建一个新产品,那么我可以创建工厂,用数据填充它,然后保存它。但是话又说回来,我开始研究这个话题,并从Fabian Schmengler(我们何时应该在Magento 2中使用存储库和工厂?)中看到了一个示例,他以这种方式加载模型,并且不鼓励其他人简单地加载模型,他没有这样做。除了解释为什么它不是“服务合同的一部分”之外,还要说明原因。据我了解,存储库是服务合同的一部分,因此在加载通过存储库不可用的模型时,在这里看不到任何联系。

为了增加一些混乱,我还找到了一种通过从创建的modelFactory中获取resourceModel来加载模型的方法,该方法由Vinai Kopp提出(如何在Magento 2中实现自定义模块的服务合同?),现在就像我一直读到的那样,我不应该直接使用资源模型而完全迷失了方向。

是的,有人可以告诉我哪种方法正确,为什么我应该用它代替所有其他方法?


我实际上是将此线程链接为包含令人困惑的示例,您甚至还阅读了我的文章?
czs

1
好问题,稍后我将尝试寻找时间详细回答。我已经告诉您很多:如果您加载自己的模型(例如Vinai)或核心模块或第三方模块的模型(我的回答),情况就不同了。此外,通过构造函数注入模型每次都会为您提供相同的实例,这可能会导致不良的副作用。
Fabian Schmengler '17

Answers:


12

好了,您应该检查所涉及模型的第一步是:是否有存储库服务合同?如果是这样,请使用它,因为服务合同已绑定到语义版本控制,并且将继续表现出应有的状态,直到Magento 3.x出现为止。不用说,当使用需要持久性的模型创建自己的模块时,还应该为此编写存储库。

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

如果没有存储库,请使用资源模型。请注意,资源模型不包含状态:它们将持久性用于其“常规”模型。因此,不需要使用工厂将它们包括在内:

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

“那么,给资源模型带来服务合同/存储库带来了什么好处呢?” 你可能会问。好吧,从理论上讲,资源模型仅应负责数据模型的持久性,而存储库还应考虑保存实体时涉及的其他任务。考虑更新索引,与其他实体建立关系,等等。这是理论,尽管在现实生活中这些界限往往会模糊不清。但是记住这一点对您自己有好处。

不应该使用模型的直接save()load()等等-方法。不推荐使用它们,因为它的语义不正确。想一想:

  • (数据)模型仅应负责包含数据。
  • 资源模型应负责此类数据的持久性。
  • 存储库应负责模块内部和外部的持久性操作通信。

最后一点与众不同:在与其他模块通信时,在理想的世界中,永远不必依赖于模块内部的持久逻辑(或与此相关的任何公共方法,但这是另一种讨论),但只能使用模块的服务合同提供的功能。

结论

回答您的问题:按优先顺序排列。加载模型的正确方法是:

  • 如果有存储库,请使用存储库加载它。
  • 仅当没有存储库时,才使用资源模型(与工厂结合使用)。

1
好的,所以,如果我正确地遵循-当我想修改/添加新数据并将其保存到数据库时,我应该使用资源模型,而我想将数据加载到内存中则应该使用Factory?那么,在任何情况下我都应该直接使用常规Model(例如在构造函数中使用Model类)?
czs

@czs是的,我为模型加载添加了更具描述性的示例。
米林德·辛格

2
  • Models被数据接口被用来只保持在对象,即到数据setget数据的行。
  • ResourceModels是负责此类数据持久性的机制,即实际执行SQL查询save或将load数据导入Model对象。

正确的方法loadsave应该通过创建一个存储库从资源加载,如下所示:

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

在此,\MyVendor\MyModule\Api\Data\QueueInterfaceQueueModel 隐含。

因此,在幕后,我们实际上是在创建一个Model对象,然后loading由该ResourceModel对象创建它。这是加载或保存的正确方法。

        $queue = $this->modelFactory->create();
        $this->resource->load($queue, $id);
        return $queue;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.