# 快速入门(二):CURD 上一篇中,我们了解了ThinkPHP的基础部分,以及如何创建一个控制器和模板,并知道了M方法的用法,本篇将会讲解下数据的CURD操作,探索下更多的数据操作。 ## CURD **CURD**是一个数据库技术中的缩写词,一般的项目开发的各种参数的基本功能都是CURD。它代表`创建(Create)`、`更新(Update)`、`读取(Read)`和`删除(Delete)`操作。CURD 定义了用于处理数据的基本原子操作。之所以将CURD 提升到一个技术难题的高度是因为完成一个涉及在多个数据库系统中进行CURD操作的汇总相关的活动,其性能可能会随数据关系的变化而有非常大的差异。 > CURD在具体的应用中并非一定使用create、update 、read和delete字样的方法,但是他们完成的功能是一致的。例如,ThinkPHP就是使用add、save、select和delete方法表示模型的CURD操作。 ## 创建数据 大多数情况下,CURD的Create操作通常会通过表单来提交数据,首先,我们在Home模块的View/Form 目录下面创建一个add.html 模板文件,内容为: ~~~ <FORM method="post" action="__URL__/insert"> 标题:<INPUT type="text" name="title"><br/> 内容:<TEXTAREA name="content" rows="5" cols="45"></TEXTAREA><br/> <INPUT type="submit" value="提交"> </FORM> ~~~ 然后,我们还需要在Home模块的Controller目录下面创建一个FormController.class.php文件,暂时只需要定义FormController类,不需要添加任何操作方法,代码如下: ~~~ <?php namespace Home\Controller; use Think\Controller; class FormController extends Controller{ } ~~~ 接下来,访问 `http://localhost/app/index.php/home/Form/add` 就可以看到表单页面了,我们并没有在控制器里面定义add操作方法,但是很显然,访问是正常的。因为ThinkPHP在没有找到对应操作方法的情况下,会检查是否存在对应的模板文件,由于我们有对应的add模板文件,所以控制器就直接渲染该模板文件输出了。所以说对于没有任何实际逻辑的操作方法,我们只需要直接定义对应的模板文件就行了。 我们可以看到,在表单中定义了提交地址是到Form控制器的insert操作,为了处理表单提交数据,我们需要在FormController类中添加insert操作方法,如下: ~~~ <?php namespace Home\Controller; use Think\Controller; class FormController extends Controller{ public function insert(){ $Form = D('Form'); if($Form->create()) { $result = $Form->add(); if($result) { $this->success('数据添加成功!'); }else{ $this->error('数据添加错误!'); } }else{ $this->error($Form->getError()); } } } ~~~ 如果你的主键是自增类型的话,add方法的返回值就是该主键的值。不是自增主键的话,返回值表示插入数据的个数。如果返回false则表示写入出错。 ## 模型 为了方便测试,我们首先在数据库中创建一个think_form表: ~~~ CREATE TABLE IF NOT EXISTS `think_form` ( `id` smallint(4) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) NOT NULL, `content` varchar(255) NOT NULL, `create_time` int(11) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ; ~~~ 我们在insert操作方法中用了D函数,和M函数不同,D函数需要有对应的模型类,下面我们就来创建模型类。 模型类的定义规范是: ~~~ 模型名+Model.class.php (模型名的定义采用驼峰法并且首字母大写) ~~~ 我们在Home模块的Model目录下面创建FormModel.class.php文件,添加代码如下: ~~~ <?php namespace Home\Model; use Think\Model; class FormModel extends Model { // 定义自动验证 protected $_validate = array( array('title','require','标题必须'), ); // 定义自动完成 protected $_auto = array( array('create_time','time',1,'function'), ); } ~~~ 主要是用于表单的自动验证和自动完成,具体用法我们会用另外的篇幅单独讲述,这里暂时先略过。我们只要了解的是,如果使用D函数实例化模型类,一般需要对应一个数据模型类,而且create方法会自动把表单提交的数据进行自动验证和自动完成(如果有定义的话),如果自动验证失败,就可以通过模型的getError方法获取验证提示信息,如果验证通过,就表示数据对象已经成功创建,但目前只是保存在内存中,直到我们调用add方法写入数据到数据库。这样就完成了一个完整的Create操作,所以可以看到ThinkPHP在创建数据的过程中使用了两步: * 第一步,create方法创建数据对象 * 第二步,使用add方法把当前的数据对象写入数据库 当然,你完全可以跨过第一步,直接进行第二步,但是这样的预处理有几个优势: 1. 无论表单有多复杂,create方法都可以用一行代码轻松创建数据对象; 2. 在写入数据之前,可以对数据进行验证和补充; 其实create方法还有很多的功能操作,目的只有一个,确保写入数据库的数据安全和有效。 我们来验证下表单提交的效果,当我们不输入标题就直接提交表单的话,系统会给出标题必须这样的提示信息。 ![2015-04-23/5538cfc446925](https://box.kancloud.cn/2015-04-23_5538cfc446925.jpg) 当我们顺利提交表单后,会看到写入数据表的数据中的`create_time`字段已经有值了,这就是通过模型的自动完成写入的。 ![2015-04-23/5538cfe376ae6](https://box.kancloud.cn/2015-04-23_5538cfe376ae6.jpg) 如果你的数据完全是内部操作写入而不是通过表单的话(也就是说可以充分信任数据的安全),那么可以直接使用add方法,如: ~~~ $Form = D('Form'); $data['title'] = 'ThinkPHP'; $data['content'] = '表单内容'; $Form->add($data); ~~~ 也可以支持对象方式操作: ~~~ $Form = D('Form'); $Form->title = 'ThinkPHP'; $Form->content = '表单内容'; $Form->add(); ~~~ 对象方式操作的时候,add方法无需传入数据,会自动识别当前的数据对象赋值。 ## 读取数据 当我们成功写入数据后,就可以进行数据读取操作了。在前面一篇中,我们已经知道可以用select方法获取数据集,这里我们来通过find方法获取一个单一数据,定义read操作方法如下: ~~~ public function read($id=0){ $Form = M('Form'); // 读取数据 $data = $Form->find($id); if($data) { $this->assign('data',$data);// 模板变量赋值 }else{ $this->error('数据错误'); } $this->display(); } ~~~ read操作方法有一个参数$id,表示我们可以接受URL里面的id变量(后面我们会在变量章节详细描述。这里之所以用M方法而没有用D方法,是因为find方法是基础模型类Model中的方法,所以没有必要浪费开销去实例化FormModel类(即使已经定义了FormModel类)。我们通常采用find方法读取某个数据,这里使用了AR模式来操作,所以没有传入查询条件,find($id) 表示读取主键为$id值的数据,find方法的返回值是一个如下格式的数组: ~~~ array( 'id' => 5, 'title' => '测试标题', 'content' => '测试内容', 'status' => 1, ) ~~~ 然后我们可以在模板中输出数据,添加一个read模板文件, ~~~ <table> <tr> <td>id:</td> <td>{$data.id}</td> </tr> <tr> <td>标题:</td> <td>{$data.title}</td> </tr> <tr> <td>内容:</td> <td>{$data.content}</td> </tr> </table> ~~~ 完成后,我们就可以访问 `http://localhost/app/index.php/home/Form/read/id/1` 来查看了。 如果你只需要查询某个字段的值,还可以使用getField方法,例如: ~~~ $Form = M("Form"); // 获取标题 $title = $Form->where('id=3')->getField('title'); ~~~ 上面的用法表示获取id值为3的数据的title字段值。其实getField方法有很多用法,但是获取某个字段的值是getField方法最常规的用法。 查询操作是最常用的操作,尤其是涉及到复杂的查询条件,我们会在查询语言一章对查询进行更加详细的讲解。 ## 更新数据 在成功写入并读取数据之后,我们就可以对数据进行编辑操作了,首先我们添加一个编辑表单的模板文件edit.html,如下: ~~~ <FORM method="post" action="__URL__/update"> 标题:<INPUT type="text" name="title" value="{$vo.title}"><br/> 内容:<TEXTAREA name="content" rows="5" cols="45">{$vo.content}</TEXTAREA><br/> <INPUT type="hidden" name="id" value="{$vo.id}"> <INPUT type="submit" value="提交"> </FORM> ~~~ 编辑模板不同于新增表单,需要对模板进行变量赋值,所以,我们这次需要在FormController类添加两个操作方法: ~~~ public function edit($id=0){ $Form = M('Form'); $this->assign('vo',$Form->find($id)); $this->display(); } public function update(){ $Form = D('Form'); if($Form->create()) { $result = $Form->save(); if($result) { $this->success('操作成功!'); }else{ $this->error('写入错误!'); } }else{ $this->error($Form->getError()); } } ~~~ 完成后,我们就可以访问 `http://localhost/app/index.php/home/Form/edit/id/1` 数据的更新操作在ThinkPHP使用save方法,可以看到,我们同样可以使用create方法创建表单提交的数据,而save方法则会自动把当前的数据对象更新到数据库,而更新的条件其实就是表的主键,这就是我们在编辑页面要把主键的值作为隐藏字段一起提交的原因。 如果更新操作不依赖表单的提交的话,就可以写成: ~~~ $Form = M("Form"); // 要修改的数据对象属性赋值 $data['id'] = 5; $data['title'] = 'ThinkPHP'; $data['content'] = 'ThinkPHP3.2.3版本发布'; $Form->save($data); // 根据条件保存修改的数据 ~~~ save方法会自动识别数据对象中的主键字段,并作为更新条件。当然,你也可以显式的传入更新条件: ~~~ $Form = M("Form"); // 要修改的数据对象属性赋值 $data['title'] = 'ThinkPHP'; $data['content'] = 'ThinkPHP3.2.3版本发布'; $Form->where('id=5')->save($data); // 根据条件保存修改的数据 ~~~ 也可以改成对象方式来操作: ~~~ $Form = M("Form"); // 要修改的数据对象属性赋值 $Form->title = 'ThinkPHP'; $Form->content = 'ThinkPHP3.2.3版本发布'; $Form->where('id=5')->save(); // 根据条件保存修改的数据 ~~~ 数据对象赋值的方式,save方法无需传入数据,会自动识别。 save方法的返回值是影响的记录数,如果返回false则表示更新出错。 有些时候,我们只需要修改某个字段的值,就可以使用setField方法,而不需要每次都调用save方法。 ~~~ $Form = M("Form"); // 更改title值 $Form->where('id=5')->setField('title','ThinkPHP'); ~~~ 对于统计字段,系统还提供了更加方便的setInc和setDec方法。 例如: ~~~ $User = M("User"); // 实例化User对象 $User->where('id=5')->setInc('score',3); // 用户的积分加3 $User->where('id=5')->setInc('score'); // 用户的积分加1 $User->where('id=5')->setDec('score',5); // 用户的积分减5 $User->where('id=5')->setDec('score'); // 用户的积分减1 ~~~ ## 删除数据 删除数据很简单,只需要调用delete方法,例如: ~~~ $Form = M('Form'); $Form->delete(5); ~~~ 表示删除主键为5的数据,delete方法可以删除单个数据,也可以删除多个数据,这取决于删除条件,例如: ~~~ $User = M("User"); // 实例化User对象 $User->where('id=5')->delete(); // 删除id为5的用户数据 $User->delete('1,2,5'); // 删除主键为1,2和5的用户数据 $User->where('status=0')->delete(); // 删除所有状态为0的用户数据 ~~~ delete方法的返回值是删除的记录数,如果返回值是false则表示SQL出错,返回值如果为0表示没有删除任何数据。 ## 总结 现在,你已经基本掌握了ThinkPHP的CURD操作了,并且学会了使用ThinkPHP的create、add、save和delete方法,还有两个对字段操作的getField和setField方法。下一篇,我们会更深入的了解下如何使用ThinkPHP提供的查询语言。