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

2年前 阅读 190 评论 0 赞 0

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

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

编写一个新 Module

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

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

  1. <?php
  2. // 文件名: /module/Blog/Module.php
  3. namespace Blog;
  4. class Module
  5. {
  6. }

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

  1. <?php
  2. // 文件名: /config/application.config.php
  3. return array(
  4. 'modules' => array(
  5. 'Application',
  6. 'Blog'
  7. ),
  8. // ...
  9. );

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

这个时候,我们有必要回头讨论一下 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:

  1. <?php
  2. // 文件名: /module/Blog/Module.php
  3. namespace Blog;
  4. use Zend\ModuleManager\Feature\ConfigProviderInterface;
  5. class Module implements ConfigProviderInterface
  6. {
  7. public function getConfig()
  8. {
  9. return array();
  10. }
  11. }

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

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

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

  1. <?php
  2. // 文件名: /module/Blog/Module.php
  3. namespace Blog;
  4. use Zend\ModuleManager\Feature\ConfigProviderInterface;
  5. class Module implements ConfigProviderInterface
  6. {
  7. public function getConfig()
  8. {
  9. return include __DIR__ . '/config/module.config.php';
  10. }
  11. }

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

  1. <?php
  2. // 文件名: /module/Blog/config/module.config.php
  3. return array(
  4. // 该行为 RouteManager 打开配置
  5. 'router' => array(
  6. // 打开所有可能路径的配置O
  7. 'routes' => array(
  8. // 定义一个新路径,称为 "post"
  9. 'post' => array(
  10. // 定义一个路径 "Zend\Mvc\Router\Http\Literal" , 基本上就是一个字符串
  11. 'type' => 'literal',
  12. // 配置路径本身
  13. 'options' => array(
  14. // 监听 uri "/blog"
  15. 'route' => '/blog',
  16. // 定义默认控制器和当这个路径匹配时需要执行的动作
  17. 'defaults' => array(
  18. 'controller' => 'Blog\Controller\List',
  19. 'action' => 'index',
  20. )
  21. )
  22. )
  23. )
  24. )
  25. );

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

  1. A 404 error occurred
  2. Page not found.
  3. The requested controller could not be mapped to an existing controller class.
  4. Controller:
  5. Blog\Controller\List(resolves to invalid controller class or alias: Blog\Controller\List)
  6. No Exception available

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

  1. <?php
  2. // 文件名: /module/Blog/config/module.config.php
  3. return array(
  4. 'controllers' => array(
  5. 'invokables' => array(
  6. 'Blog\Controller\List' => 'Blog\Controller\ListController'
  7. )
  8. ),
  9. 'router' => array( /** Route Configuration */ )
  10. );

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

  1. ( ! ) 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 中定义的,尽管实际上类是否要实现这个接口是可选的)

  1. <?php
  2. // 文件名: /module/Blog/Module.php
  3. namespace Blog;
  4. use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
  5. use Zend\ModuleManager\Feature\ConfigProviderInterface;
  6. class Module implements
  7. AutoloaderProviderInterface,
  8. ConfigProviderInterface
  9. {
  10. /**
  11. * 返回一个 array 传给 Zend\Loader\AutoloaderFactory.
  12. *
  13. * @return array
  14. */
  15. public function getAutoloaderConfig()
  16. {
  17. return array(
  18. 'Zend\Loader\StandardAutoloader' => array(
  19. 'namespaces' => array(
  20. // Autoload 所有类从名称空间 'Blog' 来自于 '/module/Blog/src/Blog'
  21. __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
  22. )
  23. )
  24. );
  25. }
  26. /**
  27. * 返回配置来和应用程序配置合并
  28. *
  29. * @return array|\Traversable
  30. */
  31. public function getConfig()
  32. {
  33. return include __DIR__ . '/config/module.config.php';
  34. }
  35. }

这看上去好像很多东西被改变了,不过不用害怕。我们已经添加了一个 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,因为我们还需要创建控制器类。我们立刻创建这个文件:

  1. <?php
  2. // 文件名: /module/Blog/src/Blog/Controller/ListController.php
  3. namespace Blog\Controller;
  4. class ListController
  5. {
  6. }

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

  1. A 404 error occurred
  2. Page not found.
  3. The requested controller was not dispatchable.
  4. Controller:
  5. Blog\Controller\List(resolves to invalid controller class or alias: Blog\Controller\List)
  6. Additional information:
  7. Zend\Mvc\Exception\InvalidControllerException
  8. File:
  9. {libraryPath}/Zend/Mvc/Controller/ControllerManager.php:{lineNumber}
  10. Message:
  11. Controller of type Blog\Controller\ListController is invalid; must implement Zend\Stdlib\DispatchableInterface

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

  1. <?php
  2. // 文件名: /module/Blog/src/Blog/Controller/ListController.php
  3. namespace Blog\Controller;
  4. use Zend\Mvc\Controller\AbstractActionController;
  5. class ListController extends AbstractActionController
  6. {
  7. }

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

  1. An error occurred
  2. An error occurred during execution; please try again later.
  3. Additional information:
  4. Zend\View\Exception\RuntimeException
  5. File:
  6. {libraryPath}/library/Zend/View/Renderer/PhpRenderer.php:{lineNumber}
  7. Message:
  8. 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 这个位置。创建这个文件并且放置一些假内容在上面:

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

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

  1. <?php
  2. // 文件名: /module/Blog/config/module.config.php
  3. return array(
  4. 'view_manager' => array(
  5. 'template_path_stack' => array(
  6. __DIR__ . '/../view',
  7. ),
  8. ),
  9. 'controllers' => array( /** Controller Configuration */),
  10. 'router' => array( /** Route Configuration */ )
  11. );

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

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

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

你的支持将鼓励作者继续创作

评论(0)

(无)