# 快速入門(四):連貫操作
上一篇我們詳細描述了查詢語言的用法,但是查詢語言僅僅解決了查詢或者操作條件的問題,更多的配合還需要使用模型提供的連貫操作方法。
## 介紹
連貫操作可以有效的提高數據存取的代碼清晰度和開發效率,并且支持所有的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的連貫操作來實現,并且不用考慮數據庫之間的表達差異,具有可移植性。后面會和大家講解如何操作和獲取變量。