在Magento 2中加载自定义模型的最佳方法


15

由于我很难找到正确的方法,因此您可以在下面找到我制定的最佳实践。享受,如果需要,请更正我的英语,如果我是我,我就说我错了。:)

编辑: ...,我发现我在某些方面是错误的。因此,在Raphael的答案帮助我了解了更多信息之后,我更新了原始帖子。多亏他!

以下使用的概念

如果您熟悉以下概念,将更容易理解以下代码和说明:

  • 注入依赖性(因为$this->variable代码中的每个变量都被注入)
  • 服务合同和资料库

内容

只是为了获得更多的上下文,假设我们有一个可以正确构造的模块:

  • 包含方法的块类CustomBlock getCustomModel($id)
  • 此方法根据在参数中传递的ID返回CustomModel对象,
  • CustomModel类型对应于 \Vendor\Module\Model\CustomModel
  • 此模型带有其资源模型(在中\Vendor\Module\Model\ResourceModel\CustomModel
  • 及其存储库(在中\Vendor\Module\Model\CustomModelRepository)。

问题

  • 让所有内容加载CustomModel对象的最佳实践是什么?

由于不建议使用load()此方法,因此不能使用CustomModel对象中的。

优良作法是您必须使用CustomModel服务合同。服务合同是数据接口(例如CustomModelInterface)和服务接口(例如CustomModelRepositoryInterface)。所以我的方块看起来像这样:

/ ** @var SlideRepositoryInterface * /
受保护的$ slideRepository;

/ **
 * CustomBlock构造函数
 * ...
 * @param CustomModelRepositoryInterface $ customModelRepository
 * ...
 * /
公共功能__construct(
...
CustomModelRepositoryInterface $ customModelRepository
...
){
    $ this-> customModelRepository = $ customModelRepository;
}

公共功能getCustomModel($ id){
    返回$ this-> customModelRepository-> get($ id);
}

首先,我们将CustomModelRepositoryInterface对象注入构造函数中,并在getCustomModel()方法中使用它。

在课堂Api\CustomModelRepositoryInterface上没有很多。通常(但没有阻止你做不同的),你会声明基本方法:getgetListsavedeletedeleteById。出于本主题的目的,下面仅是get方法声明:

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

好的,但是如果我的CustomModel接口是通过我的块构造函数中的依赖项注入调用的,那么代码在哪里?为了回答这个问题,您必须向Magento解释,在哪里可以找到实现此接口的类。在模块的etc / di.xml文件中,您必须添加:

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

因此,CustomModelRepositoryInterface类是服务接口。在实现它时,您还必须实现数据接口(至少Vendor\Module\Api\Data\CustomModelInterfaceVendor\Module\Api\Data\CustomModelSearchResultsInterface)。您的模型将必须为每个接口实现Vendor\Module\Api\Data\CustomModelInterface并添加<preference ... />行。最后,无论何时使用服务合同,mySomethingInterface都不mySomething要再考虑:让magento使用di.xml首选项机制。

好吧,接下来会发生什么?当我们注入CustomModelRepositoryInterface块构造器时,我们得到一个CustomModelRepository对象。CustomModelRepository必须实现中声明的方法CustomModelRepositoryInterface。因此,我们在Vendor\Module\Model\CustomModelRepository

公共功能get($ id){
    $ customModel = $ this-> customModelFactory-> create();
    $ customModel-> load($ id);
    如果(!$ customModel-> getId()){
      抛出新的NoSuchEntityException(__('ID为“%1”的CustomModel不存在。',$ id));
    }
    返回$ customModel;
}

我们在做什么?CustomModel由于工厂,我们创建了一个空对象。接下来,我们CustomModel使用加载模型方法加载数据。接下来,NoSuchEntityException如果我们无法CustomModel使用参数加载id,则返回a 。但是,如果一切正常,我们将返回模型对象,并且生命将继续。

但是,哇...!在这个例子中是什么?

$customModel->load($id);

不建议使用load与开始时相同的方法吗?是的。我认为这很遗憾,但是您必须使用它,因为在此load()方法中调度了一些事件,开发人员可以侦听它们(请参阅下面的Raphael的回答)。

将来,我们将由实体管理器保存。作为新的Magento 2概念,这是另一个故事,但是如果您不介意,则CMS页面(v2.1)的资源模型中已经实现了Entity Manager:

public function load(AbstractModel $object, $value, $field = null)
{
    $pageId = $this->getPageId($object, $value, $field);
    if ($pageId) {
        $this->entityManager->load($object, $pageId);
    }
    return $this;
}

Answers:


16

最佳实践:通过服务合同

最佳实践始终是尽可能使用服务合同。您可以在此处找到原因列表:Magento 2:使用服务合同的好处是什么?

有关如何实施服务合同的详细信息,建议您检查以下主题:如何在Magento 2中为自定义模块实施服务合同?

如果没有可用的服务合同

如果没有可用的服务合同,则应使用模型存储库get方法。使用此方法,您可以从magento缓存系统中受益,例如用于CategoryRepository该类:

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()方法

Magento 2通过删除继承系统并使用新的2.1 EntityManager通过合成实现继承体系,逐渐远离标准CRUD系统,您可以在此处找到详细信息:Magento 2.1:使用实体管理器

我也建议您阅读有关不建议使用的CRUD方法的有趣话题:抽象模型中不建议使用的保存和加载方法

为什么不使用资源模型负载

主要原因是,如果您使用资源模型load方法,则将跳过模型load方法中实现的加载系统的重要部分,请参见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;
}

load直接调用资源模型方法将产生以下影响:

  • _beforeLoad 不会被调用:因此,在未调度事件之前加载模型
  • _afterLoad 不被调用:因此事件未调度后的模型加载
  • 存储的数据未更新,可能导致各种问题(例如,如果您prepareDataForUpdate从调用Magento\Framework\Model\ResourceModel\Db\AbstractDb

谢谢拉斐尔,您所说的每件事都说得通,并且使我的知识更加完整。但是我不明白为什么KAndy评论(在他的回答下)Marius可以使用其自定义模块资源模型的load()方法?在[ magento.stackexchange.com/questions/114929/…中,保存和加载抽象模型中的方法]。有任何想法吗 ?
Nicolas PERNOT '16

@NicolasPERNOT基本上,KAndy解释说,目标是每个模块都有SL(服务层),并且这是每次需要加载实体时都必须使用的功能。我建议您通过提及他来发表评论,因为他是我认为是Magento Inc的员工,也许可以启发您
数码钢琴家Raphael在

好吧,我终于更新了我的原始帖子。谢谢Raphael的帮助。
Nicolas PERNOT '16

我看到至少在Magento 2.2中,那个重要的事情已经包含在ResourceModel的负载中,所以直接使用ResourceModel方法不是正确的,对吧?
贾尼斯·埃默里斯(JānisElmeris)

当前,我们可以使用资源模型load()方法安全地加载模型。资源模型从自己的load()方法中调用模型的方法:$model->beforeLoad() { $this->_beforeLoad() }$model->afterLoad() { $this->_afterLoad() }
sergei.sss

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.