使用插件将Magento 1模块移植到Magento 2
使用插件将Magento 1模块移植到Magento 2

使用插件将Magento 1模块移植到Magento 2

2015年8月13日发布 in 发展历程
使Magento代码更具可移植性
使您的Magento代码更便携
2015年7月28日
期待Magento 2会期待什么
当您期待什么’重新期待…Magento 2
2015年10月15日

项目概况

法师nto的最大优势之一就是其多商店功能。在继承较高范围值的同时覆盖给定网站或商店的数据和配置的功能特别强大,从而使商家和开发人员可以创建量身定制的体验,而无需管理完全不同的商店。

尽管功能非常强大且非常灵活,但为许多不同的网站和商店管理许多系统配置值可以轻松忽略在特定范围内覆盖值的情况。

当管理带有许多子类的大型代码库时,存在类似的问题。当方法被子类覆盖时,有时很容易忽略,浪费了开发时间。当方法被子类覆盖时,我一直是PHP Storm的忠实拥护者—简单的图标提供的可见性是不可思议的。

PHP Storm方法覆盖图标

在一个特别繁杂的网站上工作时,我发现自己同样在浪费时间,因为我无法注意到(或检查)系统配置值是否在更特定的范围内被覆盖,并希望我有类似的视觉提示。这促使我探索创建一个模块,该模块会将此类功能添加到系统配置中。

由于Magento 2 RC2的最新版本,我还想探索将此模块移植到Magento 2的方法。

 

法师nto 1模块

要求

从概念上讲,这是一个相对简单的添加。呈现每个字段时,请将其在当前范围内的值与更特定范围内的值进行比较。如果该字段的值在任何特定范围内都不相同,请在最右边的列中添加一个直观指示,其中包含详细信息以及指向覆盖范围的链接。

实作

与大多数将新功能注入Magento的现有区域的模块一样,该方法有两个主要部分:找到最合适的点来修改或与核心代码进行交互,然后使用该交互点来调用实现新功能的自定义逻辑。

互动点

经过几次调试会话后,我发现各个系统配置字段都没有模板或布局XML—我们将使用直接的PHP进行此修改。特别是,我需要在核心中找到一个点,其中(a)存在有关该字段的足够信息以执行值比较,并且(b)我还可以影响范围标签列的输出。

In general, 法师_Adminhtml_Block_System_Config_Form::initFields() loops over elements of a group, and encapsulates the entire rendering of a 领域 row. However, it doesn’t dispatch any events, nor 是 it well structured to subclass without copying the method contents 对于ward —实际上,重写此方法将需要向前复制200行以上的逻辑密集代码。这样,这是不希望的交互点。

法师_Adminhtml_Block_System_Config_Form::getScopeLabel()但是,似乎很有希望;一个简单的方法,该方法接受字段元素,确定其范围标签,然后将范围标签作为字符串返回。子类可以将内容添加到返回的字符串中,而不必向前复制任何代码。不幸的是,element参数没有足够的信息来获取字段的值,从而使该交互点无法用于我们的目的。

经过多方的努力寻找“surgical” 在 teraction point, I came to realize that, unfortunately, it would be necessary to rewrite 法师_Adminhtml_Block_System_Config_Form and copy its entire 在 itFields() method 对于ward. In an effort to avoid any more direct modifications than necessary, the 上ly change I made to 这个 method was to add an event before the element 是 rendered. This event, 在 turn, 是 observed by the 模组 to effect the desired 新 功能ality.

注意:这种兼容性问题很快就显现出来了—在EE 1.10(至少)上,配置作用域提示已显示且准确,但靠近它们的UI输入始终为空白。经过调查后,这是由于两个版本之间的initFields()不同。此时,协调不同版本非常耗时。

定制逻辑实施

确定了交互点后,必须实现实际的新功能。经过一番考虑,我决定根据以下逻辑构造封装的值覆盖检测,并给出配置路径和当前上下文作用域和作用域ID。

  1. 获取上下文范围内字段的精确值。
  2. 在当前作用域下生成作用域树。
  3. 遍历作用域树,获取每个精确作用域的值。
  4. 如果迭代作用域的值与上下文作用域的值不匹配,则将其添加到覆盖作用域的数组中。
  5. 最后,根据覆盖数组格式化输出HTML字符串。

The most complicated part of 这个 logic was reviewing the plethora of system 配置 methods available through the core, and determining which 是 the best 上e to 使用 对于 each of these steps. In particular, getting the precise 值 of a 路径 at a given 范围 要求d using different methods from different models depending 上 the actual 范围. That 是, 如果 a given 领域 是 set at the 默认, 网站, and 商店 值s, I couldn’t simply 使用 法师::getStoreConfig() to get the 网站 值 —此方法需要一个商店实例,并且传入网站的子商店之一将返回商店的值,该值可能与网站的值不同。另外,由于必须使用较低级别的方法来获取精确值,因此我必须确保正确处理带有后端模型的字段。

但是,在经过反复试验以找到可以可靠地处理这些问题的核心方法之后,所形成的逻辑才是完整的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
<?的PHP
 
电子战_ConfigScopeHints_Helper_Data 延伸 法师_Core_Helper_Abstract
{
    const PROFILER_KEY = 'EW_ConfigScopeHints';
 
    / **
    *获取默认商店ID。
    
     * ID并不总是0。
     *
     * @return 在 t
     * /
    受保护的 功能 _getDefaultStoreId() {
        返回 0;
    }
 
    / **
    *以以下形式获取范围树:
     *
    * 数组('网站'=> 数组 (
     *          website id => 数组('stores' =>商店ID的数组),
     *          ...
     *     )
     *)
     *
     * @返回数组
     * /
    上市 功能 getScopeTree() {
        $ = 数组(“网站” => 数组());
 
        $网站 = 法师::应用程式()->getWebsites();
 
        / * @var $ 网站 法师_Core_Model_Website * /
        前言($网站 $网站) {
            $[“网站”][$网站->getId()] = 数组(“商店” => 数组());
 
            / * @var $ 商店 法师_Core_Model_Store * /
            前言($网站->getStores() $商店) {
                $[“网站”][$网站->getId()][“商店”][] = $商店->getId();
            }
        }
 
        返回 $;
    }
 
    / **
    *使用后端模型处理后获取配置节点值
     *
    * @参数Mage_Core_Model_Config_Element $ 节点
     * @参数$ 路径
     * @返回字符串
     * /
    受保护的 功能 _getProcessedValue(法师_Core_Model_Config_元件 $节点, $路径) {
        $ = ()$节点;
        如果 (!空的($节点['backend_model']) && !空的($)) {
            $后端 = 法师::getModel(()$节点['backend_model']);
            $后端->setPath($路径)->设定值($)->afterLoad();
            $ = $后端->getValue();
        }
 
        返回 $;
    }
 
    / **
    *通过范围和范围ID获取当前值,
    *或null(如果找不到)。
     *
     * @参数$ 路径
    * @param $ 语境Scope
    * @param $ 语境ScopeId
    * @return mixed | 空值 | 串
    * @抛出Mage_Core_Exception
     * /
    受保护的 功能 _getConfigValue($路径, $语境Scope, $语境ScopeId) {
        $当前值 = 空值;
        开关($语境Scope) {
            案件 “网站”:
                $ = 法师::应用程式()->getWebsite($语境ScopeId)->getCode();
                $节点 = 法师::getConfig()->getNode(“网站/”.$.'/'.$路径);
                $当前值 = !$节点 ? '' : $这个->_getProcessedValue($节点, $路径);
                打破;
            案件 '默认':
                $节点 = 法师::getConfig()->getNode('默认/' . $路径);
                $当前值 = !$节点 ? '' : $这个->_getProcessedValue($节点, $路径);
                打破;
            案件 “商店”:
                $当前值 = 法师::应用程式()->getStore($语境ScopeId)->getConfig($路径);
                打破;
        }
 
        返回 $当前值;
    }
 
    / **
    *获取覆盖路径上的config值的作用域。
    *以以下形式返回
    * 数组(array('scope'=>覆盖范围,“ 范围_id” =>覆盖范围ID),...)
     *
     * @参数$ 路径
    * @param $ 语境Scope
    * @param $ 语境ScopeId
     * @返回数组
     * /
    上市 功能 getOverridenLevels($路径, $语境Scope, $语境ScopeId) {
        $语境ScopeId = $语境ScopeId ?: $这个->_getDefaultStoreId();
 
        $当前值 = $这个->_getConfigValue($路径, $语境Scope, $语境ScopeId);
 
        如果(一片空白($当前值)) {
            返回 数组(); //发生了问题,让我们优雅地保释。
        }
 
        $ = $这个->getScopeTree();
 
        $覆写 = 数组();
 
        开关($语境Scope) {
            案件 “网站”:
                $商店 = 数组_values($[“网站”][$语境ScopeId][“商店”]);
                前言($商店 $商店Id) {
                    $ = $这个->_getConfigValue($路径, “商店”, $商店Id);
                    如果($ != $当前值) {
                        $覆写[] = 数组(
                            '范围'     => '商店',
                            'scope_id'  => $商店Id
                        );
                    }
                }
                打破;
            案件 '默认':
                前言($[“网站”] $网站Id => $网站) {
                    $网站价值 = $这个->_getConfigValue($路径, “网站”, $网站Id);
                    如果($网站价值 != $当前值) {
                        $覆写[] = 数组(
                            '范围'     => '网站',
                            'scope_id'  => $网站Id
                        );
                    }
 
                    前言($网站[“商店”] $商店Id) {
                        $ = $这个->_getConfigValue($路径, “商店”, $商店Id);
                        如果($ != $当前值 && $ != $网站价值) {
                            $覆写[] = 数组(
                                '范围'     => '商店',
                                'scope_id'  => $商店Id
                            );
                        }
                    }
                }
                打破;
        }
 
        返回 $覆写;
    }
 
    / **
    *格式覆盖输出范围
     *
    * @参数数组$ 覆写
     * @返回字符串
     * /
    上市 功能 形成atOverriddenScopes(数组 $覆写) {
        $标题 = $这个->__('此设置在更具体的范围内被覆盖。点击查看更多细节。');
 
        $格式化的 = '<一个class =“ 覆写-hint-list-toggle” 标题 =“'. $标题 .'" href="#">'. $标题 .'</a>'.
                     '<ul 类 =“ 覆写-hint-list”>';
 
        前言($覆写 $覆盖范围) {
            $范围 = $覆盖范围['范围'];
            $范围Id = $覆盖范围['scope_id'];
            $范围Label = $范围Id;
 
            $网址 = '#';
            $部分 = 法师::应用程式()->getRequest()->getParam('部分'); //grrr.
            开关($范围) {
                案件 '网站':
                    $网址 = 法师::getModel('adminhtml / 网址')->getUrl(
                        '* / * / *',
                        数组(
                            '部分'=>$部分,
                            '网站'=>法师::应用程式()->getWebsite($范围Id)->getCode()
                        )
                    );
                    $范围Label = 冲刺(
                        'website <a href="%s">%s</a>',
                        $网址,
                        法师::应用程式()->getWebsite($范围Id)->getName()
                    );
 
                    打破;
                案件 '商店':
                    $商店 = 法师::应用程式()->getStore($范围Id);
                    $网站 = $商店->getWebsite();
                    $网址 = 法师::getModel('adminhtml / 网址')->getUrl(
                        '* / * / *',
                        数组(
                            '部分'   => $部分,
                            '网站'   => $网站->getCode(),
                            '商店'     => $商店->getCode()
                        )
                    );
                    $范围Label = 冲刺(
                        'store view <a href="%s">%s</a>',
                        $网址,
                        $网站->getName() . '/' . $商店->getName()
                    );
                    打破;
            }
 
            $格式化的 .= "<li 类='$scope'>在$ 范围Label上覆盖</li>";
        }
 
        $格式化的 .= '</ul>';
 
        返回 $格式化的;
    }
}

结果

在添加了一些样式和少量的Prototype UI javascript之后,所需的功能就像一个吊饰一样工作—现在,在更具体的范围内被覆盖的字段清楚地表明了这一事实。

法师nto 1配置范围提示屏幕截图

打包

与GitHub上分发的任何其他模块一样,标准打包系统将创建一个 莫德曼 配置文件,以允许开发人员和商人进行自动安装。

1
2
3
4
5
6
7
8
应用程式//社区/电子战/设定档ScopeHints
应用程式/等等/模组/电子战_ConfigScopeHints.XML文件
应用程式/设计/adminhtml/默认/默认/布局/ew/配置scopehints.XML文件
皮肤/adminhtml/默认/默认/ew/配置scopehints
 
##翻译
应用程式/地区/zh_CN/电子战_ConfigScopeHints.CSV
应用程式/地区/es_ES/电子战_ConfigScopeHints.CSV

法师nto 2模块

要求

法师nto 2不仅提供了一个经过改进和设计更好的框架来构建,而且还提供了重新思考现有方法和要求的机会。但是,鉴于该项目是专门为直接端口的具体实践而设计的,因此要求与Magento 1模块完全相同。

注意:此端口已在Magento 2版本0.74.0-beta4上实现并测试。

实作

该模块具有与Magento 1灵感相同的方法注意事项:找到与内核进行交互的最佳方法,然后使用该交互点来实现自定义功能来满足需求。由于M1和M2在架构上存在显着差异,因此需要重新构造M2模块,并从M1版本中尽可能多地引入代码。

互动点

M1模块的最大缺点是,尽管使用了适当的扩展机制(块重写),但仍必须从内核复制大量逻辑并对其进行修改以创建交互点,从而为兼容性创造了很大的表面积问题。

In an effort to improve the 在 teraction point 在 M2, I revisited the oh-so-close \Magento\Config\Block\System\Config\Form::getScopeLabel() method. Unlike M1, the M2 版 of 这个 method accepts a more specific object 如 its parameter, which handily does have all the 要求d 在 formation to get its 值. This small change means that the surface area of the 在 teraction point (along with the possibility 对于 compatibility 是sues) 是 significantly reduced.

Using my 法师nto customization bag of tricks, honed over the last three years and spanning 如 many certifications, I examined the options to 在 teract with the getScopeLabel() method. As always, my first consideration 是 to determine which events, 如果 any, can be observed. Alas, 如 是 too often the 案件, there are no events which can be 使用d.

In the absence of a usable event, my next consideration 是 to determine 如果 the 在 teraction point can be created by subclassing 形成 and overriding the getScopeLabel() method. Subclassing the 形成 类 是 still not ideal, 如 it would 要求 a rewrite to take effect system-wide. While no 码 will be copied 对于ward, 这个 rewrite would conflict with any other 模组 which also rewrites 这个 类.

如果只有Magento可以预料到我的要求并调度一个事件,该事件传递了方法的参数,还提供了附加到方法的返回值的机会…

幸运的是,Magento 2引入了 外挂程式 —提供此确切功能的全新扩展机制。插件无需依赖开发人员来预期将来可能发生的所有自定义并调度相应的事件,而是可以在调用之前或之后拦截方法调用,从而提供了非常灵活的全局交互点。

In order to accomplish 这个 模组’s 要求ments, it actually needs to observe both before (to have original arguments present) and after (to 应用程式end to the 返回 值) getScopeLabel() 在 vocation. This 是 supported by the around 插入 listener 类型.

di.xml

1
2
3
4
5
6
<?XML文件 ="1.0"?>
<配置 XML文件ns:si="http://www.w3.org/2001/XMLSchema-instance" si:noNamespaceSchemaLocation=“ ../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd”>
    <类型 名称=“ 法师nto \ 设定档 \ 块 \ 系统 \ 设定档 \ 形成”>
        <插入 名称=“ 配置ScopeHints” 类型=“ 电子战 \ 设定档ScopeHints \ 模型 \ 插入” 排序="100" />
    </类型>
</配置>

插入.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?的PHP
 
命名空间 电子战\设定档ScopeHints\模型;
使用 \法师nto\设定档\模型\设定档\结构体\元件\领域;
使用 \法师nto\构架\短语;
 
插入
{
    / ** @var \ 电子战 \ 设定档ScopeHints \ 帮手 \ 数据 * /
    受保护的 $_帮手;
    / **
    * @参数\ 电子战 \ 设定档ScopeHints \ 帮手 \ 数据 $ 帮手
     * /
    上市 功能 __构造(\电子战\设定档ScopeHints\帮手\数据 $帮手) {
        $这个->_帮手 = $帮手;
    }
    / **
    *拦截核心配置表单块getScopeLabel()方法
    *添加其他替代提示。
     *
    * @请参阅Magento \ 设定档 \ 块 \ 系统 \ 设定档 \ 形成 :: getScopeLabel()
    * @参数\ 法师nto \ 设定档 \ 块 \ 系统 \ 设定档 \ 形成 $ 形成
    * @参数可调用$ getScopeLabel
    * @参数字段$ 领域
     * @返回短语
     * /
    上市 功能 GetScopeLabel附近(\法师nto\设定档\\系统\设定档\形成 $形成, \关闭 $getScopeLabel, 领域 $领域)
    {
        $currentScopeId = 空值;
        开关($形成->getScope()) {
            案件 “网站”:
                $currentScopeId = $形成->getWebsiteCode();
                打破;
            案件 “商店”:
                $currentScopeId = $形成->getStoreCode();
                打破;
        }
        $覆盖级别 = $这个->_帮手->getOverridenLevels($领域->getPath(), $形成->getScope(), $currentScopeId);
        / * @var $ 返回Phrase短语* /
        $标签短语 = $getScopeLabel($领域);
        如果(!空的($覆盖级别)) {
            $范围HintText = $标签短语 . $这个->_帮手->形成atOverriddenScopes($形成, $覆盖级别);
            //创建新词组,因为构成字符串已单独翻译
            $标签短语 = 短语($范围HintText, $标签短语->getArguments());
        }
        返回 $标签短语;
    }
}

只需编写少量代码,该模块便拥有了完美的交互点,而无需重写模块—兼容性问题的可能性从未降低。

定制逻辑实施

建立了平滑的交互点后,必须将M1自定义逻辑移植到M2。这非常容易— the 上ly significant changes were that the 配置 models must be 在 jected rather than pulled off the old global 法师 类 and the logic had to be updated to 使用 the M2 配置 methods to find 配置 值s 对于 comparison.

由于M2中强大的依赖关系注入支持,因此我无需做任何特别的事情即可获取配置模型的实例—只需将它们添加到构造函数中就足够了。调整新的配置模型方法也很简单。实际上,很容易找到新的方法,这些方法大大简化了以前在给定范围内查找配置字段精确值的不直观的机制。总而言之,移植模块的业务逻辑并没有’耗时超过半小时左右。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
<?的PHP
 
命名空间 电子战\设定档ScopeHints\帮手;
使用 \法师nto\商店\模型\网站;
使用 \法师nto\商店\模型\商店;
 
数据 延伸  \法师nto\构架\应用程式\帮手\抽象助手
{
    / ** @var \ 法师nto \ 构架 \ 应用程式 \ 帮手 \ 语境 * /
    受保护的 $_context;
    / ** @var \ 法师nto \ 商店 \ 模型 \ 商店ManagerInterface * /
    受保护的 $_storeManger;
 
    / **
    * @param \ 法师nto \ 构架 \ 应用程式 \ 帮手 \ 语境 $ 语境
    * @参数\ 法师nto \ 商店 \ 模型 \ 商店ManagerInterface $ 店经理
     * /
    上市 功能 __构造(
        \法师nto\构架\应用程式\帮手\语境 $语境,
        \法师nto\商店\模型\商店ManagerInterface $店经理
    ) {
        $这个->_storeManger = $店经理;
        $这个->_context = $语境;
    }
 
    / **
    *以易于浏览的格式获取商店树
    *用于配置路径值比较
     *
     * @返回数组
     * /
    上市 功能 getScopeTree() {
        $ = 数组(“网站” => 数组());
 
        $网站 = $这个->_storeManger->getWebsites();
 
        / * @var $ 网站网站* /
        前言($网站 $网站) {
            $[“网站”][$网站->getId()] = 数组(“商店” => 数组());
 
            / * @var $ 商店存储* /
            前言($网站->getStores() $商店) {
                $[“网站”][$网站->getId()][“商店”][] = $商店->getId();
            }
        }
 
        返回 $;
    }
 
    / **
    *包装器方法,用于在提供的路径,作用域和作用域代码中获取配置值
     *
     * @参数$ 路径
    * @param $ 语境Scope
    * @param $ 语境ScopeId
     * @返回混合
     * /
    受保护的 功能 _getConfigValue($路径, $语境Scope, $语境ScopeId) {
        返回 $这个->_context->getScopeConfig()->getValue($路径, $语境Scope, $语境ScopeId);
    }
 
    / **
    *获取路径值不同的范围和范围ID的数组
    *比提供的上下文范围和上下文范围ID。
    *如果没有任何下级作用域覆盖该值,则返回空数组。
     *
     * @参数$ 路径
    * @param $ 语境Scope
    * @param $ 语境ScopeId
     * @返回数组
     * /
    上市 功能 getOverridenLevels($路径, $语境Scope, $语境ScopeId) {
        $ = $这个->getScopeTree();
 
        $当前值 = $这个->_getConfigValue($路径, $语境Scope, $语境ScopeId);
 
        如果(一片空白($当前值)) {
            返回 数组(); //发生了问题,让我们优雅地保释。
        }
 
        $覆写 = 数组();
 
        开关($语境Scope) {
            案件 “网站”:
                $商店 = 数组_values($[“网站”][$语境ScopeId][“商店”]);
                前言($商店 $商店Id) {
                    $ = $这个->_getConfigValue($路径, “商店”, $商店Id);
                    如果($ != $当前值) {
                        $覆写[] = 数组(
                            '范围'     => '商店',
                            'scope_id'  => $商店Id
                        );
                    }
                }
                打破;
            案件 '默认':
                前言($[“网站”] $网站Id => $网站) {
                    $网站价值 = $这个->_getConfigValue($路径, “网站”, $网站Id);
                    如果($网站价值 != $当前值) {
                        $覆写[] = 数组(
                            '范围'     => '网站',
                            'scope_id'  => $网站Id
                        );
                    }
 
                    前言($网站[“商店”] $商店Id) {
                        $ = $这个->_getConfigValue($路径, “商店”, $商店Id);
                        如果($ != $当前值 && $ != $网站价值) {
                            $覆写[] = 数组(
                                '范围'     => '商店',
                                'scope_id'  => $商店Id
                            );
                        }
                    }
                }
                打破;
        }
 
        返回 $覆写;
    }
 
    / **
    *获取覆盖提示UI的HTML输出
     *
    * @参数\ 法师nto \ 设定档 \ 块 \ 系统 \ 设定档 \ 形成 $ 形成
    * @参数数组$ 覆写
     * @返回字符串
     * /
    上市 功能 形成atOverriddenScopes(\法师nto\设定档\\系统\设定档\形成 $形成, 数组 $覆写) {
        $标题 = __('此设置在更具体的范围内被覆盖。点击查看更多细节。');
 
        $格式化的 = '<一个class =“ 覆写-hint-list-toggle” 标题 =“'. $标题 .'" href="#"><span>'. $标题 .'</span></a>'.
            '<ul 类 =“ 覆写-hint-list”>';
 
        前言($覆写 $覆盖范围) {
            $范围 = $覆盖范围['范围'];
            $范围Id = $覆盖范围['scope_id'];
            $范围Label = $范围Id;
 
            $网址 = '#';
            $部分 = $形成->getSectionCode();
            开关($范围) {
                案件 '网站':
                    $网址 = $这个->_context->getUrlBuilder()->getUrl(
                        '* / * / *',
                        数组(
                            '部分'=>$部分,
                            '网站'=>$范围Id
                        )
                    );
                    $范围Label = 冲刺(
                        'website <a href="%s">%s</a>',
                        $网址,
                        $这个->_storeManger->getWebsite($范围Id)->getName()
                    );
 
                    打破;
                案件 '商店':
                    $商店 = $这个->_storeManger->getStore($范围Id);
                    $网站 = $商店->getWebsite();
                    $网址 = $这个->_context->getUrlBuilder()->getUrl(
                        '* / * / *',
                        数组(
                            '部分'   => $部分,
                            '网站'   => $网站->getCode(),
                            '商店'     => $商店->getCode()
                        )
                    );
                    $范围Label = 冲刺(
                        'store view <a href="%s">%s</a>',
                        $网址,
                        $网站->getName() . '/' . $商店->getName()
                    );
                    打破;
            }
 
            $格式化的 .= "<li 类='$scope'>在$ 范围Label上覆盖</li>";
        }
 
        $格式化的 .= '</ul>';
 
        返回 $格式化的;
    }
}

结果

将一点点CSS移植到LESS中,以及将一点点UI javascript从Prototype移植到jQuery,一点时间都没有,在我不知道将我的M1模块成功移植到M2之前。

法师nto 2配置范围提示屏幕截图

打包

与小细节 实际满足要求 顺便说一句,是时候打包该模块,使商人和开发人员易于安装了。由于Magento 2在作曲家方面的标准化,因此非常简单。

该模块已在模块根级别提交给GitHub,并添加了一个简单的composer.json文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
    “名称”: “ 黑客 / 马真托2-configscopehints”,
    “描述”: “ 法师nto 2存储配置覆盖提示模块”,
    “要求”: {
        “ 马真托 / 马真托-composer-installer”: “ *”
    },
    “类型”: “ 马真托2模块”,
    “版”: "2.0",
    “额外”: {
        “地图”: [
            [
                “ *”,
                “ 电子战 / 设定档ScopeHints”
            ]
        ]
    },
    “作者”: [
        {
            “名称”: “埃里克·维斯”,
            “主页”: "//ericwie.se/",
            “角色”: “开发人员”
        }
    ]
}

有了此文件,商人和开发人员可以通过从其Magento 2根目录运行类似于以下命令的命令来轻松安装模块。

1
2
3
4
5
$ 作曲家 配置 仓库.马真托2-配置scopehints vcs https://github.com/ericthehacker/magento2-configscopehints.git # add repo
$ 作曲家 要求 黑客/马真托2-配置scopehints #require模块
$ 的PHP -f 箱子/马真托 模组:使能 电子战_设定档ScopeHints
$ 的PHP -f 箱子/马真托 建立:升级
 

专业提示和经验教训

尽管我认为该港口进展顺利,但仍有一些障碍并不明显。这些是我遇到的难题和解决方案。

过时/缺少文档

当关注Magento的 官方开发人员文档 (与 旧文件 令人讨厌的是,它似乎比Google搜索结果中的新文档更胜一筹),模块的基本声明采用以下形式。

1
2
3
4
5
6
 
<配置>
<模组 名称=“名称空间模块” schema_version=“ 2.0.0”>
</模组>
</配置>
 

在该代码段之后,我创建了我的模块外壳,但仅收到以下错误消息。

1
属性 'setup_version' 失踪 对于 模组 'EW_ConfigScopeHints'.

As stated 在 the error message, the schema_version attribute should actually be 建立_version. Not a big change, but it’s kind of concerning that such a fundamental snippet 是 factually 在 correct 在 the documentation —尤其是因为这将是每个新开发人员的第一站。

但是,至少,该错误消息的存在表明我的模块确实已被加载。但是,固定属性名称后,我似乎无法使用我的模块的任何功能。在过时且缺少文档的情况下,我认为依赖注入XML完全不正确。经过反复的试验和调试,我发现我的模块根本没有启用。

尽管文档确实提供了以下注意事项,但并未说明如何实际启用模块—我正在开发中,但是没有部署配置文件!

在模块内不再设置模块的启用/禁用标志;它由部署配置文件控制,启用= 1,禁用= 0。这是由管理员和集成商控制的,而不是由模块开发人员控制的。

With no actual documentation to guide me, I eventually figured out that 模组 must be 使能d 在 应用程式/etc/config.php. However, editing 这个 file manually 是 discouraged, so the following commands (currently) are the best way to 使能 a 模组 manually.

1
2
3
4
 
$ 的PHP -f 箱子/马真托 模组:使能 电子战_设定档ScopeHints
$ 的PHP -f 箱子/马真托 建立:升级
 

由于我认为这些文档差距至关重要,因此我创建了一个 拉取要求 更新文档。

开发者模式

Looking up errors 在 var/report gets old pretty quickly, so 上e of the first things I needed to figure out 是 how to display errors by enabling developer mode. Fortunately, it’s really straightforward — the MAGE_MODE environmental variable must be set to the 值 developer, 如 shown 在 the following examples.

  • Apache配置: SetEnv MAGE_MODE developer
  • Bash .bash_profile: export MAGE_MODE=developer

编译资产

由于Magento 2“compilation” of 在 terceptors and views, I frequently ended up with 法师nto running out of date 码. Having to work with 这个 类型 of 是sue was not entirely unforeseen, but it took a few tries to figure out the exact directories to flush. For back end compiled 如sets (such 如 在 terceptors), deleting the contents of var/generation will cause them to be regenerated. For front end 如sets (such 如 LESS files), var/view_preprocessed should be flushed.

从这往哪儿走

如前所述,Magento 2不仅是更新的框架,而且还是重新思考现有解决方案的机会。尽管此模块提供了直接的价值,但仍有提高其对Magento 2约定的依从性的空间。

例如,我认为应该重新考虑管理界面—这个小图标看起来在M2上显得不合适,并且所有内容都位于“最后一个商店配置”字段列中也无法正常工作,尤其是考虑到M2管理员的半响应性。

如果将用户界面更新为与Magento 2管理员更一致,并且考虑到Magento通过GitHub接受社区贡献这一事实,则可能迈出的另一步是该模块可能值得将其移植到核心并向其中发出拉取请求包含在Magento 2内核中。

从哪里获得

有兴趣使用这些模块之一,还是只想查看整个源代码?可通过以下GitHub链接免费获得它们并获得自由许可。

明智地使用它!

发表评论

您的电子邮件地址不会被公开。 必需的地方已做标记 *

该网站使用Akismet减少垃圾邮件。 了解如何处理您的评论数据.

最近的帖子查看全部
2020年10月22日
通过 阿什莉·科利弗 商业见解, 优雅的骆马 2020年10月22日
毫无疑问,2020年是历史性的一年。火灾,全球大流行,暴动,老虎王等’只是冰山一角。如 […]