未加星标

[ Laravel 5.3 文档 ] Eloquent ORM ―― 关联关系

字体大小 | |
[开发(php) 所属分类 开发(php) | 发布者 店小二03 | 时间 2016 | 作者 红领巾 ] 0人收藏点击收藏
1、简介

数据表经常要与其它表做关联,比如一篇博客文章可能有很多评论,或者一个订单会被关联到下单用户,Eloquent使得组织和处理这些关联关系变得简单,并且支持多种不同类型的关联关系:

一对一 一对多 多对多 远层一对多 多态关联 多对多的多态关联 2、定义关联关系

Eloquent 关联关系以Eloquent模型类方法的形式被定义。和 Eloquent 模型本身一样,关联关系也是强大的查询构建器,定义关联关系为函数能够提供功能强大的方法链和查询能力。例如:

$user->posts()->where('active', 1)->get();

但是,在深入使用关联关系之前,让我们先学习如何定义每种关联类型:

一对一

一对一关联是一个非常简单的关联关系,例如,一个 User 模型有一个与之对应的 Phone 模型。要定义这种模型,我们需要将 phone 方法置于 User 模型中, phone 方法会调用 Eloquent 模型基类上 hasOne 方法并返回其结果:

<php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model{
/**
* 获取关联到用户的手机
*/
public function phone()
{
return $this->hasOne('App\Phone');
}
}

传递给 hasOne 方法的第一个参数是关联模型的名称,关联关系被定义后,我们可以使用 Eloquent 的动态属性获取关联记录。动态属性允许我们访问关联函数就像它们是定义在模型上的属性一样:

$phone = User::find(1)->phone;

Eloquent 默认关联关系的外键基于模型名称,在本例中, Phone 模型默认有一个 user_id 外键,如果你希望重写这种约定,可以传递第二个参数到 hasOne 方法:

return $this->hasOne('App\Phone', 'foreign_key');

此外,Eloquent 假设外键应该在父级上有一个与之匹配的 id ,换句话说,Eloquent 将会通过 user 表的 id 值去 phone 表中查询 user_id 与之匹配的 Phone 记录。如果你想要关联关系使用其他值而不是 id ,可以传递第三个参数到 hasOne 来指定自定义的主键:

return $this->hasOne('App\Phone', 'foreign_key', 'local_key'); 定义相对的关联

我们可以从 User 中访问 Phone 模型,相应的,我们也可以在 Phone 模型中定义关联关系从而让我们可以拥有该 phone 的 User 。我们可以使用 belongsTo 方法定义与 hasOne 关联关系相对的关联:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Phone extends Model{
/**
* 获取手机对应的用户
*/
public function user()
{
return $this->belongsTo('App\User');
}
}

在上面的例子中,Eloquent 将会尝试通过 Phone 模型的 user_id 去 User 模型查找与之匹配的记录。Eloquent 通过关联关系方法名并在方法名后加 _id 后缀来生成默认的外键名。然而,如果 Phone 模型上的外键不是 user_id ,也可以将自定义的键名作为第二个参数传递到 belongsTo 方法:

/**
* 获取手机对应的用户
*/
public function user(){
return $this->belongsTo('App\User', 'foreign_key');
}

如果父模型不使用 id 作为主键,或者你希望使用别的列来连接子模型,可以将父表自定义键作为第三个参数传递给 belongsTo 方法:

/**
* 获取手机对应的用户
*/
public function user(){
return $this->belongsTo('App\User', 'foreign_key', 'other_key');
} 一对多

“一对多”是用于定义单个模型拥有多个其它模型的关联关系。例如,一篇博客文章拥有无数评论,和其他关联关系一样,一对多关联通过在 Eloquent 模型中定义方法来定义:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model{
/**
* 获取博客文章的评论
*/
public function comments()
{
return $this->hasMany('App\Comment');
}
}

记住,Eloquent 会自动判断 Comment 模型的外键,为方便起见,Eloquent 将拥有者模型名称加上 _id 后缀作为外键。因此,在本例中,Eloquent 假设 Comment 模型上的外键是 post_id 。

关联关系被定义后,我们就可以通过访问 comments 属性来访问评论集合。记住,由于 Eloquent 提供“动态属性”,我们可以像访问模型的属性一样访问关联方法:

$comments = App\Post::find(1)->comments;
foreach ($comments as $comment) {
//
}

当然,由于所有关联同时也是查询构建器,我们可以添加更多的条件约束到通过调用 comments 方法获取到的评论上:

$comments = App\Post::find(1)->comments()->where('title', 'foo')->first();

和 hasOne 方法一样,你还可以通过传递额外参数到 hasMany 方法来重新设置外键和本地主键:

return $this->hasMany('App\Comment', 'foreign_key');
return $this->hasMany('App\Comment', 'foreign_key', 'local_key'); 一对多(逆向)

现在我们可以访问文章的所有评论了,接下来让我们定义一个关联关系允许通过评论访问所属文章。要定义与 hasMany 相对的关联关系,需要在子模型中定义一个关联方法去调用 belongsTo 方法:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model{
/**
* 获取评论对应的博客文章
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}

关联关系定义好之后,我们可以通过访问动态属性 post 来获取一条 Comment 对应的 Post :

$comment = App\Comment::find(1);
echo $comment->post->title;

在上面这个例子中,Eloquent 尝试匹配 Comment 模型的 post_id 与 Post 模型的 id ,Eloquent 通过关联方法名加上 _id 后缀生成默认外键,当然,你也可以通过传递自定义外键名作为第二个参数传递到 belongsTo 方法,如果你的外键不是 post_id ,或者你想自定义的话:

/**
* 获取评论对应的博客文章
*/
public function post(){
return $this->belongsTo('App\Post', 'foreign_key');
}

如果你的父模型不使用 id 作为主键,或者你希望通过其他列来连接子模型,可以将自定义键名作为第三个参数传递给 belongsTo 方法:

/**
* 获取评论对应的博客文章
*/
public function post(){
return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
} 多对多

多对多关系比 hasOne 和 hasMany 关联关系要稍微复杂一些。这种关联关系的一个例子就是一个用户有多个角色,同时一个角色被多个用户共用。例如,很多用户可能都有一个“Admin”角色。要定义这样的关联关系,需要三个数据表: users 、 roles 和 role_user , role_user 表按照关联模型名的字母顺序命名,并且包含 user_id 和 role_id 两个列。

多对多关联通过编写一个调用 Eloquent 基类上的 belongsToMany 方法的函数来定义,例如,我们在User模型上定义roles方法:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model{
/**
* 用户角色
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}

关联关系被定义之后,可以使用动态属性 roles 来访问用户的角色:

$user = App\User::find(1);
foreach ($user->roles as $role) {
//
}

当然,和所有其它关联关系类型一样,你可以调用 roles 方法来添加条件约束到关联查询上:

$roles = App\User::find(1)->roles()->orderBy('name')->get();

正如前面所提到的,为了决定关联关系连接表的表名,Eloquent以字母顺序连接两个关联模型的名字。然而,你可以重写这种约定――通过传递第二个参数到 belongsToMany 方法:

return $this->belongsToMany('App\Role', 'user_roles');

除了自定义连接表的表名,你还可以通过传递额外参数到 belongsToMany 方法来自定义该表中字段的列名。第三个参数是你定义的关系模型的外键名称,第四个参数你要连接到的模型的外键名称:

return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id'); 定义相对的关联关系

要定义与多对多关联相对的关联关系,只需在关联模型中在调用一下 belongsToMany 方法即可。让我们在 Role 模型中定义 users 方法:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model{
/**
* 角色用户
*/
public function users()
{
return $this->belongsToMany('App\User');
}
}

正如你所看到的,定义的关联关系和与其对应的 User 中定义的一模一样,只是前者引用 App\Role ,后者引用 App\User ,由于我们再次使用了 belongsToMany 方法,所有的常用表和键自定义选项在定义与多对多相对的关联关系时都是可用的。

获取中间表字段

正如你已经学习到的,处理多对多关联要求一个中间表。Eloquent 提供了一些有用的方法来与其进行交互,例如,我们假设 User 对象有很多与之关联的 Role 对象,访问这些关联关系之后,我们可以使用模型上的 pivot 属性访问中间表:

$user = App\User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}

注意我们获取到的每一个 Role 模型都被自动赋上了 pivot 属性。该属性包含一个代表中间表的模型,并且可以像其它 Eloquent 模型一样使用。

默认情况下,只有模型键才能用在 pivot 对象上,如果你的 pivot 表包含额外的属性,必须在定义关联关系时进行指定:

return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');

如果你想要你的 pivot 表自动包含 created_at 和 updated_at 时间戳,在关联关系定义时使用 withTimestamps 方法:

return $this->belongsToMany('App\Role')->withTimestamps();

通过中间表字段过滤关联关系

你还可以在定义关联关系的时候使用 wherePivot 和 wherePivotIn 方法过滤 belongsToMany 返回的结果集:

return $this->belongsToMany('App\Role')->wherePivot('approved', 1);
return $this->belongsToMany('App\Role')->wherePivotIn('priority', [1, 2]); 远层的一对多

“远层一对多”关联为通过中间关联访问远层的关联关系提供了一个便利之道。例如, Country 模型通过中间的 User 模型可能拥有多个 Post 模型。在这个例子中,你可以轻易的聚合给定国家的所有文章,让我们看看定义这个关联关系需要哪些表:

countries
id - integer
name - string
users
id - integer
country_id - integer
name - string
posts
id - integer
user_id - integer
title - string

尽管 posts 表不包含 country_id 列, hasManyThrough 关联提供了通过 $country->posts 来访问一个国家的所有文章。要执行该查询,Eloquent 在中间表 $users 上检查 country_id ,查找到相匹配的用户ID后,通过用户ID来查询 posts 表。

既然我们已经查看了该关联关系的数据表结构,接下来让我们在 Country 模型上进行定义:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Country extends Model{
/**
* 获取指定国家的所有文章
*/
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User');
}
}

第一个传递到 hasManyThrough 方法的参数是最终我们希望访问的模型的名称,第二个参数是中间模型名称。

当执行这种关联查询时通常 Eloquent 外键规则会被使用,如果你想要自定义该关联关系的外键,可以将它们作为第三个、第四个参数传递给 hasManyThrough 方法。第三个参数是中间模型的外键名,第四个参数是最终模型的外键名。

class Country extends Model{
public function posts()
{
return $this->hasManyThrough('App\Post', 'App\User', 'country_id', 'user_id');
}
} 多态关联 表结构

多态关联允许一个模型在单个关联下属于多个不同模型。例如,假设应用用户既可以对文章进行评论也可以对视频进行评论,使用多态关联,你可以在这两种场景下使用单个 comments 表,首先,让我们看看构建这种关联关系需要的表结构:

posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
comments
id - integer
body - text
commentable_id - integer
commentable_type - string

两个重要的需要注意的列是 comments 表上的 commentable_id 和 commentable_type 。 commentable_id 列对应 Post 或 Video 的 ID 值,而 commentable_type 列对应所属模型的类名。当访问 commentable 关联时,ORM根据 commentable_type 字段来判断所属模型的类型并返回相应模型实例。

模型结构

接下来,让我们看看构建这种关联关系需要在模型中定义什么:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* Get all of the owning commentable models.
*/
public function commentable()
{
return $this->morphTo();
}
}
class Post extends Model
{
/**
* Get all of the post's comments.
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
class Video extends Model
{
/**
* Get all of the video's comments.
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
} 获取多态关联

数据表和模型定义好以后,可以通过模型访问关联关系。例如,要访问一篇文章的所有评论,可以通过使用动态属性 comments :

$post = App\Post::find(1);
foreach ($post->comments as $comment) {
//
}

你还可以通过调用 morphTo 方法从多态模型中获取多态关联的所属对象。在本例中,就是 Comment 模型中的 commentable 方法。因此,我们可以用动态属性的方式访问该方法:

$comment = App\Comment::find(1);
$commentable = $comment->commentable;

Comment 模型的 commentable 关联返回 Post 或 Video 实例,这取决于哪个类型的模型拥有该评论。

自定义多态类型

默认情况下,Laravel使用完全限定类名来存储关联模型的类型。举个例子,上面示例中的 Comment 可能属于某个 Post 或 Video ,默认的 commentable_type 可能是 App\Post 或 App\Video 。不过,有时候你可能需要解除数据库和应用内部结构之间的耦合,这样的情况下,可以定义一个morphMap关联来告知Eloquent为每个模型使用自定义名称替代完整类名:

use Illuminate\Database\Eloquent\Relations\Relation;
Relation::morphMap([
'posts' => App\Post::class,
'videos' => App\Video::class,
]);

你可以在 AppServiceProvider 的 boot 方法中注册这个 morphMap ,如果需要的话,也可以创建一个独立的服务提供者来实现这一功能。

多对多的多态关联 表结构

除了传统的多态关联,还可以定义“多对多”的多态关联,例如,一个博客的 Post 和 Video 模型可能共享一个 Tag 模型的多态关联。使用对多对的多态关联允许你在博客文章和视频之间有唯一的标签列表。首先,让我们看看表结构:

posts
id - integer
name - string
videos
id - integer
name - string
tags
id - integer
name - string
taggables
tag_id - integer
taggable_id - integer
taggable_type - string 模型结构

接下来,我们准备在模型中定义该关联关系。 Post 和 Video 模型都有一个 tags 方法调用 Eloquent 基类的 morphToMany 方法:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model{
/**
* 获取指定文章所有标签
*/
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
} 定义相对的关联关系

接下来,在 Tag 模型中,应该为每一个关联模型定义一个方法,例如,我们定义一个 posts 方法和 videos 方法:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model{
/**
* 获取所有分配该标签的文章
*/
public function posts()
{
return $this->morphedByMany('App\Post', 'taggable');
}
/**
* 获取分配该标签的所有视频
*/
public function videos()
{
return $this->morphedByMany('App\Video', 'taggable');
}
} 获取关联关系

定义好数据库和模型后可以通过模型访问关联关系。例如,要访问一篇文章的所有标签,可以使用动态属性 tags :

$post = App\Post::find(1);
foreach ($post->tags as $tag) {
//
} 还可以通过访问调用 morphedByMany 的方法名从

本文开发(php)相关术语:php代码审计工具 php开发工程师 移动开发者大会 移动互联网开发 web开发工程师 软件开发流程 软件开发工程师

分页:12
转载请注明
本文标题:[ Laravel 5.3 文档 ] Eloquent ORM ―― 关联关系
本站链接:http://www.codesec.net/view/480498.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 开发(php) | 评论(0) | 阅读(31)