codecamp

Zend Framework 2-介绍我们第一个“博客” Module

介绍我们第一个“博客” Module

现在我们已经对 Zend Framework 2 骨架应用程序有所了解,让我们继续来创建我们自己的模块。我们会创建一个名为“博客”的模块。这个模块会显示一个代表每个博客帖子的数据库条目清单。每个帖子都有三个属性:idtexttitle。我们会创建用于提交新帖子到数据库中的表单,和修改现有帖子的表单。另外在我们整个快速开始指南中都会使用最佳实践。

编写一个新 Module

首先我们在 /module 目录下创建一个新文件夹名为 blog

为了能让其被 ModuleManager 认作是模块,我们只需要在目标module 的名称空间(Blog)中创建一个名为 Module 的 PHP 类。创建文件 /module/Blog/Module.php

 <?php
 // 文件名: /module/Blog/Module.php
 namespace Blog;

 class Module
 {
 }

现在我们拥有一个可以被 ZF2 的 ModuleManager 侦测到的 module 了。让我们将这个 module 添加到我们应用程序中。虽然这个 module 目前还不能干任何事情,但仅仅拥有 Module.php 类已经能让其被 ZF2 的 ModuleManager 载入。要实现这点,在主应用程序配置文件 /config/application.config.php 中内的 module 数组中为 Blog 添加一个条目:

 <?php
 // 文件名: /config/application.config.php
 return array(
     'modules' => array(
         'Application',
         'Blog'
     ),

     // ...
 );

如果你刷新你的应用程序你应该看不见任何改变(也没有任何错误)。

这个时候,我们有必要回头讨论一下 module 到底是做什么的。简而言之,一个 module 是您的应用程序的一个被封装的功能集合。一个 module 可以为您的应用程序添加可见功能,像我们的博客 module;一个 module 也可以为应用程序内的其他 module 提供背景功能,例如和第三方 API 进行互动。

将您的代码组织成 module 形式有利于让您轻松地重用其他应用程序中的功能,或者使用社区提供的 module。

配置 Module

我们要做的下一件事情就是为我们的应用程序添加一个路径,这样就能通过 URL localhost:8080/blog 来访问我们的 module。要实现这点,需要为我们的 module 添加路由配置,但首先我们需要让 ModuleManager 知道我们的 module 具有这些需要载入的配置表。

添加一个 getConfig() 函数到 Module 类中,并让其返回配置。(这个函数是在 ConfigProviderInterface 中声明的,尽管实际上是否要实现这个接口是可选的) 这个函数应该能返回一个 array 或者一个 Traversable 对象。继续编辑您的/module/Blog/Module.php:

<?php
 // 文件名: /module/Blog/Module.php
 namespace Blog;

 use Zend\ModuleManager\Feature\ConfigProviderInterface;

 class Module implements ConfigProviderInterface
 {
     public function getConfig()
     {
         return array();
     }
 }

做到这里我们的 Module 就可以被配置了。配置文件可能会变得十分大,此时将所有东西放在 getConfig() 中就不是最佳手段了。为了保持我们的工程的组织清晰,我们会将数组配置放在单独的文件中。在此我们创建 /module/Blog/config/module.config.php

 <?php
 // 文件名: /module/Blog/config/module.config.php
 return array();

现在我们来重写 getConfig() 函数来添加这个刚刚建立的新文件。

<?php
 // 文件名: /module/Blog/Module.php
 namespace Blog;

 use Zend\ModuleManager\Feature\ConfigProviderInterface;

 class Module implements ConfigProviderInterface
 {
     public function getConfig()
     {
         return include __DIR__ . '/config/module.config.php';
     }
 }

重新装载您的应用程序,这是你便会见到所有东西仍然和之前一样,接下来我们给配置文件添加一个新路径:

<?php
 // 文件名: /module/Blog/config/module.config.php
 return array(
     // 该行为 RouteManager 打开配置
     'router' => array(
         // 打开所有可能路径的配置O
         'routes' => array(
             // 定义一个新路径,称为 "post"
             'post' => array(
                 // 定义一个路径 "Zend\Mvc\Router\Http\Literal" , 基本上就是一个字符串
                 'type' => 'literal',
                 // 配置路径本身
                 'options' => array(
                     // 监听 uri "/blog" 
                     'route'    => '/blog',
                     // 定义默认控制器和当这个路径匹配时需要执行的动作
                     'defaults' => array(
                         'controller' => 'Blog\Controller\List',
                         'action'     => 'index',
                     )
                 )
             )
         )
     )
 );

现在我们添加了一个叫做 blog 的路径,该路径负责监听 URL localhost:8080/blog。每当有人访问这个路径时,Blog\Controller\List 类中的 indexAction() 函数就会被执行。不过,这个控制器暂时还不存在,所以如果你重新装载页面你就会看见这个错误信息:

 A 404 error occurred
 Page not found.
 The requested controller could not be mapped to an existing controller class.

 Controller:
 Blog\Controller\List(resolves to invalid controller class or alias: Blog\Controller\List)
 No Exception available

现在我们需要告诉 module 要去哪里寻找这个叫做 Blog\Controller\List 的控制器。要做到这点我们必须将这个 key 添加到 controllers 配置 key,在 /module/Blog/config/module.config.php 内。

 <?php
 // 文件名: /module/Blog/config/module.config.php
 return array(
     'controllers' => array(
         'invokables' => array(
             'Blog\Controller\List' => 'Blog\Controller\ListController'
         )
     ),
     'router' => array( /** Route Configuration */ )
 );

这个配置文件定义了 Blog\Controller\List 是在名称空间 Blog\Controller 下的 ListController 的别名。重新装载页面应该会看见以下信息:

( ! ) Fatal error: Class 'Blog\Controller\ListController' not found in {libPath}/Zend/ServiceManager/AbstractPluginManager.php on line {lineNumber}

这个错误告诉我们应用程序知道要载入哪个类,但是并不知道在哪里能找到它。要修正这个问题,我们需要为我们的 Module 配置 autoloading。 Autoloading 是一个过程,让 PHP 能自动根据需求载入类。至于我们的 Module,我们通过添加 getAutoloaderConfig() 函数到我们的 Module 类来实现这点。(这个函数是在 AutoloaderProviderInterface 中定义的,尽管实际上类是否要实现这个接口是可选的)

 <?php
 // 文件名: /module/Blog/Module.php
 namespace Blog;

 use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
 use Zend\ModuleManager\Feature\ConfigProviderInterface;

 class Module implements
     AutoloaderProviderInterface,
     ConfigProviderInterface
 {
     /**
      * 返回一个 array 传给 Zend\Loader\AutoloaderFactory.
      *
      * @return array
      */
     public function getAutoloaderConfig()
     {
         return array(
             'Zend\Loader\StandardAutoloader' => array(
                 'namespaces' => array(
                     // Autoload 所有类从名称空间 'Blog' 来自于 '/module/Blog/src/Blog'
                     __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
                 )
             )
         );
     }

     /**
      * 返回配置来和应用程序配置合并
      *
      * @return array|\Traversable
      */
     public function getConfig()
     {
         return include __DIR__ . '/config/module.config.php';
     }
 }

这看上去好像很多东西被改变了,不过不用害怕。我们已经添加了一个 getAutoloaderConfig() 函数来提供Zend\Loader\StandardAutoloader 的配置。这个配置文件告诉应用程序 __NAMESPACE__ (Blog) 中的类都能在 __DIR__.'/src/'.__NAMESPACE__中找到 (/module/Blog/src/Blog)。

Zend\Loader\StandardAutoloader 使用了 PHP 社区领导的标准,称为PSR-0https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md)。在其他东西之中,这个标准为 PHP 定义了一个方法来映射类名称到文件系统。所以有了这个配置后,应用程序就知道我们的 Blog\Controller\ListController 类应该在 /module/Blog/src/Blog/Controller/ListController.php 中存在。

如果你现在刷新浏览器,你还是会见到同样的错误,即使我们已经配置了 autoloader,因为我们还需要创建控制器类。我们立刻创建这个文件:

 <?php
 // 文件名: /module/Blog/src/Blog/Controller/ListController.php
 namespace Blog\Controller;

 class ListController
 {
 }

再次重新载入页面,现在我们终于看见不一样的内容了。新的错误信息(类似下文):

 A 404 error occurred
 Page not found.
 The requested controller was not dispatchable.

 Controller:
 Blog\Controller\List(resolves to invalid controller class or alias: Blog\Controller\List)

 Additional information:
 Zend\Mvc\Exception\InvalidControllerException

 File:
 {libraryPath}/Zend/Mvc/Controller/ControllerManager.php:{lineNumber}
 Message:
 Controller of type Blog\Controller\ListController is invalid; must implement Zend\Stdlib\DispatchableInterface

这是因为我们的控制器必须实现 ZendStdlibDispatchableInterface 接口才能被 ZendFramework 的 MVC 层运行。ZendFramework 提供了一些该接口的基础控制器实现,叫做 AbstractActionController,我们稍后会用。让我们先修改我们的控制器:

 <?php
 // 文件名: /module/Blog/src/Blog/Controller/ListController.php
 namespace Blog\Controller;

 use Zend\Mvc\Controller\AbstractActionController;

 class ListController extends AbstractActionController
 {
 }

是时候再次刷新站点的页面了,您应该会看见有一个新的错误消息:

 An error occurred
 An error occurred during execution; please try again later.

 Additional information:
 Zend\View\Exception\RuntimeException

 File:
 {libraryPath}/library/Zend/View/Renderer/PhpRenderer.php:{lineNumber}
 Message:
 Zend\View\Renderer\PhpRenderer::render: Unable to render template "blog/list/index"; resolver could not resolve to a file

现在应用程序告诉你一个视图模板文件没法被渲染,这也是可预见的,毕竟我们还没有将其创建。应用程序期望它在 /module/Blog/view/blog/list/index.phtml 这个位置。创建这个文件并且放置一些假内容在上面:

 <!-- Filename: /module/Blog/view/blog/list/index.phtml -->
 <h1>Blog\ListController::indexAction()</h1>

在我们继续之前,先让我们快速的看一下我们将该文件放置在何处。请注意视图文件是放置在 /view 子目录下的,并不是在 /src 子目录下,因为他们不是 PHP 类文件,而是用于渲染 HTML 的模板文件。下面的路径需要一些解释,不过也非常简单。首先我们有全小写的名称空间,跟着一个小写的控制器名称(没有后缀‘controller’),最后跟上我们正在访问的动作的名称(同样地,没有后缀‘action’)。总的来看像这样:/view/{namespace}/{controller}/{action}.phtml。这已经成为了社区标准,不过也有潜在的随时被你改变的可能。不过光是创建这个文件是不够的,这就带出了这次快速开始指南的最后主题:我们需要让应用程序知道上哪去寻找视图文件。通过修改我们的 module 配置文件 module.config.php 来实现:

 <?php
 // 文件名: /module/Blog/config/module.config.php
 return array(
     'view_manager' => array(
         'template_path_stack' => array(
             __DIR__ . '/../view',
         ),
     ),
     'controllers' => array( /** Controller Configuration */),
     'router'      => array( /** Route Configuration */ )
 );

上面的配置告诉应用程序目录 /module/Blog/view 中拥有视图文件来匹配上述默认样式。重要的是,你需要记住你不单能将视图文件指定给您的 module,同时也能用于覆盖其他 module 的视图文件。

现在刷新您的站点。终于我们可以看见错误消息之外的内容了。

恭喜!你不单止创建了一个简单的“Hello World”风格 module,还学会了许多错误消息和他们的起因。如果到现在你还没被磨死,请继续我们的快速开始指南,一起来创建一个能真正干点事情的 module。

关于 Zend Framework 2
Zend Framework 2-介绍 Service 和 ServiceManager
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }