模型关联
一、一对一
建立关联关系
一对一是最简单的关联关系,一般可用于某张数据库表的扩展表与主表之间的关联关系。比如在大型系统中,我们的用户表通常用于最基本信息的存储,如邮箱、用户名、密码等,然后像用户的爱好、标签、签名、所在地等信息都存到另一张扩展表中,
需要的时候才会去扩展表取数据,从而提高查询性能。针对这样的场景,我们就可以在两张表对应模型之间建立一对一关联
例如,创建一张user_profiles表 表中添加一个user_id字段用于指向所属用户,从而建立与user表的关联
首先在user模型中通过haaone方法定义其与userprofile的一对一关联
public function profile()
{
return $this->hasone(UserProfile::class,'user_id','id');
}
使用,获取用户的profiles表信息
$user = User::findOrFail(1);
$profile = $user->profile;
Eloquent 底层约定
需要指出的是,在关联关系的建立过程中,Eloquent 遵循了[约定大于配置] 的原则。你可能注意到了我们在定义关联关系时,仅仅指定了模型类名,并没有指定通过哪些数据表字段建立关联,这并不是说Laravel神通广大
能知过去未来之事,而是因为Eloquent对此做了默认的约定。hasone方法的完整签名是:
public function hasOne($related,$foreignKey=null,$localKey=null){}
其中,
第一个参数是关联模型的类名,
第二个参数是关联模型类所属表的外键,这里对应的是user_profiles表的user_id 字段,
第三个参数是关联表的外键关联到当前模型所属表的哪个字段,这里对应的是users表的id字段
为什么我们不需要指定 Laravel 就能完成这种关联呢,这是因为如果没有指定 $foreignKey,Eloquent 底层会通过规则固定去拼接
建立相对的关联关系
通常我们都是通过user模型获取userprofiles模型,但是有时候我们可能需要反过来通过userprofiles 反查所属的user模型,eloquent底层也为我们提供了相应的belongsTo 方法来建立相对的一对一的关联关系
我们在userprofile模型类定义其与user模型的关联如下
public function user(){
return $this->belongsTo(User::class,'user_id','id')
}
使用,获取某个profiles信息的user信息
$profile = UserProfile::findOrFail(2);
$user = $profile->user;
同样,和hasone方法一样 belongsTo 方法也是遵循了默认的约定规则,其完整方法签名如下:
public function belongsTo($related,$foreignKey=null,$ownerKey=null,$relation=null){}
其中第一个参数是关联模型的类名。
第二个参数是当前模型类所属表的外键,在本例中是user_profiles表的user_id字段
第三个参数是关联模型类所属表的主键
第四个参数是关系调用方法名,如下
public function user(){
return $this->belongsTo(User::class,'user_id','id','userinfo')
}
使用,获取某个profiles信息的user信息
$profile = UserProfile::findOrFail(2);
$user = $profile->userinfo;
获取用户信息就可以使用userinfo来获取
注意: hasone 第二个参数是当前关联模型类所属的外键 belongsTo是当前模型类所属表的外键
二、一对多
建立关联关系
一对多关联是我们日常开发中经常碰到的一种关联关系。以博客系统为例,一个用户可以发布多篇文章,反过来,一篇文章只属于一个用户,那么用户和文章之间就算一对多的关系,同样,用户可以发布多条评论,一个评论只能
归属于一个用户,用户与评论之间也是一对多的关系。
要定义用户文章之间的一对多关联,可以在user模型类中通过eloquent底层提供的hasMany方法来实现
public function posts(){
return $this->hasMany(Post::class,'user_id','id');
}
与hasOne返回的单个模型实例不一样,hasMany返回的是模型类的集合
和hasOne方法一样,hasMany方法底层也对如何建立关联关系做了约定,而且hasMany方法和hasOne方法的签名一样:
public function hasMany($realted,$foreignKey=null,$localKey=null){}
建立相对的关联关系
与一对一一样,我们可以在文章模型中建立与用户模型之间的相对关联关系,而且这种使用场景很普遍,比如在文章详细页或列表页显示文章作者信息.还是通过Eloquent提供的belongsTo方法来实现
public function user(){
return $this->belongsTo(User::class,'user_id','id');
}
这样我们就可以在文章模型实例商通过动态属性user来访问对应的用户信息
$post=Post::findOrFail(29);
$author = $post->user;
渴求式加载(with)关键字
前面我们演示的关联关系查询都是通过动态属性的方式,这种加载方式叫做[懒惰式加载],因为都是用到的时候才会去查询,这就意味着要多次对数据库的进行查询才能返回需要的结果。如果是单挑记录获取关联关系,就需要
两次查询;如果是多条记录获取关联关系,比如文章列表页面获取作者信息,因为每篇文章的坐着通过动态属性获取都有一次查询,所以对N条记录来说,需要[N+1]次查询才能返回需要的结果,从数据库查询优化的角度来说
显然是不合理的,能不能一次就返回所有的关联查询结果呢?
可以,Eloquent为我们提供了with方法,我们将需要查询的关联关系动态属性(关联方法名)传入该方法,并将其链接到Eloquent模型原有的查询中,就可以一次完成关联查询,加上模型自身查询,总共查询两次。我们将这种
加载方式叫做[渴求式加载],即根据所需预先查询所有数据
以文章表为例,我们可以通过这种方式获取文章及对应的作者信息:
$posts = Post::with('user')->where('views',>,0)->offset(1)->limit(10)->get();
对应底层执行的sql语句是
select * from `posts` where `views` > 0 and `posts`.`deleted_at` is null limit 10 offset 0;
select * from `users` where `users`.`id` in (?, ?, ?, ?, ?, ?) and `email_verified_at` is not null
遍历的时候可以使用$post->user 获取作者信息,而无需每次加载,从而提高数据库查询性能
三、多对多
建立关联关系
多对多关联也很常见,还是以博客系统为例,我们会为每篇文章设置标签,一篇文章往往有多个标签,反过来,一个标签也可能会归属多篇文章,这时,我们说文章和标签之间是多对多的关联关系
多对多关联比一对一和一对多关联复杂一些,需要借助一张中间表,才能建立关联关系。以文章标签为例,文章表已经存在了,还需要创建一张tags表和中间表post_tags 首先创建tags模型类以及其对应数据表tags
然后创建post_tags表
接下来在post模型类中定义其与tags模型类的关联关系,通过Eloquent提供的belongsToMany 方法来实现:
public function tags()
{
return $this->belongsToMany(Tag::class,'post_tags','post_id','tag_id','id','id');
}
belongsToMany方法签名如下
public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null, $parentKey = null, $relatedKey = null, $relation = null)
第一个参数是关联模型的类名,这里是Tag
第二个参数$table是建立多对多关联的中间表名
第三个参数$foreignPivotKey 指的是中间表中当前模型类的外键,默认拼接规则和前面一对一及一对多一样,所以在本例中是posts表的post_id字段
第四个参数$relatedPivotKey 是中间表中当前关联模型类的外键,在本例中是tags表的tag_id 字段
第五个参数$parentKey 表示对应当前模型的哪个字段 (即$foreignPivotKey 映射到当前模型所属表的哪个字段) 默认是主键id 即posts表的id字段
第六个参数$relatedKey 标识对应关联模型的哪个字段(即$relatedPivotKey 映射到关联模型所属表的哪个字段) 默认是关联模型的主键id 即tags表的id字段
最后一个参数$relation 标识关联关系名称,用于设置查询结果中的关联属性,默认是关联方法名
建立相对的关联关系
与之前的关联关系一样,多对多关联也支持建立相对的关联关系,而且由于多对多的双方是平等的,不存在谁归属谁的问题,所以建立相对关联的方法都是一样的,我们可以在tag模型中通过
belongsToMany方法建立其与post模型的关联关系
public function posts(){
return $this->belongsToMany(Post::class,'post_tags','tag_id','post_id','id','id');
}
pivot返回中间表字段
四、远程一对多关联
我们在上一篇教程中学习了一对多关联,远层一对多在一对多关联的基础上加上了一个修饰词「远层」,意味着这个一对多关系不是直接关联,而是「远层」关联,远层怎么关联呢?借助中间表。前面我们讨论的多对多关联
也是借助中间表,但是远层一对多与其区别在于还是一对多的关联。所以理解一对多和多对多关联是理解今天介绍的几种关联关系的基础。
举例说明:
如果我们的博客系统是针对全球市场的话,可能针对不同的国家推出不同的用户系统和功能,然后中国用户过来就只展示中国用户发布的文章,日本用户过来就只展示日本用户发布的文章,这里面涉及到三张表,
存储国家的 countries 表,存储用户的 users 表,以及存储文章的 posts 表。用户与文章是一对多的关联关系,这一点我们上篇教程已经说过,国家与用户之间是一对多的关联(一个用户只能有一个国籍),
那么通过用户这张中间表,国家和文章之间也建立起来一对多的关联,只是这个关联不是直接的关联,而是「远层」的关联。针对这样的情况,我们说国家和文章之间是远层的一对多关联。
country 模型类中通过hasManyThrough 方法定义其与post模型类之间的远程一对多关联
public function posts(){
return $this->hasManyThrough(Post::class,User::class);
}
hasManyThrough完整签名
public function hasManyThrough($related, $through, $firstKey = null, $secondKey = null, $localKey = null, $secondLocalKey = null)
第一个参数是关联的模型类
第二个参数是中间借助的模型类
第三个参数$firstKey 表示中间模型类与当前模型类的关联外键 上例中是user表中的country_id字段
第四个参数$secondKey 指的是关联模型类与中间模型类的关联外键 上例中是user表与post表的关联外键,也就是关联表post表中的user_id字段
第五个参数$localKey默认是当前模型类的主键ID 上例是country表的id字段
第六个参数$secondLocalKey 是中间模型类的主键id 上例是user表的id字段
上一篇: php 面向对象工厂模式记录...
下一篇: php导出excel...