跨境电商
经验交流分享

CakePHP 2.x十分钟博客教程(三):数据库基础操作、表单、路由器

通过前面两篇文章的学习,你的CakePHP博客已经初具雏形了。本文会构建博客的文章内容页面,添加新的文章,修改老文章以及删除文章的功能,并且涉及到CakePHP表单创建,数据验证,路由规则等主题。如果还没有仔细了解本教程的前两张,可以访问《Cakephp 2.0十分钟博客教程(一):安装与配置》从头开始学习。

锐想网CakePHP系列教程—CakePHP博客三部曲:

文章内容页

在上一篇教程当中,我们已经实现了博客文章列表的显示,并且在文章名上包含该篇文章的URL地址。复习下该段代码。

[php]
echo $this->Html->link(
$post[‘Post’][‘title’],
array(‘controller’ => ‘posts’, ‘action’ => ‘view’, $post[‘Post’][‘id’])
);
[/php]

该URL指向PostsController控制器的View方法,并传递文章的id作为URL参数。如果点击该链接,可以发现URL地址如下所示。

www.example.com/posts/view/id

现在,一起为该页面建立控制器。在PostsController控制器中添加view()方法,代码如下。

[php]
public function view($id = null) {
$this->Post->id = $id;
$this->set(‘post’, $this->Post->read());
}

[/php]

这段代码非常简单,通过参数获取到文章$id,然后赋予Post模型对象的id属性,接着通过该对象的read()方法读取匹配该id的一行数据,并赋予post变量传递到视图文件中。完成控制器之后,该为文章内容页建立视图文件了,view视图文件路径位于/app/View/Posts/view.ctp。创建该文件,并复制如下代码到该文件中。

[php]
<!– 路径: /app/View/Posts/view.ctp –>

<h1><?php echo $post[‘Post’][‘title’]?></h1>
<p><small>Created: <?php echo $post[‘Post’][‘created’]?></small></p>
<p><?php echo $post[‘Post’][‘body’]?></p>
[/php]

完成上述操作之后,访问index页面,点击文章标题,即可访问view页面。一切正常的话,CakePHP会使用默认布局文件显示如下内容。

添加文章 — CakePHP表单助手

显示文章列表以及文章内容是一个好的开始,然而一个简单的CakePHP博客应用必不可少的就是添加新的文章。这次改变下文件创建的顺序,我们首先创建视图文件,来构建博客页面添加文章的表单。事先约定,在PostsController控制器中处理文章添加逻辑的方法为add(),那么相应的模板文件就位于/app/View/Posts/add.ctp。

之前我们说到CakePHP视图层包含许多助手类,其中就有一个FormHelper用于输出HTML表单。将如下代码复制到add.ctp视图文件中。

[php]
<h1>Add Post</h1>
<?php
echo $this->Form->create(‘Post’);
echo $this->Form->input(‘title’);
echo $this->Form->input(‘body’, array(‘rows’ => ‘3’));
echo $this->Form->end(‘添加’);
?>
[/php]

表单助手类默认可以在任何视图文件中调用,通过$this->Form,即可实例化一个新的FormHelper类对象。上述代码首先通过create()方法,生成了表单头,如下所示,

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

在上面的create()方法中,我们传递了Post作为参数,该参数用于指定当前模型名。默认的,如果该方法没有传递任何参数,则假设该表单通过Post方式,默认当前模型,传递表单数据到当前控制器的add()方法中进行处理。

input()方法用于input表单元素。参数一用户匹配数据库的字段,参数二用于指定该表单元素的各种设置,以索引数组形式组织。

end()方法用于生成表单提交按钮,并完成表单HTML代码的结尾部分。该方法如果指定参数,则用于表单提交按钮中的按钮文字。

到此为止,我们的视图文件建立完毕。是时候建立控制器方法处理该表单提交的数据了。在PostsController控制器中添加一个add()方法,并添加如下代码到该方法中。另外需要在控制器中新建一个$components属性,该属性的作用稍后会作解释。

[php]
public $components = array(‘Session’);

public function add() {
if($this->request->is(‘post’) {
if($this->Post->save(($this->request->data)) {
$this->Session->setFlash(‘新文章创建完成’):
$this->redirect(array(‘action’ => ‘index’));
} else {
$this->Session->setFlash(‘创建文章出错’);
}
}
}
[/php]

完成上述代码之后,访问www.example.com/posts/add,会显示如下页面。

add()方法是目前PostsController控制器中最复杂的方法了。首先,通过$this->request获取CakeRequest请求对象,然后判断当前HTTP请求是否为POST类型。还记得吧,上面创建的表单默认是通过POST方式传递数据的,如果是,则$this->Post获取到模型对象,并调用起save()方法保存表单提交来的数据到数据库中。

需要注意的是$this->request->data,上面说到request是获取到了框架封装的CakeRequest类对象,接着获取其data属性中的值。这里实际上就是表单中提交的数据。可以通过CakePHP调试方法pr()输出下该数组,查看该数组的结构。

接着用到了新添加的Session组件,该组件用于设置一段消息并保存在session变量中,该段消息可以在页面转向后显示在视图文件中。相对于控制器中SessionComponent::setFlash()方法设置的字符串消息,在转向后的视图文件中,可以通过SessionHelper::flash()方法显示该消息,显示结束后,系统会自动销毁该变量,再次刷新页面,就不会再次显示该消息。

写好session消息之后,保存成功,页面需要转向到博客的文章列表页,该步骤通过控制器的流程控制方法redirect()进行,该方法同样使用数组形式作为URL地址,上述代码中意思是转向到当前控制器Posts的index()方法。如果想了解更多的关于CakePHP框架的URL组成形式,可以查看Router::url()方法。

到此为止,如果一切正常的话,你应该可以完美添加新的博客了,如下图所示,填写表单并点击提交之后,系统会转向的文章列表页,注意,红色背景显示的文字即我们在控制器中通过SessionComponent::setFlash()方法设置的提示成功的消息,如果再次刷新页面,该消息会自动消失。

CakePHP 表单数据验证

CakePHP模型类中可以通过$validate属性定义表字段的验证规则,该验证规则主要面向用户通过表单提交的数据。在上面的代码中我们通过FormHelper助手类生成了表单代码,如果说在该表单页面中不填写数据直接提交,也可以通过。而这并不是我们想要的结果,一篇博客文章,至少应该包含文章标题和文章内容。

这些验证规则可以通过在相应的模型中的$validate属性进行定义,进入/app/Model/Post.php文件,添加如下代码到Post模型类中。

[php]
public $validate = array(
‘title’ => array(
‘rule’ => ‘notEmpty’
),
‘body’ => array(
‘rule’ => ‘notEmpty’
)
);
[/php]

$validate属性通过上述数组形式进行赋值,根据数据库字段,非常容易该段代码的含义。这里,我们制定‘title’和’body’字段的验证规则为非空(notEmpty)。完成该段代码之后,访问add()访问,提交一个空的或者任意表单域为空的时候,系统会出现禁止提交的提示,如下图所示。

CakePHP的验证系统非常强大,本身内置了如信用卡号,email地址等规则,你也可以自己建立新的验证规则,以适用不同的数据。访问Data Validation获得更多数据验证的信息。另外,CakePHP表单之所以能够关联模型中字段的验证规则,大前提是通过FormHelper表单助手来创建表单元素,如果你是通过HTML标签进行编码,则无法享受CakePHP框架的表单验证功能。

编辑博客

细心的同学可以发现,锐想CakePHP基础教程到目前为止,为大家提供的例子都遵循了一个基本顺序。建立控制器方法,接着再写视图文件(除了表单一节,为了方便大家学习,首先写出的包含表单的视图文件)。接下来,我们继续按照该顺序构建博客的编辑逻辑。

在PostsController控制器中创建edit()方法,赋值如下内容到该方法。

[php]
function edit($id = null) {
$this->Post->id = $id;
if ($this->request->is(‘get’)) {
$this->request->data = $this->Post->read();
} else {
if ($this->Post->save($this->request->data)) {
$this->Session->setFlash(‘Your post has been updated.’);
$this->redirect(array(‘action’ => ‘index’));
} else {
$this->Session->setFlash(‘Unable to update your post.’);
}
}
}
[/php]

在看上述代码之前,我们需要回忆下大家在编辑老文章时候的顺序。首先你看到的依然是文章列表,然后在文章列表中选择文章进行编辑。

代码中,首先通过URL传递来的参数获取到文章的id。先判断请求是否为GET方式,如果是GET请求,说明是在文章列表页点击编辑链接,此时,我们需要读取该id相对应的数据,然后赋予request对象的data属性,以在视图文件中的表单域显示,方便我们进行修改。接着,else体重,当我们修改完成,点击提交时,该请求一定是POST方式进行。这段代码和save()方法完全一致,不再多做解释。

view()方法相对应的视图文件如下。

[php]
<!– File: /app/View/Posts/edit.ctp –>

<h1>Edit Post</h1>
<?php
echo $this->Form->create(‘Post’, array(‘action’ => ‘edit’));
echo $this->Form->input(‘title’);
echo $this->Form->input(‘body’, array(‘rows’ => ‘3’));
echo $this->Form->input(‘id’, array(‘type’ => ‘hidden’));
echo $this->Form->end(‘Save Post’);
[/php]

该视图文件与add()方法的视图文件也没有太大区别,唯一多出的是一个隐藏表单,和使用HTML标签写表单时隐藏域的功能一样,它负责在点击提交按钮时,传递当前编辑的文章id值。另外,添加新的文章与编辑文章的控制器代码实在相似,CakePHP是如何区分两种操作的呢?方法是$id,如果在request对象中的data元素中包含id键,则判定为更新博客;如果没有id值,则判定为添加新博客。

PS:值得注意的是,在进入文章编辑页面时,表单中会显示该条数据到相应的表单字段中。可能大家会想,可以通过$this->Post->read()获取数据,然后赋予一个变量,并以表单value属性值的形式,传递到视图文件中,以实现表单域中显示待编辑内容的效果。但是CakePHP框架已经为我们准备好了一切,我们只需要将值取出后赋予request对象的data属性即可,不需要再多写一行代码。

接着,可以在index.ctp视图中为每一篇文章添加一个修改按钮了。赋值如下内容到之前创建的index.ctp文件中。

[php]
<!– 文件路径: /app/View/Posts/index.ctp  (添加编辑URL) –>

<h1>Blog posts</h1>
<p><?php echo $this->Html->link("Add Post", array(‘action’ => ‘add’)); ?></p>
<table>
<tr>
<th>Id</th>
<th>Title</th>
<th>Action</th>
<th>Created</th>
</tr>

<!– 循环输出由控制器中赋值的$posts变量 –>

<?php foreach ($posts as $post): ?>
<tr>
<td><?php echo $post[‘Post’][‘id’]; ?></td>
<td>
<?php echo $this->Html->link($post[‘Post’][‘title’], array(‘action’ => ‘view’, $post[‘Post’][‘id’]));?>
</td>
<td>
<?php echo $this->Html->link(‘Edit’, array(‘action’ => ‘edit’, $post[‘Post’][‘id’]));?>
</td>
<td>
<?php echo $post[‘Post’][‘created’]; ?>
</td>
</tr>
<?php endforeach; ?>
</table>
[/php]

删除文章

关于CakePHP数据库的操作进行到了最后一步,删除文章也是博客应用中必不可少的环节。在PostsController控制器中创建delete()方法。

[php]
function delete($id) {
if ($this->request->is(‘get’)) {
throw new MethodNotAllowedException();
}
if ($this->Post->delete($id)) {
$this->Session->setFlash(‘The post with id: ‘ . $id . ‘ has been deleted.’);
$this->redirect(array(‘action’ => ‘index’));
}
}
[/php]

delete()方法包含$id参数,该参数用于表示文章,匹配数据库表中的递增id字段,第一个if语句用于判断当前请求的类型,在后边的视图文件中我们会看到,删除操作是通过表单助手的postLink()方法完成的,所以说如果检测到当前请求是GET形式,则很有可能网站遭到了某种黑客攻击或者误操作。

确定没有问题之后,通过Post模型对象的delete方法,即可删除该条数据。非常简单,不是嘛。最后,在index.ctp文章列表视图中,在每条博客后边添加 一个删除博客的链接按钮。

[php]
<!– 文件: /app/View/Posts/index.ctp –>

<h1>Blog posts</h1>
<p><?php echo $this->Html->link(‘Add Post’, array(‘action’ => ‘add’)); ?></p>
<table>
<tr>
<th>Id</th>
<th>Title</th>
<th>Actions</th>
<th>Created</th>
</tr>

<!– Here’s where we loop through our $posts array, printing out post info –>

<?php foreach ($posts as $post): ?>
<tr>
<td><?php echo $post[‘Post’][‘id’]; ?></td>
<td>
<?php echo $this->Html->link($post[‘Post’][‘title’], array(‘action’ => ‘view’, $post[‘Post’][‘id’]));?>
</td>
<td>
<?php echo $this->Form->postLink(
‘Delete’,
array(‘action’ => ‘delete’, $post[‘Post’][‘id’]),
array(‘confirm’ => ‘Are you sure?’));
?>
<?php echo $this->Html->link(‘Edit’, array(‘action’ => ‘edit’, $post[‘Post’][‘id’]));?>
</td>
<td>
<?php echo $post[‘Post’][‘created’]; ?>
</td>
</tr>
<?php endforeach; ?>

</table>
[/php]

上述视图文件没有太多值得解释的地方。唯一需要注意的是postLink()方法。该方法在表单助手中定义,使用Javascript创建POST请求完成删除操作。安全提示:通过GET方式构建删除的操作非常危险,用户可以轻易掌握删除动作的URL地址,并更改id信息删除所有内容!

CakePHP路由器

CakePHP框架的默认路由能够满足绝大多数应用程序的需要。对于非常在意用户体验及搜索引擎优化的程序员来说,可能需要在程序构建完毕之后,通过CakePHP路由器更改默认的URL地址,在本教程的最后,我们简单看下CakePHP路由器。

CakePHP的默认响应请求(访问www.example.com)是使用PagesController控制器,然后输出home.ctp视图文件,这些文件都包含在CakePHP框架的核心库中。现在我们通过路由更改默认响应到PostsController控制器的index()方法。首先找到路由配置文件,/app/Config/routes.php。找到如下代码,并替换新的代码。

[php]
//注释掉:
//Router::connect(‘/’, array(‘controller’ => ‘pages’, ‘action’ => ‘display’, ‘home’));

//添加代码:
Router::connect(‘/’, array(‘controller’ => ‘posts’, ‘action’ => ‘index’));
[/php]

对于该段代码,在本教程中不做过多解释,一切正常的话,现在访问www.example.com,就可以直接转向到PostsController控制器的index()方法了。

PS:CakePHP在获取到路由信息的修改知道,会自动完成反向路由的操作。例如,添加上述路由规则之后,任何在视图文件中,通过HtmlHelper助手link()方法的数组参数定义的链接,都会有www.example.com/posts/index自动输出为www.example.com,你不需担心视图中还保留路由更改之前的URL链接。

总结

到此为止,CakePHP基础教程结束。通过上面三章的学习,你已经能够掌握CakePHP框架的各种操作了,好吧,你已经是CakePHP专家了。当然,想要成为专家,需要做的还有很多,如果说CakePHP只有这些小花招,它也就不会风靡PHP社区这么多年,被称作PHP社区中的ROR了。

锐想网以后会推出更多CakePHP教程,帮助所有想学习CakePHP框架的同学们跨越英语障碍。值得一说的是,CakePHP相对于ThinkPHP以及CI框架,在国内的流行程度稍低,但是它绝对是值得深入学习的框架。记住一句话,一起向前行,我们一定行。

赞(0)
未经允许不得转载:锐想 » CakePHP 2.x十分钟博客教程(三):数据库基础操作、表单、路由器
分享到: 更多 (0)

评论 9

评论前必须登录!

 

  1. #1

    之前一直看网上的教程例子,不过都不是2.x版的,命名上吃了不少苦头,一口气看了这三篇一下就豁然开朗了,非常感谢
    function add()
    $this->request->save那里多了一个前括弧

    非常好7年前 (2012-03-24)
    • 这个系列是我原创的,网上的都是转载我的~~哈哈~先把命名规范看下是有必要,约定性的MVC框架对于命名规范的要求很严格

      ruiwant7年前 (2012-03-25)
      • 。。。这不是翻译了CAKEPHP的手册么。。。

        诗圣杜甫5年前 (2013-08-20)
        • 对的。CakePHP手册里面的入门教程。

          ruiwant5年前 (2013-08-25)
          • 命名规范,感觉说的不相符啊,是不是容易误导初学者,例如:posts_controller.php,我还以为要创建一个这文件(rails 中是这样命名的),结果创建了还不行,需要创建PostsController.php 的文件

            yueye5年前 (2014-01-16)
  2. #2

    😀 写得很不错,国内就是缺乏开源、互相帮助、互相学习精神

    开源真谛7年前 (2012-04-05)
  3. #3

    😛

    非常感谢分享6年前 (2012-12-12)
  4. #4

    非常不错,我从1.34到2.2都弄了一个多月了才看到,太感谢了

    zz6年前 (2012-12-17)
  5. #5

    非常感谢

    大声道3年前 (2016-01-19)

锐想电商 - 跨境电商经验交流分享

锐想无限