laravel支援多種模型之間的relation,對應著模型間的one2one, one2many,many2many,hasManyThrough,Polymorphic, many2many polymorphic關係。
心法
1.所有relation都由model class上的方法來定義;
2. relationship和model本身都是以query builder作為基類的,因此對relation的操作也可以使用類似query builder的方法,比如:可以支援級聯;
3.Dynamic property of model:這是 model->relation方式引用的結果,這種方式直接返回relation model的value,不支援query builder的級聯操作,這是和model->relation()方法呼叫的本質區別。
也就是說model->relation = model->relation()->get()
關係的種類:
one 2 one(一對一)
例子User hasOne Phone, Phone belongsTo User這種關係。要使得這種關係work,則需要以下前提:
1. User模型
base table: users,
欄位預設: id
relation method: phone (){ return $this->hasOne('App\Phone') }
注意:按照laravel的命名規約,laravel會假設phones表中有一個user_id欄位作為users表中的id外來鍵,
我們可以通過在user()方法中傳入另外的引數來覆蓋這種規約,比如$this->hasOne('App\Phone','owner_id')
在這裡owner_id就是phones表中外來鍵引用users表的id的欄位名稱(預設為user_id);
2.Phone模型
base table: phones,
欄位預設: user_id
relation method: user(){ return $this->belongsTo('App\User')}
在這裡,laravel也會預設parent表(也就是users表)中有一個id欄位為primary key,如果你的parent表並不是使用id作為主鍵,則在上面的hasOne呼叫中,可以使用第三個引數來指定這個主鍵,比如
user(){return $this->hasOne('App\User','owner_id','u_id'}. 這裡owner_id為phones表中對users表主鍵(u_id)的外來鍵引用欄位名稱
One 2 Many(一對多)
例子: Post hasMany Comments, Comment belongsTo Post,這就是典型的一對多關係模型
1. Post模型
base table: posts
欄位預設:id標示行主鍵
relation method: comments(){return $this->hasMany('App\Comment')}
如果需要覆蓋預設命名規約,我們需要這樣呼叫:
public function comments(){return $this->hasMany('App\Comment','foreign_owner_postid_key','local_postid_key')}
注意這裡:foreign_owner_postid_key就是要在comments表中具有的欄位名;
local_postid_key就是posts本表中必須具有的主鍵欄位名
2. Comment模型
base table: comments
欄位預設: post_id 作為posts表的外來鍵
relation method: post(){return $this->belongsTo('App\Post')}
如果需要覆蓋預設命名規約,同樣我們需要這樣呼叫:
public function post(){return $this->belongsTo('App\Post','foreign_owner_postid_key','other_local_postid_key'}
注意:這裡foreign_owner_postid_key就是在本comments表中必須具有的引用posts表的外來鍵欄位名;
other_local_postid_key就是必須在posts表中具有的主鍵名稱。之所以加local,other就是代表的是本表的還是關聯表的。
另外,上面也已經提到由於relationship本身就是query builder因此可以增加約束級聯,比如:
Post::find(1)->comments()->where('title','foo')->first()就只取title為foo的第一條post對應的comment
other_local_postid_key就是
注意按照laravel預設命名規約,一對多的關係模型,child model中的owning model
many 2 many(多對多)
例子: User belongsToMany Role, Role belongsToMany User
1. User模型
base table: users
欄位預設: id主鍵
relation method: roles(){return $this->belongsToMany('App\Role')}
其他預設: role_user pivot表(即兩個小寫關係之間按照字母排序使用 _ 連線起來)
2. Role模型
base table: roles
欄位預設: id主鍵
relation method: users(){return $this->belongsToMany('App\User')}
其他預設: role_user pivot表(即兩個小寫關係之間按照字母排序使用 _ 連線起來)
上面的兩個model中都有預設pivot表,我們可以通過傳參來覆蓋它(注意這裡只需要在一個方向上呼叫即可,不用像上面one 2 many需要兩邊都這樣呼叫才能work!):
public function roles(){return $this->belongsToMany('App\Role','user_role','user_pivot_ziduan_name','role_ziduan_name_in_pivot')}
再看一個使用query builder特性的例子:$roles=App\User::find(1)->roles()->orderBy('name')->get()
也可以在訪問relation的同時訪問pivot表資料:
public function roles(){$this->belongsToMany('App\Role')->withPivot('column1_in_pivot','c2_in_pivot')}
foreach ($user->roles as $role){ echo $role->pivot->column1_in_pivot}
上面程式碼中獲取relation的同時,也返回pivot表格的對應資料,並且可以作為pivot relation來運算元據
has Many through(通過xx一對多)
例子:由於Country hasMany User,同時User hasMany Post,所以Country可以hasMany Post via User(注意同時User belongsTo Countery, Post belongsTo User)
1. Country模型
base table: countries
欄位預設: id, name
relation methods:
public function users(){return $this->hasMany('App\User')}
public function posts(){return $this->hasManyThrough('App\Post')}
2.User模型
base table: users
欄位預設: id, country_id, username
relation method: country(){return $this->belongsTo('App\Country')}
3.Post模型
base table: posts
欄位預設: id, user_id, title,description
relation method: user(){return $this->belongsTo('App\User')}
Polymorphic relation(多型一對多)
例子:Photo可以morphTo Staff, 同時Photo也可以morphTo Product, Product可以morphMany Photo, Staff也可以morphMany Photo,也就是說一個model可以belongsTo多種parentmodel,多種parentmodel都可以hasMany這個model時,非常適合這種polymorphic relation
1. Staff模型:
base table: staffs
欄位預設: id, name
relation method: photos(){return $this->morphMany('App\Photo','imagable')} //注意imagable是Photo模型的一個relation method
2.Product模型
base table: products
欄位預設: id, name, price
relation method: photos(){return $this->morphMany('App\Photo','imagable')}//注意imagable是Photo模型的一個relation method
3. Photo模型
base table: photos
欄位預設: id,path,imageable_id,imageable_type //注意imageable就是本模型中的relation method名稱,這裡imageable_id和imageable_type非常關鍵,也是laravel根據photos表找到對應photos屬於哪種模型的關鍵,也就是說一張圖片到底是staff的圖片還是產品product的圖片,就看這個type和id了,由type找到是哪張表,id再找到這張表中第幾條資料。預設情況下laravel會使用Model的class全稱作為type,但是有時你可能並不希望如此,這時,一個可行的方案是建立一個types表,來指定這種對應關係,同時在AppServiceProvider booted方法中呼叫
Relation::morphMap($morphMapArray);
來完成這種人性化的對映關係
Many 2 Many Polymorphic Relations
例子: Post可以打上多個Tag標籤, Video也可以同樣打上多個Tag,也就是說Tag可以同時打在video或者post上。
如何在model中增加一個不存在於base table欄位中的欄位?
很多時候存在這樣的場景:你很難找到一個適合的relation來描述兩個表格之間的關係,或者你不需要建立複雜的relation,你只需要在你返回的model中新增一條額外的資訊,比如,User model中你可能需要一條額外的欄位'isAdmin'來方便判斷該使用者是否admin使用者,這時可以這樣操作:(但是好像動態訪問不是很好用)
https://laravel.com/docs/5.1/eloquent-serialization#appending-values-to-json
class User extends Model { protected $appends = ['is_admin']; /** * Get the administrator flag for the user. * * @return bool */ public function getIsAdminAttribute() { return $this->attributes['admin'] == 'yes'; } }
laravel relation相關簡單除錯手段
雖然laravel的relation非常強大,只要你稍加熟悉,你就能花非常少的effort構建出非常強大的web後端應用來。但是有時候,一些高階的sql查詢,laravel可能並未按照我們的預期工作,這時,我們就希望eloquent最終到底對映成了什麼sql,有幾種方法獲取這方面的資訊:
1. get()替換為toSql()直接列印出來;
$results = User::where(function($q) use ($request) { $q->orWhere('email', 'like', '%john@example.org%'); $q->orWhere('first_name', 'like', '%John%'); $q->orWhere('last_name', 'like', '%Doe%'); })->toSql();
2. listen事件
\DB::listen(function($sql) { var_dump($sql); });
https://scotch.io/tutorials/debugging-queries-in-laravel
查詢relation時針對relation實現限制條件:(has, doesnotHave)
// Retrieve all posts with at least one comment containing words like foo% $posts = Post::has('comments.votes')->get(); // 簡化版 $posts = Post::whereHas('comments', function ($query) { $query->where('content', 'like', 'foo%'); })->get(); $posts = App\Post::doesntHave('comments')->get(); // 簡化版 $posts = Post::whereDoesntHave('comments', function ($query) { $query->where('content', 'like', 'foo%'); })->get();
hasManyThrough 多對多?
http://laravel.io/forum/03-04-2014-hasmanythrough-with-many-to-many