詳解PHP的Laravel框架中Eloquent對象關(guān)系映射使用
零、什么是 Eloquent
Eloquent 是 Laravel 的 'ORM',即 'Object Relational Mapping',對象關(guān)系映射。ORM 的出現(xiàn)是為了幫我們把對數(shù)據(jù)庫的操作變得更加地方便。
Eloquent 讓一個 'Model類' 對應(yīng)一張數(shù)據(jù)庫表,并且在底層封裝了很多 'function',可以讓 Model 類非常方便地調(diào)用。
來看一段如下代碼:
<?php class Article extends \Eloquent { protected $fillable = []; }
'protected $fillable = [];' 這一行代碼在這里沒有任何價值,是 generator 自動生成的,在此我們不做討論。
這個類簡直再簡單不過了,沒有指定命名空間,沒有構(gòu)造函數(shù),如果那一行沒有意義的代碼也不算上的話,這個文件就只有兩個有實際意義的東西: 'Article' 和 '\Eloquent'。沒錯,Eloquent 就是這么屌炸天,只需要繼承一下 Eloquent 類,就可以干 'first() find() where() orderBy()' 等非常非常多的事情,這就是面向?qū)ο蟮膹?qiáng)大威力。
一、Eloquent 基本用法
Eloquent 中文文檔在:http://laravel-china.org/docs/eloquent
廢話不多說,下面我將直接展示 Eloquent 的幾種常見用法的代碼。
找到 id 為 2 的文章打印其標(biāo)題
$article = Article::find(2); echo $article->title;
查找標(biāo)題為“我是標(biāo)題”的文章,并打印 id
$article = Article::where('title', '我是標(biāo)題')->first(); echo $article->id;
查詢出所有文章并循環(huán)打印出所有標(biāo)題
$articles = Article::all(); // 此處得到的 $articles 是一個對象集合,可以在后面加上 '->toArray()' 變成多維數(shù)組。 foreach ($articles as $article) { echo $article->title; }
查找 id 在 10~20 之間的所有文章并打印所有標(biāo)題
$articles = Article::where('id', '>', 10)->where('id', '<', 20)->get(); foreach ($articles as $article) { echo $article->title; }
查詢出所有文章并循環(huán)打印出所有標(biāo)題,按照 updated_at 倒序排序
$articles = Article::where('id', '>', 10)->where('id', '<', 20)->orderBy('updated_at', 'desc')->get(); foreach ($articles as $article) { echo $article->title; }
基礎(chǔ)使用要點
1. 每一個繼承了 Eloquent 的類都有兩個 '固定用法' 'Article::find($number)' 'Article::all()',前者會得到一個帶有數(shù)據(jù)庫中取出來值的對象,后者會得到一個包含整個數(shù)據(jù)庫的對象合集。
2. 所有的中間方法如 'where()' 'orderBy()' 等都能夠同時支持 '靜態(tài)' 和 '非靜態(tài)鏈?zhǔn)? 兩種方式調(diào)用,即 'Article::where()...' 和 'Article::....->where()'。
3. 所有的 '非固定用法' 的調(diào)用最后都需要一個操作來 '收尾',本片教程中有兩個 '收尾操作':'->get()' 和 '->first()'。
二、中間操作流
Builder 這個單詞可以直譯成構(gòu)造器,但是“中間操作流”更容易理解,因為數(shù)據(jù)庫操作大部分時候都是鏈?zhǔn)讲僮鞯摹?/p>
中間操作流,請看代碼:
Article::where('id', '>', 10)->where('id', '<', 20)->orderBy('updated_at', 'desc')->get();
這段代碼的 `::where()->where()->orderBy()` 就是中間操作流。中間操作流用面向?qū)ο蟮姆椒▉砝斫?,可以總結(jié)成一句話:
創(chuàng)建一個對象,并不斷修改它的屬性,最后用一個操作來觸發(fā)數(shù)據(jù)庫操作。
如何找到中間操作流的蛛絲馬跡
中間操作流這個東西,文檔里幾乎沒有任何有價值的信息,那么,我們該怎么找出這個玩意兒呢?很簡單,使用以下代碼:
$builder = Article::where('title', "我是標(biāo)題")->title;
然后你就會看到下面的錯誤:
為什么會出現(xiàn)錯誤?因為 `Article::where()` 了之后依然是 `Builder` 對象,還不是 `Article` 對象,不能直接取 `title`。
“終結(jié)者”方法
所謂 “終結(jié)者” 方法,指的是在 N 個中間操作流方法對某個 Eloquent 對象進(jìn)行加工以后,觸發(fā)最終的數(shù)據(jù)庫查詢操作,得到返回值。
`first()` `get()` `paginate()` `count()` `delete()` 是用的比較多的一些 “終結(jié)者” 方法,他們會在中間操作流的最后出現(xiàn),把 SQL 打給數(shù)據(jù)庫,得到返回數(shù)據(jù),經(jīng)過加工返回一個 Article 對象或者一群 Article 對象的集合。
復(fù)雜用法示例
Article::where('id', '>', '100')->where('id', '<', '200')->orWhere('top', 1)->belongsToCategory()->where('category_level', '>', '1')->paginate(10);
三、模型間關(guān)系(關(guān)聯(lián))
1.一對一關(guān)系
顧名思義,這描述的是兩個模型之間一對一的關(guān)系。這種關(guān)系是不需要中間表的。
假如我們有兩個模型:User 和 Account,分別對應(yīng)注冊用戶和消費(fèi)者,他們是一對一的關(guān)系,那么如果我們要使用 Eloquent 提供的一對一關(guān)系方法,表結(jié)構(gòu)應(yīng)該是這樣的:
user: id ... ... account_id account: id ... ... user_id
假設(shè)我們需要在 User 模型中查詢對應(yīng)的 Account 表的信息,那么代碼應(yīng)該是這樣的。 `/app/models/User.php`:
<?php class User extends Eloquent { protected $table = 'users'; public function hasOneAccount() { return $this->hasOne('Account', 'user_id', 'id'); } }
然后,當(dāng)我們需要用到這種關(guān)系的時候,該如何使用呢?如下:
$account = User::find(10)->hasOneAccount;
此時得到的 `$account` 即為 `Account` 類的一個實例。
這里最難的地方在于后面的兩個 foreign_key 和 local_key 的設(shè)置,大家可以就此記?。涸?User 類中,無論 hasOne 誰,第二個參數(shù)都是 `user_id`,第三個參數(shù)一般都是 `id`。由于前面的 `find(10)` 已經(jīng)鎖定了 id = 10,所以這段函數(shù)對應(yīng)的 SQL 為: `select * from account where user_id=10`。
這段代碼除了展示了一對一關(guān)系該如何使用之外,還傳達(dá)了三點信息,也是我對于大家使用 Eloquent 時候的建議:
(1). 每一個 Model 中都指定表名
(2). has one account 這樣的關(guān)系寫成 `hasOneAccount()` 而不是簡單的 `account()`
(3). 每次使用模型間關(guān)系的時候都寫全參數(shù),不要省略
相應(yīng)的,如果使用 belongsTo() 關(guān)系,應(yīng)該這么寫:
<?php class Account extends Eloquent { protected $table = 'accounts'; public function belongsToUser() { return $this->belongsTo('User', 'user_id', 'id'); } }
2.一對多關(guān)系
學(xué)會了前面使用一對一關(guān)系的基礎(chǔ)方法,后面的幾種關(guān)系就簡單多了。
我們引入一個新的Model:Pay,付款記錄。表結(jié)構(gòu)應(yīng)該是這樣的:
user: id ... ... pay: id ... ... user_id
User 和 Pay 具有一對多關(guān)系,換句話說就是一個 User 可以有多個 Pay,這樣的話,只在 Pay 表中存在一個 `user_id` 字段即可。 `/app/models/User.php`:
<?php class User extends Eloquent { protected $table = 'users'; public function hasManyPays() { return $this->hasMany('Pay', 'user_id', 'id'); } }
然后,當(dāng)我們需要用到這種關(guān)系的時候,該如何使用呢?如下:
$accounts = User::find(10)->hasManyPays()->get();
此時得到的 `$accounts` 即為 `Illuminate\Database\Eloquent\Collection` 類的一個實例。大家應(yīng)該也已經(jīng)注意到了,這里不是簡單的 `-> hasOneAccount` 而是 `->hasManyPays()->get()`,為什么呢?因為這里是 `hasMany`,操作的是一個對象集合。
相應(yīng)的 belongsTo() 的用法跟上面一對一關(guān)系一樣:
<?php class Pay extends Eloquent { protected $table = 'pays'; public function belongsToUser() { return $this->belongsTo('User', 'user_id', 'id'); } }
3.多對多關(guān)系
多對多關(guān)系和之前的關(guān)系完全不一樣,因為多對多關(guān)系可能出現(xiàn)很多冗余數(shù)據(jù),用之前自帶的表存不下了。
我們定義兩個模型:Article 和 Tag,分別表示文章和標(biāo)簽,他們是多對多的關(guān)系。表結(jié)構(gòu)應(yīng)該是這樣的:
article: id ... ... tag: id ... ... article_tag: article_id tag_id
在 Model 中使用:
<?php class Tag extends Eloquent { protected $table = 'tags'; public function belongsToManyArticle() { return $this->belongsToMany('Article', 'article_tag', 'tag_id', 'article_id'); } }
需要注意的是,第三個參數(shù)是本類的 id,第四個參數(shù)是第一個參數(shù)那個類的 id。
使用跟 hasMany 一樣:
$tagsWithArticles = Tag::take(10)->get()->belongsToManyArticle()->get();
這里會得到一個非常復(fù)雜的對象,可以自行 `var_dump()`。跟大家說一個訣竅,`var_dump()` 以后,用 Chrome 右鍵 “查看源代碼”,就可以看到非常整齊的對象/數(shù)組展開了。
在這里給大家展示一個少見用法(奇技淫巧):
public function parent_video() { return $this->belongsToMany($this, 'video_hierarchy', 'video_id', 'video_parent_id'); } public function children_video() { return $this->belongsToMany($this, 'video_hierarchy', 'video_parent_id', 'video_id'); }
對,你沒有看錯,可以 belongsToMany 自己。
其他關(guān)系
Eloquent 還提供 “遠(yuǎn)層一對多關(guān)聯(lián)”、“多態(tài)關(guān)聯(lián)” 和 “多態(tài)的多對多關(guān)聯(lián)” 這另外三種用法,經(jīng)過上面的學(xué)習(xí),我們已經(jīng)掌握了 Eloquent 模型間關(guān)系的基本概念和使用方法,剩下的幾種不常用的方法就留到我們用到的時候再自己探索吧。
重要技巧:關(guān)系預(yù)載入
你也許已經(jīng)發(fā)現(xiàn)了,在一對一關(guān)系中,如果我們需要一次性查詢出10個 User 并帶上對應(yīng)的 Account 的話,那么就需要給數(shù)據(jù)庫打 1 + 10 條 SQL,這樣性能是很差的。我們可以使用一個重要的特性,關(guān)系預(yù)載入:http://laravel-china.org/docs/eloquent#eager-loading
直接上代碼:
$users = User::with('hasOneAccount')->take(10)->get()
這樣生成的 SQL 就是這個樣子的:
select * from account where id in (1, 2, 3, ... ...)
這樣 1 + 10 條 SQL 就變成了 1 + 1 條,性能大增。
相關(guān)文章
真正根據(jù)utf8編碼的規(guī)律來進(jìn)行截取字符串的函數(shù)(utf8版sub_str )
真正根據(jù)utf8編碼的規(guī)律來進(jìn)行截取的字符的函數(shù),utf8版sub_str 支持1~6個字節(jié)的字符的截取,而非只針對中文,比網(wǎng)上的全2012-10-10javascript,php獲取函數(shù)參數(shù)對象的代碼
javascript,php獲取函數(shù)參數(shù)對象的代碼,需要的朋友可以參考下。2011-02-02PHP里面把16進(jìn)制的圖片數(shù)據(jù)顯示在html的img標(biāo)簽上(實現(xiàn)方法)
下面小編就為大家?guī)硪黄狿HP里面把16進(jìn)制的圖片數(shù)據(jù)顯示在html的img標(biāo)簽上(實現(xiàn)方法)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05PHP+mysql實現(xiàn)的三級聯(lián)動菜單功能示例
這篇文章主要介紹了PHP+mysql實現(xiàn)的三級聯(lián)動菜單功能,涉及mysql數(shù)據(jù)庫創(chuàng)建、數(shù)據(jù)添加及php讀取mysql、創(chuàng)建聯(lián)動菜單相關(guān)操作技巧,需要的朋友可以參考下2019-02-02php中將數(shù)組轉(zhuǎn)成字符串并保存到數(shù)據(jù)庫中的函數(shù)代碼
為了方便可以把php的數(shù)組轉(zhuǎn)換為 字符串保存到數(shù)據(jù)庫中。當(dāng)需要用時就可以取出字符串再轉(zhuǎn)成數(shù)組即可。下為為php數(shù)組與字符串相互轉(zhuǎn)換的自定義函數(shù)2013-09-09