CakePHP 2.x CookBook 中文版 第三章 入门(二)

建立 Post 视图

现在已经有了数据流、模型、程序逻辑和定义数据流向的控制器。我们要建立与上面例子中的 index 动作对应的视图。

Cake 视图仅是呈现应用程序布局的片段。对于多数应用程序,视图是混合了 PHP 的 HTML,但是它也可以是 XML、CSV,甚至是二进制数据。

布局是包装视图的处理显示的代码,能够定义和切换,但是现在,我们使用默认的布局。

还记得上一节是如何使用 set() 方法给视图中的 ‘posts’ 变量赋值的吗?所传递的数据类似于:

 1 // print_r($posts) output:  2  3 Array  4 (  5 [0] => Array  6  (  7 [Post] => Array  8  (  9 [id] => 1 10 [title] => The title 11 [body] => This is the post body. 12 [created] => 2008-02-13 18:34:55 13 [modified] => 14  ) 15  ) 16 [1] => Array 17  ( 18 [Post] => Array 19  ( 20 [id] => 2 21 [title] => A title once again 22 [body] => And the post body follows. 23 [created] => 2008-02-13 18:34:56 24 [modified] => 25  ) 26  ) 27 [2] => Array 28  ( 29 [Post] => Array 30  ( 31 [id] => 3 32 [title] => Title strikes back 33 [body] => This is really exciting! Not. 34 [created] => 2008-02-13 18:34:57 35 [modified] => 36  ) 37  ) 38 )

Cake 的视图文件存储在 /app/View 目录下与控制器名称(去掉了 controller 后缀)同名的文件夹中(现在的情况下,要创建 /app/View/Posts 文件夹)。使用漂亮的表格格式化数据,其视图类似于:

 1 <!-- File: /app/View/Posts/index.ctp -->  2  3 <h1>Blog posts</h1>  4 <table>  5 <tr>  6 <th>Id</th>  7 <th>Title</th>  8 <th>Created</th>  9 </tr> 10 11 <!-- Here is where we loop through our $posts array, printing out post info --> 12 13 <?php foreach ($posts as $post): ?> 14 <tr> 15 <td><?php echo $post['Post']['id']; ?></td> 16 <td> 17 <?php echo $this->Html->link($post['Post']['title'], 18 array('controller' => 'posts', 'action' => 'view', $post['Post']['id'])); ?> 19 </td> 20 <td><?php echo $post['Post']['created']; ?></td> 21 </tr> 22 <?php endforeach; ?> 23 <?php unset($post); ?> 24 </table>

希望这看起来挺简单。

你可能已经注意到例子中所用的那个调用 $this->HTML 的对象,这是 CakePHP 中 ‘HtmlHelper’ 的一个实例。CakePHP 带有一个生成类似链接、表单输出、JavaScript 和 Ajax 快照的视图助手集合。在 助手 一节你能学到更多如何使用它们的方法,现在要重点注意的是 link() 方法,它能用给定的标题(第一个参数)和 URL (第二个参数)生成 HTML 链接。

推 荐使用数组格式在 CakePHP 中指定 URLs。在“路由”一节对此有更多的解释。使用数组格式处理 URLs 可以获得 CakePHP 服务器的路由兼容性。也可以用 /controller/action/param1/param2 的格式将 URLs 关联到应用程序。

现在,让你的浏览器指向 http://www.example.com/posts/index 。你会看到带有标题和帖子列表的格式正确的视图。

如 果你点击了我们建立的这个视图中的某个链接(它将一个帖子标题链接到 URL /posts/view/some_id), 你应该会发现 CakePHP 会报告那个 action 还没有定义。 如果你没有收到这个报告,表明有些事情可能出错了,或者你竟然已经定义了它,这是很诡异的一种情形^_^。 现在,让我们在 PostsController 中建立它:

 1 class PostsController extends AppController {  2 public $helpers = array('Html', 'Form');  3  4 public function index() {  5 $this->set('posts', $this->Post->find('all'));  6  }  7  8 public function view($id = null) {  9 $this->Post->id = $id; 10 $this->set('post', $this->Post->read()); 11  } 12 }

set() 调用看上去有点眼熟。需要注意的是,我们在此优先使用 read() 而不是 find('all'),这是因为我们仅仅要得到一条帖子信息。

还要注意,我们的视图 action 获取了一个参数: 我们希望看到的帖子的 ID。 这个参数是通过 URL 请求传递给 action 的。 如果用户请求 /posts/view/3,那么值 ‘3’ 就被传递给 $id

现在让我们建立一个新的 ‘view’ action 放在 /app/View/Posts/view.ctp 文件中。

1 <!-- File: /app/View/Posts/view.ctp --> 2 3 <h1><?php echo h($post['Post']['title']); ?></h1> 4 5 <p><small>Created: <?php echo $post['Post']['created']; ?></small></p> 6 7 <p><?php echo h($post['Post']['body']); ?></p>

尝试输入链接 /posts/index 或者手动访问 /posts/view/1,看看工作是否正常。.

添加帖子

虽然从数据库中读出贴子并展示给我们是个不错的开始,但是我们还需要可以添加贴子。

首先,从在 PostsController 中建立 add() 方法开始:

 1 class PostsController extends AppController {  2 public $helpers = array('Html', 'Form', 'Session');  3 public $components = array('Session');  4  5 public function index() {  6 $this->set('posts', $this->Post->find('all'));  7  }  8  9 public function view($id) { 10 $this->Post->id = $id; 11 $this->set('post', $this->Post->read()); 12 13  } 14 15 public function add() { 16 if ($this->request->is('post')) { 17 $this->Post->create(); 18 if ($this->Post->save($this->request->data)) { 19 $this->Session->setFlash('Your post has been saved.'); 20 $this->redirect(array('action' => 'index')); 21 } else { 22 $this->Session->setFlash('Unable to add your post.'); 23  } 24  } 25  } 26 }

注解

在使用 Session 的任何一个 controller 中都需要包含 SessionComponent (和SessionHelper)。如果需要,可以将它包含在 AppController 中。

add() 实现了这样几个功能:如果表单提交的数据非空,就试图使用 Post 模型保存数据。如果因为某些原因保存未成功,则渲染视图,这给了我们向用户显示数据校验或其它警告的机会。

每个 CakePHP 请求包含一个 CakeRequest 对象,它可以通过 $this->request 访问。 request 对象包含这次请求被获取的信息,并能够用于应用程序流的控制。 在本例中,我们使用 CakeRequest::is() 方法检验请求是否是以 HTTP POST 格式提交的。

当用户使用表单向应用程序提交数据(POST),其信息可以使用 $this->request->data 获取。 可以使用 pr() 或debug() 方法输出这些数据。

我们使用了 Session 组件的 SessionComponent::setFlash() 方法发送了一条信息给 session 变量,用于在中转页上显示。 在中转页的布局中我们用 SessionHelper::flash 显示和清除这个 session 变量。 该控制器的Controller::redirect 方法用于跳转到其它 URL。 参数 array('action' => 'index') 指定了跳转到 /posts URL,等于 posts 控制器的 index 方法。 你可以参考 API 中的 Router::url() 功能,它使你可以指定为不同的 Cake 函数指定一个 URL 格式。

调用 save() 方法将检查检验错误,并在出现错误时跳过保存。 我们将在后续的章节中讨论如何处理这些错误。

数据校验

Cake 在获取表单校验的单调输出方面做了大量的工作。每个人都痛恨无止境的编写表单及其验证。CakePHP 使其变得更快更容易。

想要更好的利用校验功能,就需要在视图中使用 FormHelper 助手。FormHelper 助手默认在所有视图中都可用,用法是 $this->Form

这是我们添加的视图:

1 <!-- File: /app/View/Posts/add.ctp --> 2 3 <h1>Add Post</h1> 4 <?php 5 echo $this->Form->create('Post'); 6 echo $this->Form->input('title'); 7 echo $this->Form->input('body', array('rows' => '3')); 8 echo $this->Form->end('Save Post'); 9 ?>

在这里,我们用 FormHelper 助手生成一个 HTML 表单的起始标签。$this->Form->create() 生成的 HTML 是:

1 <form id="PostAddForm" method="post" action="/posts/add">

如果 create() 是不带参数调用的,表示创建的表单将以 POST 形式提交给同一个控制器的 add() (或者当表单中含有 id 元素时提交给 edit())动作。

$this->Form->input() 方法用来生成同名的表单元素,这个方法的第一个参数告诉 CakePHP 前后台通讯的是哪个域,第二个参数允许指定选项:本例中,是 textarea 域的行数。这里有一点自动完成的:input() 方法将基于 model 指定的域生成不同的表单元素。

$this->Form->end() 调用生成一个 submit 元素并结束表单。如果提供给 end() 方法的第一个参数是字符串,FormHelper 生成一个相应名称的 submit 域并关闭表单标签。再说一次, 助手 一节有更多的助手信息。

现在我们返回并更新 /app/View/Posts/index.ctp 视图使其包含一个新的 “Add Post” 链接。在 <table> 之前添加如下行:

1 <?php echo $this->Html->link( 2 'Add Post', 3 array('controller' => 'posts', 'action' => 'add') 4 ); ?>

你可能会奇怪:我如何通知 Cake 我的校验要求?校验规则是定义在模型中的。让我们回顾 Post 模型并稍作调整:

 1 class Post extends AppModel {  2 public $validate = array(  3 'title' => array(  4 'rule' => 'notEmpty'  5 ),  6 'body' => array(  7 'rule' => 'notEmpty'  8  )  9  ); 10 }

$validate 数组告诉 CakePHP 在 save() 方法被调用时如何校验我们的数据。这里,我指定 title 和 body 都不能为空。CakePHP 的校验功能很强劲,带有一批预置的规则(如 信用卡号码、Email 地址等),而且可以灵活地添加自己的校验规则。可以浏览 数据校验 获取更多的相关信息。

现在已经有了校验规则,试着输入空的 title 和 body,看看它们是如何工作的。当我们使用 FormHelper 的FormHelper::input() 方法创建表单,校验的错误信息会自动显示。

编辑帖子

我们已经来到了帖子编辑这一步,现在你已经是一个 CakePHP 的高手了,所以你应该操起一个模式。编写动作,然后是视图。PostsController 的 edit() 方法类似于:

 1 public function edit($id = null) {  2 $this->Post->id = $id;  3 if ($this->request->is('get')) {  4 $this->request->data = $this->Post->read();  5 } else {  6 if ($this->Post->save($this->request->data)) {  7 $this->Session->setFlash('Your post has been updated.');  8 $this->redirect(array('action' => 'index'));  9 } else { 10 $this->Session->setFlash('Unable to update your post.'); 11  } 12  } 13 }

这个动作首先检查用户提交的是不是 GET 请求。如果是,就找到 Post 并将其传递给视图。如果不是,就可能包含 POST 数据。我们将使用 POST 数据更新贴子记录,或者将校验的错误信息踢回并显示给用户。

edit 视图类似于:

1 <!-- File: /app/View/Posts/edit.ctp --> 2 3 <h1>Edit Post</h1> 4 <?php 5 echo $this->Form->create('Post', array('action' => 'edit')); 6 echo $this->Form->input('title'); 7 echo $this->Form->input('body', array('rows' => '3')); 8 echo $this->Form->input('id', array('type' => 'hidden')); 9 echo $this->Form->end('Save Post');

这个视图输出编辑表单(带有相应数据值),所有的错误校验信息也一并提供。

有件事需要注意:如果提供的数据数组中包含 ‘id’ 域,CakePHP 认为这是要修改一个模型的数据。如果没有 ‘id’ 域,cake 会认为当 save() 方法被调用时插入一条新的模型数据(参见 add 视图)。

现在可以更新 index 视图,添加一个用于编辑的链接:

 1 <!-- File: /app/View/Posts/index.ctp  (edit links added) -->  2  3 <h1>Blog posts</h1>  4 <p><?php echo $this->Html->link("Add Post", array('action' => 'add')); ?></p>  5 <table>  6 <tr>  7 <th>Id</th>  8 <th>Title</th>  9 <th>Action</th> 10 <th>Created</th> 11 </tr> 12 13 <!-- Here's where we loop through our $posts array, printing out post info --> 14 15 <?php foreach ($posts as $post): ?> 16  <tr> 17 <td><?php echo $post['Post']['id']; ?></td> 18  <td> 19 <?php echo $this->Html->link($post['Post']['title'], array('action' => 'view', $post['Post']['id'])); ?> 20  </td> 21  <td> 22 <?php echo $this->Html->link('Edit', array('action' => 'edit', $post['Post']['id'])); ?> 23  </td> 24  <td> 25 <?php echo $post['Post']['created']; ?> 26  </td> 27  </tr> 28 <?php endforeach; ?> 29 30 </table>

删除帖子

下面我们来看删除贴子的道道。在 PostsController 中添加 delete() 动作:

1 public function delete($id) { 2 if ($this->request->is('get')) { 3 throw new MethodNotAllowedException(); 4  } 5 if ($this->Post->delete($id)) { 6 $this->Session->setFlash('The post with id: ' . $id . ' has been deleted.'); 7 $this->redirect(array('action' => 'index')); 8  } 9 }

这个逻辑根据 $id 删除指定的贴子,并且使用 $this->Session->setFlash() 在用户重定向到 /posts 之后显示一条确认信息。如果用户试图用 GET 请求执行删除,我们就抛出一个异常。我们没有捕获的异常,将被 CakePHP 的异常句柄捕获并显示一个友好的错误页。有许多内置的 异常 ,能够用于指示应用程序需要生成的不同的 HTTP 错误。

因为我们仅仅执行了一些逻辑就重定向了,这个动作没有视图。但是仍然可能需要更新 index 视图,添加允许用户删除贴子的链接:

 1 <!-- File: /app/View/Posts/index.ctp -->  2  3 <h1>Blog posts</h1>  4 <p><?php echo $this->Html->link('Add Post', array('action' => 'add')); ?></p>  5 <table>  6 <tr>  7 <th>Id</th>  8 <th>Title</th>  9 <th>Actions</th> 10 <th>Created</th> 11 </tr> 12 13 <!-- Here's where we loop through our $posts array, printing out post info --> 14 15 <?php foreach ($posts as $post): ?> 16  <tr> 17 <td><?php echo $post['Post']['id']; ?></td> 18  <td> 19 <?php echo $this->Html->link($post['Post']['title'], array('action' => 'view', $post['Post']['id'])); ?> 20  </td> 21  <td> 22 <?php echo $this->Form->postLink( 23 'Delete', 24 array('action' => 'delete', $post['Post']['id']), 25 array('confirm' => 'Are you sure?')); 26  ?> 27 <?php echo $this->Html->link('Edit', array('action' => 'edit', $post['Post']['id'])); ?> 28  </td> 29  <td> 30 <?php echo $post['Post']['created']; ?> 31  </td> 32  </tr> 33 <?php endforeach; ?> 34 35 </table>

你可能感兴趣的:(CakePHP 2.x CookBook 中文版 第三章 入门(二))