添加任务 添加任务的功能实现和前面实现用户功能的流程完全一致:
根据需求完善模型
创建需要的表单
编写控制器代码
完善 task 模型 打开 app/model/task.php(如果没有则用 WebSetup 创建),修改下面几个地方:
属性和关联的定义:
1
2
3
4
5
6
7
8
'props' => array
(
.....
'is_completed' => array ('readonly' => true ),
'completed_at' => array ('readonly' => true ),
'owner' => array (QDB::BELONGS_TO => 'User' , 'source_key' => 'owner_id' ),
),
验证规则:
1
2
3
4
5
6
7
8
'validations' => array
(
'subject' => array
(
array ('not_empty' , '任务主题不能为空' ),
array ('max_length' , 180 , '任务主题不能超过 60 个汉字或 180 个字符' ),
),
),
自动填充规则:
1
2
3
4
5
6
7
8
9
'create_autofill' => array
(
.....
'is_completed' => false ,
'completed_at' => null ,
),
create_autofill 设置可以在创建对象时将指定的数值设置为特定值。例如“新建任务的状态是未完成”这个逻辑就可以通过简单的指定 create_autofill 来实现。 最后再加上安全性设置:
1
'attr_accessible' => 'subject, description' ,
attr_accessible 设置会在构造对象时产生作用:
1
2
$task = new Task($form->values());
$task->save();
上述代码构造了一个新的 task 模型实例,并且以表单的值作为对象初始值。这种方式很方便,但存在一个安全隐患。
假设用户在客户端构造一个包含“task_id”字段值的表单,并提交到服务器,那么此时构造出来的 $task 对象就包含了 task_id 属性值。由于 task_id 属性也是 task 对象存储在 tasks 表中的主键字段,那么在接下来调用 save() 方法时,实际完成的操作不是“新建一个 task 对象”,而是“更新一个已有的 task 对象”。因此我们务必要堵上这样的漏洞。
设置 attr_accessible 选项,可以指定哪些属性可以通过构造函数来赋值 ,这样上面的代码中即便表单包含 task_id 值,也不会被存入新建的对象,实现了安全性。
创建表单 添加 app/form/task.php 内容是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Form_Task extends QForm
{
function __construct ()
{
parent ::__construct('form_task' );
$fields = array ('subject' => array (),'description' => array (), 'is_completed' => array ());
$this ->loadFromConfig($fields);
$this ->addValidations(Task::meta());
}
}
添加控制器动作 修改 tasks 控制器(如果没有则通过 WebSetup 创建),添加 actionCreate() 动作方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function actionCreate ()
{
$form = new Form_Task();
if ($this ->_context->isPOST() && $form->validate($_POST))
{
$user = $this ->_app->currentUserObject();
$task = $user->createTask($form['subject' ]->value, $form['description' ]->value);
$task->save();
return $this ->_redirect(url('tasks/index' ));
}
$this ->_view['form' ] = $form;
}
以及对应的视图 app/view/tasks/create.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php $this ->_extends('_layouts/default_layout' ); ?>
<?php $this ->_block('contents' ); ?>
<form action ="<?php echo url('tasks/create'); ?>" method ="post" >
<h1 > 添加任务</h1 >
<fieldset >
<p >
<label for ="username" > 任务主题</label >
<input type ="text" name ="subject" id ="subject" />
<span class ="error" > <?php echo $form['subject' ]->isValid() ? "" : $form['subject' ]->errorMsg()[0 ];?> </span >
</p >
<p >
<label for ="password" > 任务描述</label >
<textarea name ="description" id ="description" rows ="5" > </textarea >
<span class ="error" > <?php echo $form['description' ]->isValid() ? "" : $form['description' ]->errorMsg()[0 ];?> </span >
</p >
<p >
<label > </label >
<input type ="submit" name ="Submit" value ="添加" />
</p >
</fieldset >
</form >
<?php $this ->_endblock(); ?>
这里我们用到了应用程序对象的一个新方法 currentUserObject()。但这个方法还需要添加到 app/myapp.php 中,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
* 获得当前用户对应的 User 模型对象实例
*
* @return User
*/
function currentUserObject ()
{
$key = 'currentUserObj' ;
try {
$user = Q::registry($key);
return $user;
}catch (QException $e){
}
$data = $this ->currentUser();
$user = User::find('user_id = ?' , $data['id' ])->query();
if ($user->id())
{
Q::register($user, $key);
return $user;
}
else
{
throw new MyAppException('当前访问用户没有对应的 User 对象' );
}
}
currentUserObject() 方法不但通过查询操作获得 User 模型对象实例,而且通过检查其 ID 来确保这个对象是肯定存在的。这种做法从根本上保证了后续数据的有效性。为了避免每次获取都去查询数据库,通过 Q::register($user, $key)
写入内存缓存,查询数据库前先通过Q::registry($key)
取出缓存。
保证逻辑的正确性和数据的有效性是应用程序的核心使命!
测试创建任务 通过浏览器访问 http://localhost/todo/public/index.php?controller=tasks&action=create 就可以看到添加任务表单:
输入有效数据后提交,可以通过 phpMyAdmin 看到数据已经成功添加:
添加任务查看页面 修改 tasks 控制器的 actionIndex() 动作方法:
1
2
3
4
5
function actionIndex ()
{
$tasks = Task::find('owner_id = ?' , $this ->_app->currentUserObject()->id())->getAll();
$this ->_view['tasks' ] = $tasks;
}
以及对应的视图 app/view/tasks/index.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php $this->_extends('_layouts/default_layout'); ?>
<?php $this->_block('contents'); ?>
<div class="tasks">
<h1>我的任务</h1>
<?php foreach ($tasks as $task): ?>
<h2><a href="<?php echo url('tasks/edit', array('task_id' => $task->id())); ?>"><?php echo h($task->subject); ?></a></h2>
<p class="meta">
<?php if ($task->is_completed): ?>
<em>已经在 <?php echo date('m-d H:i', $task->completed_at); ?> 完成该任务</em>
<?php else: ?>
<strong>添加日期:<?php echo date('m-d H:i', $task->created); ?></strong>
<?php endif; ?>
</p>
<?php if ($task->description): ?>
<p class="description">
<?php echo nl2br(h($task->description)); ?>
</p>
<?php endif; ?>
<hr />
<?php endforeach; ?>
</div>
<?php $this->_endblock(); ?>
最后在 app/public/css/style.css 添加一些样式定义,以便让列表页面看上去更美观:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.tasks h2 {
font-size : 18px ;
font-weight : bold;
}
.tasks p {
font-size : 14px ;
color : #333 ;
font-weight : normal;
}
.tasks p .meta {
font-size : 12px ;
font-weight : normal;
color : #666 ;
}
现在添加任务后就可以看到任务列表了:
现在我们在顶部加上一个连接,跳转到添加任务的页面
1
<a href ="<?php echo url('tasks/create') ?>" > 添加任务</a >