Magento 2 简介 —— 不再是 MVC (翻译)

前言:Alan Storm 的文章以其深度和专业性给博主留下了深刻的印象,而且他非常注意通过代码实例来讲解。在博主眼中 Alan 绝对是大师级的 Magento 专家,所以博主决定翻译他关于 Magento 2 的文章。本篇原文地址

今天我们来做个 Magento 2 的“Hello World”示例。我们会设置一个新的 URL ,并向你展示如何为这个 URL 创建 phtml 模版。在这个过程中我们要说一说设计模式以及 Magento 是怎样的设计模式。不过,这篇文章的核心部分是简单地、可以跟着做的步骤,它会帮助你入门 Magento 2 的模块开发。

等不及的可以去github上下载 Alan 的本篇完整模块代码,但是我们强烈建议你自己完成创建的过程。

对 Magento 1 的开发者来说,一个大的惊喜是 Magento 2 不是一个 Model,View,Controller 的系统。Magento 2 是近似 Model,View,ViewModel(MVVM)的系统,然而他的架构师们还没有给它起一个特定的称呼。

Magento 2 中,当你请求一个URL

http://magento.example.com/hello_mvc/hello/world

系统会将请求路由到 Controller 类的 execute method,类似于 MVC 系统将请求路由到 Controller 类的 action method。但是,和传统的 MVC 系统不一样的是,这个 Controller 类只负责如下事情:

  • 决定使用哪个 page layout
  • Handling saving data from POST requests
  • 下面两件事情之一:
    • 让系统 render HTTP response
    • 将用户重定向到下一页或上一页

你会发现我们都没提在 View 中设置变量。这是因为每个 View 要自己负责从 model layer, request object,或其他外部系统中取得自己的信息。Magento 将 HTML 页面分解成许多片段,称之为 containers 。每个 container 包含嵌套的对象树,叫做 blocks。每个 block 对象拥有一个 phtml 模板文件,用来指定该 block 对象要展现的 HTML 。按照 MVVC 的说法,Magento 的 block 对象就是 View Model。block 对象将会完成所有对 CRUD models, request object, 外部系统等的数据读入。phtml 模板文件是 View (MVVM 中的 View),他只和 ViewModel 交互(block 对象)

作为模块开发者,如果你想在 Magento 中创建一个新的 URL ,你需要做如下事情:

  • Configure a module to tell Magento which controller it should use for a URL
  • Configure a module to tell Magento which Block objects should be added to the system

看到这里有点招架不住了吧,不要担心。本篇介绍文章的剩余部分会带领你完成建立一个 Magento 2 “Hello World” 模块的所有必要步骤。多练习几次,你就会开始掌握新的术语,以及什么代码应该放在哪里。

关于“Cache Clearing”的注意点

像大多数现代框架一样,Magento 2 使用了很多不同的缓存文件来加快会拖慢速度的操作。这些缓存目的是使系统在生产环境中跑地更快 —— 但是这经常会导致 Magento 没有用最新的配置或源文件,所以看不到变化。

当你在 Magento 2 系统中创建新功能的时候,常常是很有必要清空你的缓存的。你可以通过bin/magento的 CLI 命令(cache:clean),后台的System -> Cache Management,或者通过删除缓存文件(一般在var/cache文件下,如果你用了缓存引擎则另当别论)来清空缓存。

为让速度飞起来,除了缓存文件,Magneto 2 还生成很多 boilerplate classes。这些文件放在var/generation路径下。当你修改了某些配置或代码文件后,通常有必要重新生成这些文件。目前,没有任何方法(CLI或者后台)可以帮你重新生成。唯一的方法是手动删除var/generation下的文件。

我们会尽量在需要清空缓存和重新生成代码文件的时候给你提个醒的,不过要是你遇到应该起作用却没有起作用的情况时,试试看这样做:先清除缓存和生成的代码文件,然后重新载入页面或是重新运行命令。

这些命令可以帮助你快速地回到正确的道路上。

rm -rf /path/to/magento/var/generation/*
rm -rf /path/to/magento/var/cache/*

Magento 2 Hello World Module

今天我们的目标是:

访问这个URL http://magento.example.com/hello_mvc/hello/world 返回 Magento2 默认主题下的 “Hello World” 信息。

从更高的角度来说,我们要完成这个目标的步骤如下:

  • 创建一个 Magento 2 的 Module
  • Configure this module with a route for a URL
  • Create a class for our controller object
  • Create a full action name layout handle XML file
  • Use the full action name layout handle XML file to add a new block to the content container
  • Create a template for our block

上面一些术语我们有的已经提到过了,有的还没有。对熟悉 Magento 1的人来说,有些看起很熟悉,但是改变又很多,你可能想忘记 Magento 1 是怎么工作的了呢。虽然Magento 1 的许多知识在 Magento 2 的许多地方依然是非常宝贵的,但是设想 Magento 2 像 Magneto 1 一样的工作是不能帮助你寻找结果。

创建一个 Magento 2 的 Module

Magento 2 中,Module 让程序员可以以模块化地方式向系统中增加新的代码。当你通过 Module 向系统增加代码时,系统才会知道去哪里找你的代码。Module 还定义了使用PHP 命名空间的规则,这样可以避免和其他开发者的代码冲突。Module 是Magento 2 系统的“头等公民”——核心团队也是使用 Module 来实现前后端功能的哦。

Module 需要被放在app/code文件夹下。Magento 2中所有的 module 都有唯一的名字,这个名字由两部分组成。第一部分是描述创建者(公司、个人或者小组)的单词。有时候称之为 vendor namespace。第二部分,是描述该模块功能的单词。

举例来说,Magento 2 自带的一个模块叫 Magento_Contact。这个第一部分 Magento 描述的是该模块的创建者(Magneto 核心团队)。第二部分 Contact 说的是这个模块干什么事情(给 Magento 增加 Contact form)

我们这个入门教程,就来创建一个叫Pulsestorm_HelloWorldMVVM的 module 吧。要创建这个模块,请你创建相应的文件,把内容复制进去。

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/etc/module.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
    <module name="Pulsestorm_HelloWorldMVVM" setup_version="0.0.1" />
</config>

module 一般放在app/code文件夹下,根据 module 的名字我们给他创建文件结构。(Pulsestorm_HelloWorldMVVM 对应 Pulsestorm/HelloWorldMVVM)

Magento 2 的 module 将包含许多 XML 配置文件,这些配置文件放在 etc 文件夹下。刚刚创建的module.xml是 module 的主要配置文件。这个文件正是 Magento 核心代码搜索系统的模块时要找的文件。

<config/> 根节点就是样板,不用管他,照抄就行。

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/etc/module.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">

Magento 2 使用 XSD schema 文件来验证所有 module 配置文件的内容。拷贝就行了,不会有变化。

这个配置文件我们感兴趣的部分是:

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/etc/module.xml -->
<module name="Pulsestorm_HelloWorldMVVM" setup_version="0.0.1" />

<module/> 节点告诉 Magento 我们想要给系统添加一个 module。name属性告诉 Magneto module 的名字是什么。setup_version告诉 Magento 我们 module 的版本是什么。这个 version 节点对 Magento 的 setup-resource/migration 系统很重要,不过今天我们不展开说。

上面的事情做好后,下面我们向app/etc/config.php文件的全局 module 列表中添加我们的 module。

#File: app/etc/config.php
<?php
return array (
  'modules' =>
  array (
    'Magento_Store' => 1,
    'Magento_AdvancedPricingImportExport' => 1,
    'Magento_Directory' => 1,
    //...
  ),
);

你看这是一个长长的 module 列表。这个文件是为了让核心代码不用每次请求时都去逐个扫描app/code/*文件。把我们的模块也加入到数组的最后。

#File: app/etc/config.php
<?php
return array (
  'modules' =>
  array (
    'Magento_Store' => 1,
    'Magento_AdvancedPricingImportExport' => 1,
    'Magento_Directory' => 1,
    //...
    'Pulsestorm_HelloWorldMVVM'=> 1
  ),
);

Magento Merchant Beta 版本后的更新:beta 版本后核心团队增加了 registration.php 文件,现在每个 module 都需要他。

#File: app/code/Pulsestorm/HelloWorldMVVM/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Pulsestorm_HelloWorldMVVM',
    __DIR__
);

上面的文件,除了Pulsestorm_HelloWorldMVVM不同,其他所有的 module 内容都是一样的。第二个参数是你的 module 的全名。

Why is this here here? It’s part of how Magento identifies which modules are installed into a system. This replaces the old Magento 1 app/etc/module files, and takes the ultimate source of truth for module presence away from app/etc/config.php.

安装疑难解答

有几种不同的方法来检查 module 是否正确安装好了。其中一种是使用 CLI 命令module:status

$ php bin/magento module:status
List of enabled modules:
Magento_Store
Magento_AdvancedPricingImportExport
//...
Pulsestorm_HelloWorldMVVM

List of disabled modules:
//...

这个命令会列出系统中所有安装了的和没有安装的 module

另一种方式是进入后台,在Stores -> Configuration -> Advanced -> Advanced -> Disable Modules Output 查看。

关于module 的创建最后要提醒的是,安装一个新的 module 可能会看到如下错误提示:

Please upgrade your database: Run "bin/magento setup:upgrade" 
from the Magento root directory. 
The following modules are outdated:
//...

这里不去深究错误原因,这是 Magento setup resource migration system 发出的警告。他告诉你模块的版本和上次setup resource migration scripts 执行的 module 版本不相符。

别管他,从 CMD 运行如下命令,警告就会消失了:

$ php bin/magento setup:upgrade

添加 controller action

我们已经创建了 module,接下来为我们的 URL 配置好路由,并创建一个控制器类。在这之前,我们来做个事情。

Magneto 2 社区版本吸收了许多原来企业版才有的特性。其中就包括 full page caching。这对生产环境来说是非常棒的,然而要是你开着全页缓存,开发一个新的 URL 的话,会有点痛苦。你可以登录后台,到System -> Cache Management 中关了它。如果你让他开着,那么每次请求都要删除缓存,缓存文件在var/page_cache目录下。

之前提到的两个缓存文件夹是var/cachevar/generation

关闭全页缓存后,我们就可以继续前进了。

Magnento 2 的路由

我们要让 Magento 2 响应如下 URL

http://magento.example.com/index.php/hello_mvvm/hello/world

index.php部分是可以不要的。如果你启用了mod_rewrite(或者是web 服务器其他对应的组建),那么上面的 URL 等价于下面的:

http://magento.example.com/hello_mvvm/hello/world

在Magento 2 系统中,每个独立的 module 都可以声明一个 front name,这个front name就是URL 中的第一段,对应我们的URL,就是hello_mvvm。如果一个 module 申明了一个 front name,那么相当于他告诉系统:

Magneto system code 你好,如果你看见任何以 /hello_mvvm 开头的URL,我有他们的控制器。

要让我们的模块声明hello_mvvm的front name,请增加如下配置文件。

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/etc/frontend/routes.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd">
    <router id="standard">
        <route id="hello_mvvm" frontName="hello_mvvm">
            <module name="Pulsestorm_HelloWorldMVVM" />
        </route>
    </router>
</config>

这个新创建的routes.xml文件告诉 Magento 我们要声明一个 front name。注意这个文件放在子目录frontend下面。Magnento 2 系统允许开发者创建多个application areas。 areas 控制的事情有载入哪个session,检查哪个 access control rule,以及载入那个配置文件。frontend area 就是前台购物应用。如果这里没有看明白也不用担心,乖乖放在指定位置就没事了。

<router/>节点包含了我们所有的routes

<router id="standard">
    <!-- ... -->
</router>

这里id="standard" 有点让人困惑。如果你为frontend area 设置 URL ,你就得用standard。如果你是为后台应用设置 URL 的话,你就得用id="admin"。这个原因是历史遗留问题,如果有机会的话,后续的文章会讲到原因。

<router/>内,我们会看到<route/>标签。

<route id="hello_mvvm" frontName="hello_mvvm">
    <module name="Pulsestorm_HelloWorldMVVM" />
</route>

每个单独的<route/>节点告诉 Magento 我们想要声明一个特定的 front name。frontName的属性值是 URL 第一部分的文本值。比方说frontName="hello_mvvm"告诉 Magento 我们想要声明的 URL 长下面这样:

http://magento.example.com/hello_mvvm/*

node节点的id属性唯一确定该节点。该id有时候也称为 route 的名称。按照惯例,99.9%的时候,该id的值应该都是和frontName一样的。如果你不知道为什么是这样的——你其实不需要知道。只要让他们一样就好了。

route节点内,你会看到:

<module name="Pulsestorm_HelloWorldMVVM" />

name属性节点应该是你 module 的名称。上面这些都弄好了以后,我们可以继续去创建控制器文件了。

新建控制器文件

Magento 2 使用传统的PHP“将 URL 转化为控制器类名”的方法。让我们再来看看我们的URL

http://magento.example.com/hello_mvvm/hello/world

要找出控制器的类名,Magento 2 将会查看 URL 第二和第三部分(helloworld

  1. 因为我们声明了Pulsestorm_HelloWorldMVVM的 front name 为hello_mvvm,所以控制器的名称以Pulsestorm\HelloWorldMVVM为开头。
  2. 因为我要定义一个控制器,所以接下来增加一个ControllerPulsestorm\HelloWorldMVVM\Controller
  3. 接下来 Magento 把 URL 的第二部分(hello)添加进去,首字母大写。(Pulsestorm\HelloWorldMVVM\Controller\Hello
  4. 然后 Magento 把 URL 的第三部分(world)添上去,首字母大写。(Pulsestorm\HelloWorldMVVM\Controller\Hello\World)

现在我们有控制器的全名了Pulsestorm\HelloWorldMVVM\Controller\Hello\World。现在创建这个文件吧。

#File: app/code/Pulsestorm/HelloWorldMVVM/Controller/Hello/World.php
<?php
namespace Pulsestorm\HelloWorldMVVM\Controller\Hello;
class World extends \Magento\Framework\App\Action\Action
{
    public function execute()
    {
        echo '<p>You Did It!</p>';
        var_dump(__METHOD__);
    }
}

控制器文件的位置和他的全名是对应的(PSR-0标准)

Class Name: Pulsestorm\HelloWorldMVVM\Controller\Hello\World;
   app/code/Pulsestorm/HelloWorldMVVM/Controller/Hello/World.php

Magento\Framework\App\Action\Action是前端控制器的基类。(Action 这个名字源于历史名称”action controller”)

在 Magnento 2 系统中,每个控制器有且仅有一个入口,就是execute方法。Magento 2 的设计师这样设计是为了帮助避免冲突,在一个大的开发团队中很多人都修改同一个控制器文件实现不同的功能,如果入口还不同是很容易冲突的。

上面的工作都完成后,载入这个 URL 试试看吧。(当然记得先清空生成的缓存)

http://magento.example.com/hello_mvvm/hello/world

假如你正确地跟着上面的步骤做了,那你应该看到如下输出:

You Did It
string 'Pulsestorm\HelloWorldMVVM\Controller\Hello\World::execute' (length=57)

恭喜你!你已经创建了自己的第一个 Magneto 2 controller

passing off to the view

前面我们说到控制器的任务有:

  • 决定使用哪个 page layout
  • Handling saving data from POST requests
  • 下面两件事情之一:
    • 让系统 render HTTP response
    • 将用户重定向到下一页或上一页

今天我们只示范控制器让系统 render HTTP response。Magento 2 系统中,如果你想让控制器渲染(render)一个 HTML Page,你得让控制器返回一个”page” objec。这是一个三步的过程:

  1. You need to inject a “page factory” object via automatic constructor dependency injection
  2. 使用上面的 page factory 对象来创建一个 page 对象
  3. 返回上面创建的 page 对象。

实际上没有听起来这么复杂。修改你的控制器文件让他像下面这样:

<?php
namespace Pulsestorm\HelloWorldMVVM\Controller\Hello;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\Action\Context;

class World extends \Magento\Framework\App\Action\Action
{
    protected $pageFactory;
    public function __construct(Context $context, PageFactory $pageFactory)
    {
        $this->pageFactory = $pageFactory;
        return parent::__construct($context);
    }

    public function execute()
    {
        var_dump(__METHOD__);
        $page_object = $this->pageFactory->create();;
        return $page_object;
    }
}

清空缓存以及var/generation里的内容,然后重新载入你的页面。你可能会困惑,因为你只看到var_dump输出的方法名:

string 'Pulsestorm\HelloWorldMVVM\Controller\Hello\World::execute' (length=57)

但是,如果你查看网页的源代码,你应该看到类似下面的代码:

<html >
<head >
<meta charset="utf-8"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="description" content="Default Description"/>
<meta name="keywords" content="Magento, Varien, E-commerce"/>

这里的问题并不是代码有误——这是因为我们还没有告诉layout system 它需要给http://magento.example.com/hello_mvvm/hello/world页面做点什么。正因为如此,Magento 渲染了 HTML 的结构,但是没有填入内容。接下来我们会了解怎么告诉 layout system 做事,但是首先我们得解释一下上面我们做了什么。

下面两行改变

use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\Action\Context;

严格来说并非必须的,但他让我们下面可以使用PageFactoryContext短的类名,如果你对PHP的命名空间不是很熟悉,可以参考short primer is a great place to start

接着是我们的constructor

protected $pageFactory;
public function __construct(Context $context, PageFactory $pageFactory)
{
    $this->pageFactory = $pageFactory;
    return parent::__construct($context);
}

这是 Magento 2 的依赖注入(automatic constructor dependency injection)起作用——如果你对这个不是很熟悉,你可以参考our object manager series。简单说来,Magento 有一些神奇的代码,当你在构造函数参数中写下某个类后,它就会自动帮你构造这个类的对象。所以,就这样我们创建了PageFactory的对象,并且把他赋给pageFactory属性。

即使你对依赖注入很熟悉,上面的$context变量可能也会使你很困惑。 This is here because it’s also in the parent object’s (Magento\Framework\App\Action\Action) constructor, and we need to call parent::__construct to make sure any work in the parent constructor still happens.

最后,一切都到了execute方法中

public function execute()
{
    var_dump(__METHOD__);
    $page_object = $this->pageFactory->create();;
    return $page_object;
}

在这里我们使用PageFactory对象创建一个page 对象,并且把他返回出去。

creating the view

对最终程序员用户(我们),Magento 的 Page Layout 系统是通过 XML based domain specific language 来控制的。直白一点,这意味着我们可以通过创建包含指令的 XML 文件来告诉layout system 干什么。完整讲解 layout 系统已经超出本文的范围了(另开一篇也讲不完),如果你有问题,请在 Alan 的博客下评论,或者去 Stack Exchange 提问。

先做下面的事情,创建下面的文件:

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <referenceBlock name="content">
        <block
            template="content.phtml"
            class="Pulsestorm\HelloWorldMVVM\Block\Main"
            name="pulsestorm_helloworld_mvvm"/>
    </referenceBlock>
</page>

之后创建下面的 php Block

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php
<?php
namespace Pulsestorm\HelloWorldMVVM\Block;
use Magento\Framework\View\Element\Template;

class Main extends Template
{
    protected function _prepareLayout()
    {

    }
}

最后,创建如下phtml模板

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml
<h1>Hello World</h1>

上面都做好了以后,清空缓存和生成的代码(var/generation)重新载入页面,你现在应该看到Hello World标题被 Magento 的设计包围着。

what just happened

接下来的几个小节我们会解释上面做的事情——上面一个小节可能是本篇文章中最让人困惑的部分了。我们正在引入新的术语,重新定义 Magento 1 的术语。你不需要完全理解这些部分才能继续,不过这是深入了解 Magento 核心代码的伟大起点。

Even if the next few sections are a little over your head, you’ll definitely want to skip ahead to the View/View Model section near the end.

The Full Action Name Layout Handle XML File

让我们从创建的 XML 文件说起

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <referenceBlock name="content">
        <block
            template="content.phtml"
            class="Pulsestorm\HelloWorldMVVM\Block\Main"
            name="pulsestorm_helloworld_mvvm"/>
    </referenceBlock>
</page>

这个文件被称为full action name layout handle XML file。之前我们提到,开发者通过 XML 文件中的指令告诉 Magneto Page Layout 系统做什么事情。这就是那个 XML 文件。Layout handle 就像是 Page Layout 系统的事件或是消息。每个 page 会触发某些 handles,这些 handles 告诉 Magento 哪些 Layout Handle XML Files 应该被加载。

每个控制器页面触发一个full action namehandle。一个 full action name 是一个组合了<route/> ID (hello_mvvm通常唯一确定front name)以及URL第二和第三部分(helloworld)的字符串。在我们的例子中,full acton name 就是hello_mvvm_hello_world

所以,当我们创建名为hello_mvvm_hello_world的文件时,相当于告诉 Magento:

如果触发了hello_mvvm_hello_world handle,请使用这个文件中的布局指令。

至于该文件在模块文件夹中的层次位置:

app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml

Magento 的每个模块都有一个view文件夹,用来放视图相关的文件。在view目录下是为单独的application area 准备的子文件夹(上面的例子application area 指的是frontend),在area文件夹下是为特定的文件类型准备的文件夹(layout文件,template文件等)

最后,layout 文件中的指令是这样的:

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<referenceBlock name="content">
    <block
        template="content.phtml"
        class="Pulsestorm\HelloWorldMVVM\Block\Main"
        name="pulsestorm_helloworld_mvvm"/>
</referenceBlock>

这些 XML 指令大致相当于下面的伪代码:

//pseudo code -- does not work
$our_view_block = $layout->createNewBlockWithClass('Pulsestorm\HelloWorldMVVM\Block\Main')
$our_view_block->setName('pulsestorm_helloworld_mvvm');
$out_view_block->setTemplate('content.phtml');
$layout->addBlockToContentContainer($our_view_block);

大白话来说就是:

Magento,请你用Pulsestorm\HelloWorldMVVM\Block\Main 类实例化一个block 对象,这个对象要使用 content.phtml 模板进行渲染,并且给这个block 对象一个全局唯一的名字叫pulsestorm_helloworld_mvvm

block 的名字应该是全局唯一的字符串,这样block 对象就可以被其他代码引用了。

creating a block class

之前提到过,Magento 2 的 page layout 是嵌套的container 和 block 的聚集。上面,我们使用full action name handle XML 文件告诉 Magento 我们要插入一个Pulsestorm\HelloWorldMVVM\Block\Main block 对象。我们创建了这样的 block 类:

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php
<?php
namespace Pulsestorm\HelloWorldMVVM\Block;
use Magento\Framework\View\Element\Template;

class Main extends Template
{
    protected function _prepareLayout()
    {

    }
}

Magento 的 block 类负责渲染 HTML 片段。上面 block 的基类是Magento\Framework\View\Element\Template,他是 Magento 的基本模板类。继承该类的block 会渲染他模板文件中的 HTML 内容。我们在layout handle XML 文件中设置了这个模板(content.phtml)

block 文件的路径和 Magento 系统中的所有 PHP 类一样,遵循 PSR-0 autoloader(关于psr

app/code/Pulsestorm/HelloWorldMVVM/Block
         Pulsestorm\HelloWorldMVVM\Block

creating a template file

Magento 2 系统中的大多数 block 是 template block,这意味着他们渲染phtml模板。当我们添加了如下属性:

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<block <!-- ... -->
    template="content.phtml"
<!-- ... -->

就相当于我们告诉 Magento 我们希望 block 使用content.phtml模板文件。

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml
<h1>Hello World</h1>

模板文件被认为是view assets,所以他们属模块的view文件夹。像routes 和 layout 文件一样,他们有特定的 area(这里是frontend),他们又属于template子文件夹。

如果你想的话也可以把模板放在template的子目录下

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<block <!-- ... -->
    template="some/sub/folder/content.phtml"
<!-- ... -->

view/view model

到目前为止,我们已经完成所有的事项了。但是,还有最后一件要说。之前我们把 Magento 描述成 Model, View, View Model 系统,Magento blocks 是 View Model,phtml 模板是 View。下面说他们是怎么工作的。

打开 block 类,把下面的代码添加到_prepareLayout方法中

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php
protected function _prepareLayout()
{
    $this->setMessage('Hello Again World');
}

然后再你的phtml文件中,添加如下代码:

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml
<h1><?php echo $this->escapeHtml($this->getMessage()); ?></h1>

重新载入页面,这次你应该看到的是Hello Again World信息。

作为视图开发者,你需要自己获取或计算模板需要的数据。你可以通过模式方法setget来完成工作,或者可以通过在 block 中直接定义方法,然后在phtml 模板中调用该方法来完成工作。

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php
public function getGoodbyeMessage()
{
    return 'Goodbye World';
}

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml
<h2><?php echo $this->escapeHtml($this->getGoodbyeMessage()); ?></h2>

block 对象还允许你访问 request object —— 试着载入如下 URL

http://magento-2-july-8.dev/hello_mvvm/hello/world/name/bill

和其他 PHP 框架类似,URL 第三部分以后的键值对被认为是参数。添加如下代码:

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php
protected function _prepareLayout()
{
    $this->setMessage('Hello');
    $this->setName($this->getRequest()->getParam('name'));
}

修改 template

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml
<h1>
    <?php echo $this->escapeHtml($this->getMessage()); ?>
    <?php echo $this->escapeHtml($this->getName()); ?>
</h1>

<h2><?php echo $this->escapeHtml($this->getGoodbyeMessage()); ?></h2>

传统的PHP MVC 系统在控制器中设置视图中的变量值,Magneto 的做法和传统的有点不一样。Magento 2 使用 Model, View, View Model 分离了业务逻辑和模板逻辑。

对于大型的开发团队来说,每个人的职责明确,Magento 的这一改动对他们很可能是更有利的,但对全栈工程师来说不是很好,全站工程师需要考虑更多的抽象层次了。

Magento 1 是可以用MVVM模式的,但是1的Zend Frameworkroots(and its too many chefs problem)导致了对传统的 MVC 方式的极大偏爱,使用全局注册或者在调用loadLayout 后直接设置 block 的属性值。在Magento 2 中你再也不能直接设置block 对象的属性值了,不过注册对象还是存在的,不过非官方的推荐做法是避免用这个注册对象。

总结

Regardless of whether Magento 2’s patterns elicit a “finally PHP gets it”, or a “WTF is this”, you’ve just successfully created a new landing page and application entry point in Magento 2. You’ve also been exposed to core Magento 2 concepts like clearing the cache, clearing generated files, and the hierarchy of configuration and view files in a Magento 2 module. You’re well on your way to unlocking the mysteries of Magento 2, and all the opportunities that will open for you in the future.

相关源文件下载

https://github.com/PiscesThankIT/HelloWorldMVVM

6 comments

  1. zyqkdl

    内容很新很有用!支持博主!

    1. Pisces Post author

      谢谢支持!

  2. VC

    寫的很棒!! 期待更多的教學文章

  3. Yadan

    超级棒!感谢作者!

  4. joe

    学习了,讲的很到位,看懂了,
    楼主有没有讲Model、Helper….的文章?

发表评论

电子邮件地址不会被公开。 必填项已用*标注