系统学习magento二次开发,推荐小册:《Magento中文全栈二次开发 》
本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。
与以前的版本相比,Magento 2提出了依赖注入的新概念,其中类为对象注入依赖关系(不同的对象),而不是该对象在内部手动创建它们。这样覆盖和操作类就容易得多,并允许我们更多扩展本机功能的方法。
必须在类中注入哪些依赖项由文件控制。每个模块都可以有一个全局和特定于区域的文件,可以根据范围使用该文件。模块 di.xml 文件的路径:di.xmldi.xml
<moduleDir>/etc/di.xml
<moduleDir>/etc/<area>/di.xml
请务必注意,覆盖块、模型、帮助程序、控制器或其他内容之间没有更多区别。它们都是可以重写的类。我们将介绍扩展本机Magento类和方法的三种不同方法。
### 类首选项
让我们称之为覆盖我们习惯的类的老式方式,但略有不同。所有类都由其接口定义,并由 di.xml 文件配置。当类的构造函数签名通过其接口请求对象时,可以实现抽象实现映射。这意味着应该在可用的情况下使用接口,并且映射将告诉应该启动哪个类。
让我们看一下目录模块的 di.xml 文件中如何定义目录产品类:
<config> <preference for="Magento\Catalog\Api\Data\ProductInterface" type="Magento\Catalog\Model\Product" /> </config>
要覆盖类,我们只需要定义我们的首选项并创建一个将扩展原始类的文件:Magento\Catalog\Model\Product
<config> <preference for="Magento\Catalog\Api\Data\ProductInterface" type="Inchoo\Catalog\Model\Product" /> </config>
<?php namespace Inchoo\Catalog\Model; class Product extends \Magento\Catalog\Model\Product { // code }
为了确保模块依赖项的顺序正确,应该为示例中的Magento_Catalog定义模块序列。etc/module.xml
### 插件
如果多个类扩展同一原始类,则按类首选项重写可能会导致冲突。为了帮助解决这个问题,引入了插件的新概念。插件扩展了方法,并且不会像通过类首选项重写那样更改类本身,而是在其调用之前、之后或周围拦截方法调用。
插件在 di.xml 文件中配置,它们在被覆盖的方法之前、之后或周围调用。第一个参数始终是观察方法名称的对象,后跟原始方法的参数。
作为示例,我们将扩展目录产品模块中的一些方法。这是 di.xml 文件的外观:
<config> <type name="Magento\Catalog\Api\Data\ProductInterface"> <plugin name="inchoo_catalog_product" type="Inchoo\Catalog\Plugin\Model\Product" /> </type> </config>
### before方法
在插件之前在观察到的方法之前运行,并且必须在数组中返回该方法接受的相同数量的参数或 null – 如果不应修改该方法。要扩展的方法必须具有相同的名称,前缀为“before”。
<?php namespace Inchoo\Catalog\Plugin\Model; class Product { public function beforeSetPrice(\Magento\Catalog\Model\Product $subject, $price) { $price += 10; return [$price]; } }
after方法
在调用原始方法后执行方法之后。在类对象旁边,该方法接受另一个参数,这也是必须返回的结果。要扩展的方法必须具有相同的名称,前缀为“after”。
<?php namespace Inchoo\Catalog\Plugin\Model; class Product { public function afterGetName(\Magento\Catalog\Model\Product $subject, $result) { $result .= ' (Inchoo)'; return $result; } }
around方法
Around 方法包装原始方法,并允许在原始方法调用之前和之后执行代码。在类对象旁边,该方法接受另一个参数接收是可调用的,允许链中的其他插件调用。要扩展的方法必须具有相同的名称,前缀为“around”。
<?php namespace Inchoo\Catalog\Plugin\Model; class Product { public function aroundSave(\Magento\Catalog\Model\Product $subject, \callable $proceed) { // before save $result = $proceed(); // after save return $result; } }
使用插件看起来像是覆盖方法的理想解决方案,但它有局限性。插件不能用于所有类型的方法,在尝试扩展以下方法时,必须寻找其他解决方案:
在 Magento\Framework\Interception 引导之前实例化的对象
### 最终方法
包含至少一个最终公共方法的任何类
非公共方法
类方法(如静态方法)
__construct
虚拟类型
构造函数参数
di.xml 配置哪些依赖项将被注入到类中,这意味着它们可以被控制并更改为对我们有用的东西。如果更改不需要是全局的,而是针对特定类的,
那么我们可以配置类接收的参数,而不是覆盖整个类或为不同的方法创建插件。
### 类型配置
目录产品模块接收的参数之一是帮助程序。使用 di.xml我们可以配置为改用我们的助手:\Magento\Catalog\Helper\Product $catalogProduct
<config> <type name="Magento\Catalog\Api\Data\ProductInterface"> <arguments> <argument name="catalogProduct" xsi:type="object">Inchoo\Catalog\Helper\Product</argument> </arguments> </type> </config>
允许不同的参数类型,具体取决于要更改的内容。允许的类型为 、、、、 和 。objectstringbooleannumberconstnullarrayinit_parameter
### 虚拟类型配置
在文档中,虚拟类型被定义为一种类型,它允许您更改特定可注入依赖项的参数并更改特定类的行为。
如果我们回到帮助程序示例,而不是注入新的帮助程序,有时只更改原始帮助程序中的一个参数就可以完成工作,并且创建新文件是多余的。
在我们的示例中,这意味着从 创建一个虚拟类型,更改其参数并将该虚拟帮助程序用作目录产品类的参数。Magento\Catalog\Helper\Product
<config> <virtualType name="virtualHelper" type="Magento\Catalog\Helper\Product"> <arguments> <argument name="catalogSession" xsi:type="object">Inchoo\Catalog\Model\Session\Proxy</argument> </arguments> </type> <type name="Magento\Catalog\Api\Data\ProductInterface"> <arguments> <argument name="catalogProduct" xsi:type="object">virtualHelper</argument> </arguments> </type> </config>
当只需要更改依赖项的构造参数而不创建任何其他文件,只需在 xml 文件中配置它时,虚拟类型会派上用场。
覆盖类和方法的方法不止一种,选择使用哪一种方法将取决于您遇到的情况。
虽然使用类首选项作为覆盖方式可能看起来是在大多数情况下都有效的最简单方法,但当不同的模块尝试覆盖相同的类和相同的方法时,
这是许多冲突的原因,这就是为什么应该考虑所有方法的原因。