Better Blocks: Magento 2 PHP View Models (PHP 视图模型让模块开发更独立)

泪湿孤枕 提交于 2020-02-27 15:52:55

Block类必须继承\Magento\Framework\View\Element\AbstractBlock,而\Magento\Framework\View\Element\AbstractBlock类又实现了\Magento\Framework\View\Element\BlockInterface。鉴于大多数Blocks需要渲染模版,所以大多数情况下他们在继承链中更进一步,继承自\Magento\Framework\View\Element\Template。

Blocks的问题
虽然Blocks可以很好的完成他们的工作,他们也有一些缺点。
所有的Blocks都使用构造注入,所以当一个Block需要额外的依赖的时候,它必须把依赖上下文变量传入parent::__construct()方法。

自然,许多开发人员然后通过受保护的getter或受保护的字段访问父项依赖项。这些定制逻辑和平台代码的交织导致代码更加复杂。 与简单的没有继承关系的类相比,代码变得难以理解和维护。

另外,当使用测试来驱动开发时,虽然依赖关系跟业务逻辑没有任何关系,但是因为必须处理父类之间的关系,使过程变得十分繁琐。 这是开发人员踏上TDD旅程的又一个障碍,并助长了难以进行测试的神话。

PHP视图模型 在写这编文章的时候我发现了以下模版block类上面的PHPDoc注释。

/** * Avoid extending this class. *
* If you need custom presentation logic in your blocks, use this class as block, and declare
* custom view models in block arguments in layout handle file.

* * Example: * * * My\Module\ViewModel\Custom\ *
* **/

此注释已添加到2.2版本中,并准确描述了Anton所指的内容。
更好的是,自Magento 2.2.1版本,我们甚至不用指定的class参数,因为 class="Magento\Framework\View\Element\Template" 是默认值。
现在我大多使用以下方式来声明我的Blocks, Example\ViewModel\Block\Example
<referenceContainer name="columns.top">
    <block name="view-model-example" template="Example_ViewModel::example.phtml">
        <arguments>
            <argument name="view_model" xsi:type="object">Example\ViewModel\Block\Example</argument>
        </arguments>
    </block>
</referenceContainer>
模版是这样开始的: 
<?php declare(strict_types=1);

/** @var \Example\ViewModel\Block\Example $viewModel */
$viewModel = $block->getData('view_model');

?>

<?php $viewModel->getSomeThing() ?>
这个视图模块必须实现标记接口,否则我们会遇到一个异常:
(UnexpectedValueException): Instance of Magento\Framework\View\Element\Block\ArgumentInterface is expected, got Example\ViewModel\Block\Example instead.

这个是异常中所指的ArgumentInterface
/** * Block argument interface.
* All objects that are injected to block arguments should implement this interface.
*/

interface ArgumentInterface { }

此限制是安全预防措施,由于布局可以在多个文件内配置--包括后台由商户添加的布局--此限制被用来限制由布局声明实例化Blocks的个数。
实际上,必须实现此接口并不是真正的麻烦。 我喜欢使用几乎解耦的视图模型,而不是从AbstractBlock扩展的块。

< ? php declare ( strict_types = 1 ) ;
 
namespace Example \ ViewModel \ Block ;
 
use Magento \ Framework \ View \ Element \ Block \ ArgumentInterface ;
 
class Example implements ArgumentInterface
{
     public function getSomeThing ( )
     {
         // ...
     }
}

构造函数不需要调用 parent::__construct(),任何依赖关系都显而易见
在某些情况下,仍然需要使用自定义块。 例如,根据条件判断对模版进行渲染。 但是在大多数情况下,视图模型已经足够了。

视图模型的好处
简而言之,使用视图模型代替块可以更好地分离自定义代码和平台代码,这导致代码具有以下属性:

更容易理解
更易于维护
更可重用
更安全的升级
更容易测试

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!