根据我们最开始的分析,todo 应用应该允许用户注册、登录,然后创建自己的任务列表。用户注册这个功能流程很简单,用流程图表示如下:
用户注册的流程
这个功能我们使用 users 控制器的 register 动作来完成,所以首先创建 users 控制器。添加文件 _code/controller/users.php 并加入如下代码:
<?php class Controller_Users extends Controller_Abstract{ function actionRegister(){ } }
现在通过浏览器访问 http://localhost/todo/public/index.php?controller=users&action=register 还只能看到只带空白页面,因为我们还没有为 register 动作方法提供对应的视图文件内容。
当没有视图文件的Action被访问的时候,程序会自动创建对应的视图文件,再次打开 _code/view/users 目录,会发现 register.php 文件已经自动生成好。若不会自动创建,手动创建即可。
自动生成的空白内容
在 QeePHP 应用程序中,视图的功能就如其名称暗示的一样,是用来显示给用户看的。大多数情况下,视图都是包含大量 HTML 代码的 PHP 文件。我们可以在视图中输出变量内容,以便将操作结果展示给用户。
register 动作的操作结果就是显示一个“用户注册”表单。因此打开 app/view/users/register.php 文件,内容如下:
<form name="form_user" id="form_user" action="<?php echo url('users/register'); ?>" method="post"> <fieldset> <p> <label for="username">用户名</label> <input type="text" name="username" id="username" /> </p> <p> <label for="password">密码</label> <input type="password" name="password" id="password" /> </p> <p> <input type="submit" name="Submit" value="提交" /> </p> </fieldset> </form>
现在刷新浏览器,就可以看到注册表单了。
在视图中,我们使用了 url() 函数。这个函数在 QeePHP 框架中承担着非常重要的职责:生成有效的 URL 地址。
在 Web 应用程序中,URL 地址的重要性无论如何强调都不过分。而在 QeePHP 中,URL 中特定参数将被 MVC 模式用于访问特定控制器和动作。为了能够生成有效的 URL 地址,QeePHP 框架提供了 url() 函数。
url() 函数有多种用法,但最常用的用法只需要两个参数:
参数 | 用途 |
---|---|
$udi | 指定 URL 地址对应的 UDI |
$args | 要在 URL 中传递的附加参数 |
这里提到的 UDI 是统一目的地标识符(Uniform Destination Identifier)的缩写。UDI 由控制器、动作、名字空间以及模块名组成,采用如下的格式:
namespace::controller/action@module
一般我们用到的格式只有controller 和action,采用如下的格式:
#controller/action url('users/register')
UDI 字符串中,每一个部分都是可选的。如果没有提供控制器和动作名,则使用默认的控制器(default)和动作名(index)代替。
UDI 字符串写法示例:
controller controller/action /action controller@module controller/action@module namespace::controller namespace::controller/action namespace::controller@module namespace::controller/action@module @module namespace::@module
由于本教程没有用到名字空间和模块,所以整篇教程中出现的 UDI 都是“tasks/create”、“users/login”这样的形式。
在传统开发模式中,视图是个不被人重视的地方。配合 Smarty 之类的模板引擎,将页面分割成多个子模板就算不错了。但是随着现代 Web 应用的复杂度越来越高,对用户界面的要求也变得更严格,传统做法的工作量和灵活性都很不理想。
参考下面的示意图,可以看到一个页面中,除了中间的内容部分外,页头、侧边栏、页脚在其他页面中都是可以重复使用。那么我们为什么不能引入面向对象中的继承思想,让视图也可以继承呢?
典型的视图布局
使用模板继承后,我们把整个页面作为一个父模板,而各个页面对应的模板做成子模板,子模板从父模板继承。
多个子模板从父模板继承
虽然没有特别要求,但惯例上将包含页面整体结构的视图称为“布局视图”。在 todo 应用中,为了简单起见,我们就不制作新的布局视图了,直接沿用生成应用时自动创建的布局视图。
修改 register.php 文件,顶部加入下列代码:
// 指示该视图从 _layouts/default_layout 继承 <?php $this->_extends('_layouts/default_layout'); ?> // 定义一个名为 contents 的区块 <?php $this->_block('contents'); ?>
在 register.php 文件底部加入:
// 区块定义结束 <?php $this->_endblock(); ?>
完整代码如下:
<?php //布局设定 ,参考 view/_layouts下面的文件 ?> <?PHP $this->_extends('_layouts/default_layout'); ?> <?PHP $this->_block('contents');?> <form name="form_user" id="form_user" action="<?php echo url('users/register'); ?>" method="post"> <fieldset> <p> <label for="username">用户名</label> <input type="text" name="username" id="username" /> </p> <p> <label for="password">密码</label> <input type="password" name="password" id="password" /> </p> <p> <input type="submit" name="Submit" value="提交" /> </p> </fieldset> </form> <?PHP $this->_endblock();?>
修改完成后刷新浏览器,可以看到现在的页面不但有 register.php 中添加的表单内容,还有完整的页头、侧边栏和页脚。
从布局视图继承后获得的完整页面
新增的代码中, _extends() 方法表示当前视图从哪一个视图继承。打开 _layouts/default_layout.php 可以看到这里定义了完整的页面结构。注意 _extends() 方法指定的视图名字不需要包含扩展名。因为 QeePHP 支持多种模板引擎,而每种模板引擎使用的文件扩展名都有所不同。
在新增代码中,出现了 _block() 和 _endblock() 两个方法。这两个方法总是成对出现的,用来定义一个“区块”。每一个区块都有名称,因此 _block() 方法需要提供区块名称作为参数。在子模板中定义的区块,区块的内容会替换掉父模板中同名区块的内容。
打开 _layouts/default_layout.php 父模板可以看到如下代码:
<?php $this->_block('contents'); ?><?php $this->_endblock(); ?>
这里定义了一个名为“contents”的区块。由于子模板中也定义了同名区块,所以子模板中的内容就将父模板的“contents”区块内容替换掉了,最终获得了完整的输出内容。
利用这个特性,我们可以实现非常灵活的视图。例如在文章显示页面,将文章标题添加到页面的 title 标签中:
// 父模板 <title>这是我的网站 - <?php $this->_block('title'); ?>欢迎<?php $this->_endblock(); ?></title>
// 子模板 <?php $this->_block('title'); echo h($article->title); $this->_endblock(); ?>
当显示文章页面时,文章页面视图中定义的“title”区块就会替换掉父模板中的内容。从而页面标题就变成了“这是我的网站 - 文章的标题”。而在没有定义“title”区块的子模板中,父模板“title”区块的内容则会被保留。
除了继承和区块,QeePHP 的视图还提供了其他非常有使用价值的特征。我们会在教程接下来的内容中为大家逐步呈现。