当前位置: 技术文章>> magento2中的依赖注入原理以及使用方法介绍

文章标题:magento2中的依赖注入原理以及使用方法介绍
  • 文章分类: Magento
  • 13528 阅读
系统学习magento二次开发,推荐小册:《Magento中文全栈二次开发 》

本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。


许多流行的PHP框架实现了依赖注入系统——尽管说它们实现了自动依赖注入系统会更准确。在我们深入研究Magento的系统之前,我们要解释一下依赖注入要解决的问题。

### 了解依赖关系注入

理解依赖注入的最简单方法是通过示例。考虑以下PHP方法/伪代码

//Obviously contrived -- if only getting a price were this simple
public function getFormattedPrice($sku)
{
    $db  = new DBHandler;
    $row = $db->query('SELECT price FROM products WHERE sku = ?', $sku);
    $formatter = new PriceFormatter;
    return $formatter->asDollars($row['price']);
}

这是一个简化的示例,说明了获取产品价格的方法可能是什么样子的。从表面上看,这种方法没有错。它实例化数据库处理程序价格查询实例化格式化程序对象

使用格式化程序对象返回格式化价格

当其他人想要重用此方法时,问题就出现了。此方法现在依赖于特定类和特定类。即使你认为代码重用是一个不真实的行业神话,这两个依赖关系也使此方法更难在自动化框架中进行测试。您的测试框架现在依赖于与实际应用程序建立相同的数据库连接。DBHandlerPriceFormatter

此问题的解决方案是不要使用具有这些依赖项的方法。相反,您应该将依赖项注入到方法中。

public function getFormattedPrice($sku, $db, $formatter)
{
    $row = $db->query('SELECT price FROM products WHERE sku = ?', $sku);
    return $formatter->asDollars($row['price']);
}

重写后,此方法有两个新参数。这个想法是客户端程序员应该传入实例化的对象(即注入依赖项)。

这就是依赖注入的全部内容——维基百科条目有更多关于这个概念的例子,如果你能忍受 java 的例子,值得一读!

### 现代依赖注入

依赖注入是一个非常简单的概念,但如果你以前从未遇到过它,可能会有一些疑问在你的脑海中挠头。其中之一可能是这个

public function prepareDataForDisplay()
{
    //...
    $data             = new stdClass;
    $db              = new DBHandler;
    $formatter         = new PriceFormatter;
    $data['price']     = $this->getFormattedPrice($row['price']);
    //...
}
public function getFormattedPrice($sku, $db, $formatter)
{
    $row = $db->query('SELECT price FROM products WHERE sku = ?', $sku);
    return $formatter->asDollars($row['price']);
}

虽然我们已经替换了依赖项 - 我们真正所做的只是将它们转移到调用的方法(上面)的级别。因此,从概念上讲,依赖关系注入很简单,但是在哪里注入依赖关系以及在哪里实例化这些对象是悬而未决的。getFormattedPricegetFormattedPriceprepareDataForDisplay

这是许多PHP框架试图通过某种自动依赖注入来解决的问题。通过创建一个自动注入这些类型的依赖项的系统,框架消除了由谁以及如何注入依赖项的问题。如果这没有意义,请不要担心。在看了Magento的依赖注入的例子之后,它应该开始变得更有意义了。

### Magento 依赖注入

就像我们上次所做的那样,我们准备了一个包含一些示例代码的模块。该模块位于 GitHub 上,最简单的安装方法是手动下载最新版本。

如果您需要手动安装Magento扩展的帮助,我们之前的系列文章提供了完整的说明。

让我们通过运行以下命令来确保正确安装模块

$ php bin/magento ps:tutorial-object-manager-2

Hello Again World!

如果您看到“Hello Again World!”消息,您就可以开始了。

如果我们看一下实现命令的类,我们会看到以下内容。

#File: app/code/Pulsestorm/TutorialObjectManager2/Command/Testbed.php
protected function execute(InputInterface $input, OutputInterface $output)
{
    $manager = $this->getObjectManager();
    $helper = $this->getObjectManager()->create(
        '\Pulsestorm\TutorialObjectManager2\Model\Example');
    $output->writeln(
        $helper->sendHelloAgainMessage()
    );        
}

我们在该方法中所做的只是实例化一个对象并调用其方法来获取一些输出文本。如果你看看这个类的构造函数executePulsestorm\TutorialObjectManager2\Model\ExamplesendHelloAgainMessagePulsestorm\TutorialObjectManager2\Model\Example

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php    
public function __construct()
{
    $object = new Message; //
    $this->messageObject = $object;
}

我们看到该类实例化一个对象并将其分配给该属性。我们使用短类名,因为此文件位于命名空间中。如果您需要一些帮助来开始使用 PHP 命名空间,我们的入门是一个不错的选择。Pulsestorm\TutorialObjectManager2\Model\MessagemessageObjectMessagePulsestorm\TutorialObjectManager2\Model

然后,在方法sendHelloAgainMessage

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php
public function sendHelloAgainMessage()
{
    return $this->messageObject->getMessage();
}

我们使用对象返回文本消息。Message

我们的(人为的)类对对象具有硬编码的依赖关系。以下是Magento 2如何解决这个问题。打开类定义文件,让我们将构造函数替换为以下代码Pulsestorm\TutorialObjectManager2\Model\ExamplePulsestorm\TutorialObjectManager2\Model\MessageExample.php

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php
public function __construct(Message $message)
{
    $this->messageObject = $message;
}

我们在这里所做的是向构造函数添加一个名为的参数,然后将该参数分配给属性。换句话说,我们已将硬编码依赖项替换为参数。这允许开发人员注入依赖项。您还会注意到我们在参数中包含了一个类型提示。$messagemessageObject

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php
__construct(Message $message);

此类型提示强制开发人员传入一个对象,该对象要么是对象,要么是其祖先链。同样,我们使用了短类名。以下内容本来是等效的,但要冗长得多。Pulsestorm\TutorialObjectManager2\Model\MessagePulsestorm\TutorialObjectManager2\Model\MessageMessage

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php
__construct(\Pulsestorm\TutorialObjectManager2\Model\Message $message);

如果您不熟悉类型提示,我们准备了有关它们的入门。

如果这是一个传统的PHP应用程序,我们会用如下所示的代码注入依赖项

$dependency = new \Pulsestorm\TutorialObjectManager2\Model\Message;
$helper        = new \Pulsestorm\TutorialObjectManager2\Model\Example($dependency);

但是,对于Magento 2的对象系统,我们无法提供参数。

$helper = $this->getObjectManager()->create('Pulsestorm\TutorialObjectManager2\Model\Example');

那么我们应该怎么做呢?

这就是Magento 2的自动依赖注入系统的美妙之处。你不需要做任何事情。只需使用我们的新代码运行命令即可。

$ php bin/magento ps:tutorial-object-manager-2
Hello Again World!

相同的结果 — 即使我们没有做任何事情来传入参数。

这是自动依赖注入。在幕后,对象管理器将使用 PHP 的反射功能来查看类的类型提示/参数,自动为我们实例化对象,然后将其作为参数传递到构造函数中。__construct

第一次遇到它时,这似乎有点奇怪,但如果您不相信我们,只需向您的构造函数添加一些调试代码即可

#File: app/code/Pulsestorm/TutorialObjectManager2/Model/Example.php    
public function __construct(Message $message)
{
    var_dump(get_class($message));
    exit;
    $this->messageObject = $message;
}

重新运行该命令,您将看到打印出的类名。

$ php bin/magento ps:tutorial-object-manager-2
string(47) "Pulsestorm\TutorialObjectManager2\Model\Message"

一旦你克服了怪异,你可能会想知道为什么这更好。类型提示现在不就是硬编码依赖项吗?

不同之处在于对象管理器可以控制依赖项的实例化。作为模块开发人员,我们有很大的权力来改变对象管理器实例化注入的依赖项的方式。这是通过模块的文件(代表依赖注入)发生的。虽然Magento 2没有“类重写” - 它确实具有类似的,更强大的功能,所有这些都可以通过文件进行配置。在接下来的几篇文章中,我们将探讨所有这些选项,您将学习如何完全控制这些依赖项。etc/di.xmldidi.xml

这么长时间的对象管理器,我们几乎不认识你

在我们结束今天的比赛之前,还有最后一件事要做。如果你浏览Magento 2当前关于Magento 2对象系统的文档(通常标记为依赖注入),你会看到如下神秘的评论。

对象管理器必须仅在编写代码时存在,编写代码在引导过程的早期执行

此外,如果您一直密切关注Magento 2的开发,您就会知道曾经有一个静态工厂方法来抓取对象管理器实例,但该方法已被弃用并删除。

对象管理器类不适用于Magento 2中的日常使用。它(按照惯例)是为处理引导Magento的代码的系统级开发人员保留的。我们在这些教程中提供的方法是一个帮助程序,因此您可以了解对象管理器是什么。getObjectManager

如果您遵循Magento扩展开发的最佳实践,您将使用依赖注入来实例化几乎所有对象。起初这似乎是不可能的(我在2013年翻了个白眼),但随着Magento 2接近完成,越来越清楚这实际上是可能的。

我们将在以后的文章中介绍实现这一点的依赖关系注入功能。然而,这里有一些高层的保证,即“没有objet经理”并不像听起来那么疯狂。

Magento中的所有对象都使用对象管理器进行实例化 - 包括依赖项。换句话说,使用注入注入的对象本身可以具有具有自动注入依赖项的方法。__construct__construct

数据管理对象(我们过去称之为“ORM模型”)可以通过工厂对象实例化 - 这些工厂对象可以通过依赖注入注入。

如果它一直向下是依赖注入,您可能想知道堆栈顶部的内容。但请记住,所有有效的代码入口点(控制器操作方法、观察者对象/方法、块对象、Symfony 控制台命令等)都是通过配置创建的。这意味着Magento知道它们,当它需要实例化它们时,它使用对象管理器。这意味着所有这些对象都有注入__construct

尽管不建议这样做,并且可能会在未来的版本中消失,但到目前为止,有一种方法可以获取对象管理器的实例

关于最后一项?查看基类Pulsestorm\TutorialObjectManager2\Command\AbstractCommand

#File: app/code/Pulsestorm/TutorialObjectManager2/Command/AbstractCommand.php
use \Magento\Framework\ObjectManagerInterface;
//...
public function __construct(ObjectManagerInterface $manager)
{
    $this->objectManager = $manager;
    parent::__construct();
}
protected function getObjectManager()
{
    return $this->objectManager;
}

这是我们教程模块的 cli 类的基类,也是实现的位置。我们如何获得对象管理器的实例?当然,通过使用依赖注入!getObjectManager

#File: app/code/Pulsestorm/TutorialObjectManager2/Command/AbstractCommand.php    
public function __construct(ObjectManagerInterface $manager)

这种模式可能需要一点时间来适应,并且可能看起来有点矫枉过正。但是,在企业环境中,一致性比本垒打更重要。沿着这条道路限制日常开发,并将更多的创造性工作留给深层次的系统开发人员,一个专注于敏捷的团队可以更好地完成最后期限并计算其速度。尽管它们很无聊和企业化,但这些设计模式是确保Magento 2稳步,无情地向发布迈进的重要组成部分。


推荐文章