# 快速入门(四):连贯操作 上一篇我们详细描述了查询语言的用法,但是查询语言仅仅解决了查询或者操作条件的问题,更多的配合还需要使用模型提供的连贯操作方法。 ## 介绍 连贯操作可以有效的提高数据存取的代码清晰度和开发效率,并且支持所有的CURD操作,也是ThinkPHP的ORM中的一个亮点。使用也比较简单, 假如我们现在要查询一个User表的满足状态为1的前10条记录,并希望按照用户的创建时间排序 ,代码如下: ~~~ $User->where('status=1')->order('create_time')->limit(10)->select(); ~~~ 这里的where、order和limit方法就称之为连贯操作方法,除了select方法必须放到最后一个外(因为select方法并不是连贯操作方法),连贯操作的方法调用顺序没有先后,例如,下面的代码和上面的等效: ~~~ $User->order('create_time')->limit(10)->where('status=1')->select(); ~~~ 其实不仅仅是查询方法可以使用连贯操作,包括所有的CURD方法都可以使用,例如: ~~~ $User->where('id=1')->field('id,name,email')->find(); $User->where('status=1 and id=1')->delete(); ~~~ 连贯操作仅在当次查询或者操作有效,完成后会自动清空连贯操作的所有传值(有个别特殊的连贯操作会记录当前的传值,如cache连贯操作)。简而言之,连贯操作的结果不会带入以后的查询。 系统支持的连贯操作方法有: |方法 |作用 |支持的参数类型| |-------|--------|-------------| |where* |用于查询或者更新条件的定义 |字符串、数组和对象| |table |用于定义要操作的数据表名称 |字符串和数组| |alias |用于给当前数据表定义别名 |字符串| |data |用于新增或者更新数据之前的数据对象赋值 |数组和对象| |field |用于定义要查询的字段(支持字段排除) |字符串和数组| |order |用于对结果排序 |字符串和数组| |limit |用于限制查询结果数量 |字符串和数字| |page |用于查询分页(内部会转换成limit) |字符串和数字| |group |用于对查询的group支持 |字符串| |having |用于对查询的having支持 |字符串| |join* |用于对查询的join支持 |字符串和数组| |union* |用于对查询的union支持 |字符串、数组和对象| |distinct |用于查询的distinct支持 |布尔值| |lock |用于数据库的锁机制 |布尔值| |cache |用于查询缓存 |支持多个参数(以后在缓存部分再详细描述)| |relation |用于关联查询(需要关联模型扩展支持) |字符串| |validate |用于数据自动验证 |数组| |auto |用于数据自动完成 |数组| |filter |用于数据过滤 |字符串| |scope* |用于命名范围 |字符串、数组| |bind* |用于数据绑定操作 |数组或多个参数| |token |用于令牌验证 |布尔值| |comment |用于SQL注释 |字符串| |index |用于数据集的强制索引| 字符串| |strict |用于数据入库的严格检测| 布尔值| 所有的连贯操作都返回当前的模型实例对象,其中带*标识的表示支持多次调用。 ## 用法 由于连贯操作的使用往往涉及到多个方法的联合使用,下面大概介绍下各个连贯操作的基本用法: #### WHERE |where |用于查询或者更新条件的定义| |-------|-------------------------| |用法 |where($where)| |参数 |where(必须):查询或者操作条件,支持字符串、数组和对象| |返回值 |当前模型实例| > 备注 如果不调用where方法,默认不会执行更新和删除操作 Where方法是使用最多的连贯操作方法,更详细的用法请参考:[快速入门(3)查询语言](query)。 ### TABLE |table |定义要操作的数据表名称,动态改变当前操作的数据表名称,需要写数据表的全名,包含前缀,可以使用别名和跨库操作| |-------|-------------------------| |用法 |table($table)| |参数 |table(必须):数据表名称,支持操作多个表,支持字符串、数组和对象| |返回值 |当前模型实例| > 备注:如果不调用table方法,会自动获取模型对应或者定义的数据表 用法示例: ~~~ $Model->Table('think_user user')->where('status>1')->select(); ~~~ 也可以在table方法中跨库操作,例如: ~~~ $Model->Table('db_name.think_user user')->where('status>1')->select(); ~~~ Table方法的参数支持字符串和数组,数组方式的用法: ~~~ $Model->Table(array('think_user'=>'user','think_group'=>'group'))->where('status>1')->select(); ~~~ 使用数组方式定义的优势是可以避免因为表名和关键字冲突而出错的情况。 一般情况下,无需调用table方法,默认会自动获取当前模型对应或者定义的数据表。 ### DATA |data |可以用于新增或者保存数据之前的数据对象赋值| |-------|-------------------------| |用法 |data($data)| |参数 |data(必须):数据,支持数组和对象| |返回值 |当前模型实例| > 备注:如果不调用data方法,则会取当前的数据对象或者传入add和save的数据 使用示例: ~~~ $Model->data($data)->add(); $Model->data($data)->where('id=3')->save(); ~~~ Data方法的参数支持对象和数组,如果是对象会自动转换成数组。如果不定义data方法赋值,也可以使用create方法或者手动给数据对象赋值的方式。 模型的data方法除了创建数据对象之外,还可以读取当前的数据对象, 例如: ~~~ $this->find(3); $data = $this->data(); ~~~ ### FIELD |field |用于定义要查询的字段| |-------|-------------------------| |用法 |field($field,$except=false)| |参数 |field(必须):字段名,支持字符串和数组,支持指定字段别名;如果为true则表示显式或者数据表的所有字段。except(可选):是否排除,默认为false,如果为true表示定义的字段为数据表中排除field参数定义之外的所有字段。| |返回值 |当前模型实例| > 备注:如果不调用field方法,则默认返回所有字段,和field('*')等效 使用示例: ~~~ $Model->field('id,nickname as name')->select(); $Model->field(array('id','nickname'=>'name'))->select(); ~~~ 如果不调用field方法或者field方法传入参数为空的话,和使用field('*')是等效的。 如果需要显式的传入所有的字段,可以使用下面的方法: ~~~ $Model->field(true)->select(); ~~~ 但是我们更建议只获取需要显式的字段名,或者采用字段排除方式来定义,例如: ~~~ $Model->field('status',true)->select(); ~~~ 表示获取除了status之外的所有字段。 ### ORDER |order |用于对操作结果排序| |-------|-------------------------| |用法 |order($order)| |参数 |order(必须):排序的字段名,支持字符串和数组,支持多个字段排序| |返回值 |当前模型实例| > 备注:如果不调用order方法,按照数据库的默认规则 使用示例: ~~~ order('id desc') ~~~ 排序方法支持对多个字段的排序 ~~~ order('status desc,id asc') ~~~ order方法的参数支持字符串和数组,数组的用法如下: ~~~ order(array('status'=>'desc','id')) ~~~ ### LIMIT |limit |用于定义要查询的结果限制(支持所有的数据库类型)| |-------|-------------------------| |用法 |limit($limit)| |参数 |limit(必须):限制数量,支持字符串| |返回值 |当前模型实例| > 备注:如果不调用limit方法,则表示没有限制 我们知道不同的数据库类型的limit用法是不尽相同的,但是在ThinkPHP的用法里面始终是统一的方法,也就是limit('offset,length') ,无论是Mysql、SqlServer还是Oracle数据库,都是这样使用,系统的数据库驱动类会负责解决这个差异化。 使用示例: ~~~ limit('1,10') ~~~ 也可以用下面的两个参数的写法,是等效的: ~~~ limit(1,10) 如果使用 limit('10') 等效于 limit('0,10') ~~~ ### PAGE |page |用于定义要查询的数据分页| |-------|-------------------------| |用法 |page($page)| |参数 |page(必须):分页,支持字符串| |返回值 |当前模型实例| Page操作方法可以更加快速的进行分页查询。 Page方法的用法和limit方法类似,格式为: ~~~ Page('page[,listRows]') ~~~ Page表示当前的页数,listRows表示每页显示的记录数。例如: ~~~ Page('2,10') ~~~ 表示每页显示10条记录的情况下面,获取第2页的数据。 listRow如果不写的话,会读取limit('length') 的值,例如: ~~~ limit(25)->page(3); ~~~ 表示每页显示25条记录的情况下面,获取第3页的数据。 如果limit也没有设置的话,则默认为每页显示20条记录。 page方法也支持传入二个参数,例如: ~~~ $this->page(5,25)->select(); ~~~ 和之前的用法 ~~~ $this->page('5,25')->select(); ~~~ 等效。 ### GROUP |group |用于数据库的group查询支持| |-------|-------------------------| |用法 |group($group)| |参数 |group(必须):group的字段名,支持字符串| |返回值 |当前模型实例| 使用示例: ~~~ group('user_id') ~~~ > Group方法的参数只支持字符串 ### HAVING |having |用于数据库的having查询支持| |-------|-------------------------| |用法 |having($having)| |参数 |having(必须):having,支持字符串| |返回值 |当前模型实例| 使用示例: ~~~ having('user_id>0') ~~~ > having方法的参数只支持字符串 ### JOIN |join |用于数据库的join查询支持| |-------|-------------------------| |用法 |join($join)| |参数 |join(必须):join操作,支持字符串和数组| |返回值 |当前模型实例| > 备注:join方法支持多次调用 使用示例: ~~~ $Model->join(' work ON artist.id = work.artist_id')->join('card ON artist.card_id = card.id')->select(); ~~~ 默认采用JOIN (等同于 INNER JOIN)方式,如果需要用其他的JOIN方式,可以改成 ~~~ $Model->join('RIGHT JOIN work ON artist.id = work.artist_id')->select(); ~~~ 如果join方法的参数用数组的话,只能使用一次join方法,并且不能和字符串方式混合使用。 例如: ~~~ join(array(' work ON artist.id = work.artist_id','card ON artist.card_id = card.id')) ~~~ ### UNION |union |用于数据库的union查询支持| |-------|-------------------------| |用法 |union($union,$all=false)| |参数 |union(必须):union操作,支持字符串、数组和对象 all(可选):是否采用UNION ALL 操作,默认为false| |返回值 |当前模型实例| > 备注:Union方法支持多次调用 使用示例: ~~~ $Model->field('name') ->table('think_user_0') ->union('SELECT name FROM think_user_1') ->union('SELECT name FROM think_user_2') ->select(); ~~~ 数组用法: ~~~ $Model->field('name') ->table('think_user_0') ->union(array('field'=>'name','table'=>'think_user_1')) ->union(array('field'=>'name','table'=>'think_user_2')) ->select(); ~~~ 或者 ~~~ $Model->field('name') ->table('think_user_0') ->union(array('SELECT name FROM think_user_1','SELECT name FROM think_user_2')) ->select(); ~~~ 支持UNION ALL 操作,例如: ~~~ $Model->field('name') ->table('think_user_0') ->union('SELECT name FROM think_user_1',true) ->union('SELECT name FROM think_user_2',true) ->select(); ~~~ 或者 ~~~ $Model->field('name') ->table('think_user_0') ->union(array('SELECT name FROM think_user_1','SELECT name FROM think_user_2'),true) ->select(); ~~~ 每个union方法相当于一个独立的SELECT语句。 > 注意:UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。 ### DISTINCT |distinct |查询数据的时候进行唯一过滤| |-------|-------------------------| |用法 |distinct($distinct)| |参数 |distinct(必须):是否采用distinct,支持布尔值| |返回值 |当前模型实例| 使用示例: ~~~ $Model->Distinct(true)->field('name')->select(); ~~~ ### LOCK |lock |用于查询或者写入锁定| |-------|-------------------------| |用法 |lock($lock)| |参数 |lock(必须):是否需要锁定,支持布尔值| |返回值 |当前模型实例| > 备注:join方法支持多次调用 Lock方法是用于数据库的锁机制,如果在查询或者执行操作的时候使用: ~~~ lock(true) ~~~ 就会自动在生成的SQL语句最后加上 FOR UPDATE或者FOR UPDATE NOWAIT(Oracle数据库)。 ### VALIDATE |validate |用于数据的自动验证| |-------|-------------------------| |用法 |validate($validate)| |参数 |validate(必须):自动验证定义| |返回值 |当前模型实例| > 备注:只能和create方法配合使用 validate方法用于数据的自动验证,我们会在数据验证部分详细描述。 ### AUTO |auto |用于数据自动完成| |-------|-------------------------| |用法 |auto($auto)| |参数 |auto(必须):定义自动完成| |返回值 |当前模型实例| > 备注:auto方法只能配合create方法使用 auto方法用于数据的自动完成操作,具体使用我们会在数据自动完成部分描述。 ### SCOPE |scope |用于模型的命名范围| |-------|-------------------------| |用法 |scope($scope)| |参数 |scope(必须):命名范围定义| |返回值 |当前模型实例| > 备注:scope方法其实是连贯操作的预定义 scope方法的具体用法可以参考:3.1的新特性 命名范围 ### FILTER |filter |用于数据的安全过滤| |-------|-------------------------| |用法 |filter($filter)| |参数 |filter(必须):过滤方法名| |返回值 |当前模型实例| > 备注:filter方法一般用于写入和更新操作 filter方法用于对数据对象的安全过滤,例如: ~~~ $Model->data($data)->filter('strip_tags')->add(); ~~~ 目前filter方法不支持多个方法的过滤。 ## 总结 连贯操作为我们的数据操作带来了很大的便捷之处,并且只要SQL可以实现的操作,基本上都可以用ThinkPHP的连贯操作来实现,并且不用考虑数据库之间的表达差异,具有可移植性。后面会和大家讲解如何操作和获取变量。