Fork me on GitHub

laravel5.2总结--关联关系

 参考文章
 
以下所用到的实例,包含在https://github.com/archer-wong/laravel-orm-relationships工程中,方便使用。
 
你可在 Eloquent 模型类内将 Eloquent 关联定义为函数。因为关联像 Eloquent 模型一样也可以作为强大的查询语句构造器,定义关联为函数提供了强而有力的链式调用及查找功能。
 

1 一对一关系

1.1 表A和表B的记录一一对应,比如一个用户对应一个社交账号
 
1.2 定义模型User(位于app/Models文件夹下),并在其中定义与UserAccount的一对一对应关系:
public function getAccount() { return $this->hasOne('App\Models\UserAccounts'); }
注意:hasOne()方法的3种不同的使用情况: Eloquent 默认关联关系的外键基于模型名称。默认是:UserAccount模型的主键是id,关联的外键是user_id  
1> hasOne('App\Models\UserAccounts') - 默认情况  
2> hasOne('App\Models\UserAccounts', 'foreign_key') -UserAccount模型的主键是id,但是外键不是 user_id  
3> hasOne('App\Models\UserAccounts', 'foreign_key', 'local_key') - UserAccount模型的主键不是id,外键也不是user_id  
第一个参数是关联模型的类名称,
第二个参数,Eloquent 会假设对应关联的外键名称是基于模型名称的,会取用自身模型的「蛇形命名」后的名称,并在后方加上 _id,所以定义模型时候要注意,模型的名称加上'_id'的组合作为外键,我们这里外键=>'user'+'_id',也就是$foreign_key = 'user_id'
第三个参数,Eloquent 的默认外键在上层模型的 id 字段会有个对应值。
 
1.3 我们也可以在UserAccount模型中定义与User的一对一关系:
public function user() { return $this->belongsTo('App\Models\User'); }
注意:belongsTo()方法的3种不同的使用情况: Eloquent 默认关联关系的外键基于关联方法名称。默认是:UserAccount模型的主键是id,关联的外键是user_id  
1> belongsTo('App\Models\User') - 默认情况  
2> belongsTo('App\Models\User', 'foreign_key') -UserAccount模型的主键是id,但是外键不是 user_id  
3> belongsTo('App\Models\User', 'foreign_key', 'local_key') - UserAccount模型的主键不是id,外键也不是user_id  
默认情况下,Eloquent将调用belongsTo的关联方法名user作为关联关系$relation的值,并将$relation.'_id'作为默认外键名对应users表的id,如果表中没有相应列,又没有在定义关联关系的时候指定具体的外键,就会报错。
 
1.4 控制器中的调用
public function oneToOne(){
$user_account = User::find(1)->getAccount;
$user = UserAccount::find(1)->user;
dd($user_account, $user);
}
调用:  
关联关系被定义后,可以使用 Eloquent 的 '动态属性' 来获取关联关系!  
注意:  
动态属性:允许我们访问关联函数,就像它们是定义在模型上的属性一样!         
理解 '动态属性' 的概念:按理说,我们定义了 getAccount() 方法,应该调用的是一个方法,而这里将其作为了一个 '属性' 来调用!  
 
1.5 总结:不管是User模型类,还是UserAccount模型类,2者都是以 'User' 模型为主。UserAccount模型还是附属于User模型。UserAccount模型具有外键 'user_id' ,但是要注意hasOne()方法的外键基于模型名称,belongsTo()方法的外键基于关联方法名称的。
调用结果:

2 一对多关系

2,1 表A的某条记录对应表B的多条记录,反之表B的某条记录归属于表A的某条记录,比如一个用户发表多篇文章
2.2 我们在用户模型User中定义与文章模型Post的一对多关系如下:
public function getPosts() { return $this->hasMany('App\Models\Post'); }
当然,因为所有的关联也都提供了查询语句构造器的功能,因此你可以对获取到的评论进一步增加条件,通过调用 posts()方法然后在该方法后面链式调用查询条件:
$posts = User::find(1)->getPosts()->where('title','vitae')->get(); dd($posts);
需要注意的是这里我们调用的是getPosts()方法,而不是动态属性getPosts,当然使用getPosts动态属性也可以支持链式调用。
 
2.3 同样,我们可以在文章模型Post中定义文章所属用户模型User的对应关系:
//1> 最基本写法
public function user() { return $this->belongsTo('App\Models\User'); }
//2> 当定义的方法名不是user的时候,传入了额外参数,意为posts表中的user_id对应users表中的id
public function author() { return $this->belongsTo('App\Models\User', 'user_id', 'id'); }
2.4 控制器中调用
public function oneToMany(){
$post = User::find(1)->getPosts;
//如果我们想增加更多的附加条件,可以使用posts()方法,这样仍然支持链式。
$post2 = User::find(1)->getPosts()->where('title','vitae')->get();
//区分user和user2的区别
$user = Post::find(1)->user;
$user2 = Post::find(1)->user();
//注意author方法的话,需要增加键的限制
$author = Post::find(1)->author;
dd($post, $post2, $user, $user2, $author);
}
2.4 总结
写法和一对一关系中非常相像。

3 多对多关系

3.1 表A的某条记录通过中间表C与表B的多条记录关联,反之亦然。比如一个用户有多种角色,反之一个角色对应多个用户。
 
提示:定义中间表的时候没有在结尾加s并且命名规则是按照字母表顺序,将role放在前面,user放在后面,并且用_分隔,在定义多对多关联的时候如果没有指定中间表,Eloquent默认的中间表使用这种规则拼接出来,比如模型1 user,模型2 role 那么表名就是role_user,Eloquent默认的中间表使用这种规则拼接出来。
 
3.2 我们在模型User中定义多对多关联如下:
public function getRoles() { return $this->belongsToMany('App\Models\Role'); }
注意:
完整写法:
return $this->belongsToMany('App\Models\Role', 'user_roles', 'user_id', 'role_id');
Eloquent 会合并两个关联模型的名称并依照字母顺序命名。当然你也可以随意重写这个约定。可通过传递第二个参数至 belongsToMany 方法来实现:
return $this->belongsToMany('App\Models\Role', 'user_roles');
除了自定义合并数据表的名称,你也可以通过传递额外参数至 belongsToMany 方法来自定义数据表里的键的字段名称。第三个参数是你定义在关联中的模型外键名称,而第四个参数则是你要合并的模型外键名称:
 
3.3 相对的我们也可以在模型Role中定义获取对应User模型的方法:
public function getUsers() { return $this->belongsToMany('App\Models\Users'); }
 
3.4 此外我们还可以通过动态属性pivot获取中间表字段:
$roles = Users::find(1)->roles; foreach ($roles as $role) { echo $role->pivot->role_id.'<br>'; }
注意:我们取出的每个 Role 模型对象,都会被自动赋予 pivot 属性。此属性代表中间表的模型,它可以像其它的 Eloquent 模型一样被使用。
默认情况下,pivot 对象只提供模型的键。
如果你的 pivot 数据表包含了其它的属性,则可以在定义关联方法时指定那些字段:
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
如果你想要中间表自动维护 created_at 和 updated_at 时间戳,可在定义关联方法时加上 withTimestamps 方法:
return $this->belongsToMany('App\Role')->withTimestamps();
 
3.5 控制器中调用:
public function manyToMany(){
$roles = User::find(1)->getRoles;
$users = Role::find(1)->getUsers;
//通过动态属性pivot获取中间表字段
foreach ($roles as $role) {
$pivot = $role->pivot->role_id;
echo $pivot . '<br>';
}
dd($roles, $users);
}
 

4 远层一对多

4.1 所谓的“远层一对多”指的是通过一个中间关联对象访问远层的关联关系,比如国家与用户之间存在一对多关系,用户与文章之间也存在一对多关系,那么通过用户可以建立国家与文章的之间的一对多关联关系,我们称之为“远层一对多”。
4.2 我们创建一个模型Country,并在其中定义国家与文章的远层一对多关系如下:
public function getPostsThroughUser() { return $this->hasManyThrough('App\Models\Post','App\Models\User'); }
其中第一个参数是关联对象类名,第二个参数是中间对象类名。
当运行关联查找时,通常会使用 Eloquent 的外键约定。如果你想要自定义关联的键,则可以将它们传递至 hasManyThrough 方法的第三与第四个参数。第三个参数为中间模型的外键名称,而第四个参数为最终模型的外键名称.
$this->hasManyThrough('App\Models\Post','App\Models\User',$country_id,$user_id);
4.3 接下来我们在控制器中定义测试代码如下:
public function hasManyThrough(){
$country = Country::find(1);
$posts = $country->getPostsThroughUser;
echo 'Country#'.$country->name.'下的文章:<br>';
foreach($posts as $post){
echo '&lt;&lt;'.$post->title.'&gt;&gt;<br>';
}
}
 
页面会输出:该国家下对应的所有文章
Country#China下的文章:
<<vitae>>
<<officiis>>
<<hic>>
<<quam>>
<<veritatis>>
 

5 多态关联

5.1 多态关联允许一个模型在单个关联下属于多个不同父模型。比方,某一条评论可能归属于某篇文章,也可能归属于某个视频。我们可以在评论表中添加一个item_id字段表示其归属节点ID,同时定义一个item_type字段表示其归属节点类型。
 
5.2 分别定义三个模型
在Post和Video模型类中定义关联评论:
public function comments() { return $this->morphMany('App\Models\Comment','item'); }
 
同样也可以在Comment模型中定义相对的关联关系获取其所属节点:
public function item() { return $this->morphTo(); }
//扩展,当不使用item作为方法名,可以传入自定义参数
public function getItem()
{
return $this->morphTo('item', 'item_type', 'item_id');
}
 
完整写法:
1> $this->morphMany('App\Models\Comment',$item,$item_type,$item_id,$id);
其中第一个参数是关联模型类名,第二个参数是关联名称,即$item_id和$item_type中的$item部分,最后一个参数是posts/videos表的主键。
2> $this->morphTo($item,$item_type,$item_id); 如果$item部分不等于item可以自定义传入参数到morphTo:
 
5.3 控制器中调用:
public function polymorphicRelations(){
$video = Video::find(1);
$videoComments = $video->comments;
$comment = Comment::find(1);
$item = $comment->item;
dd($videoComments, $item);
}
 
输出结果如下
 
 

6 多态多对多关联

6.1 最常见的应用场景就是标签,比如一篇文章对应多个标签,一个视频也对应多个标签,同时一个标签可能对应多篇文章或多个视频,
 
6.2 定义模型方法
在Post/Video中定义关联关系如下:
public function tags() { return $this->morphToMany('App\Models\Tag','taggable'); }
在Tag中定义相对的关联关系如下:
public function posts() { return $this->morphedByMany('App\Models\Post','taggable'); } public function videos() { return $this->morphedByMany('App\Models\Video','taggable'); }
完整写法:
1> $this->morphToMany('App\Models\Tag','taggable','taggable','taggable_id','tag_id',false);
其中第一个参数是关联模型类名,第二个参数是关联关系名称,其中第三个参数是对应关系表名,最后一个值若为true,则查询的是关联对象本身,若为false,查询的是关联对象与父模型的对应关系。
2> $this->morphedByMany('App\Models\Video','taggable','taggable','tag_id','taggable_id');
其中第一个参数是关联对象类名,第二个参数是关联关系名称,其中第三个参数是对应关系表名。
 
6.3 控制器中调用
public function manyToManyPolymorphicRelations(){
$post = Post::find(1);
$tags = $post->tags;
$tag = Tag::find(1);
$posts = $tag->posts;
dd($tags, $posts);
}
结果:
 
 
 
 
 
 
 
 
 
posted @ 2016-12-21 08:41  archer-wong  阅读(2284)  评论(0编辑  收藏  举报