如何更快的找到自己所需的模型关联类型?
@这是小豪的第一篇文章
这遍文章主要的目的就是让大家能够更快捷的找到自己所需的模型关联类型。
模型关联有哪些关联类型 ?
一对一关联
已知我们有两张表 users
和 id_cards
从生活常识中我们可以得到两个信息:
- 用户 -> 有且只有一个身份证(普通人、普通人。。。。)
- 身份证 -> 有且只有一个用户 ( 依赖用户而存在 )
好现在看看,Laravel 是如何将上面这种文字转换为代码的:
class User extends Model
{
/**
* 获取与用户关联的身份证。
*/
public function idCard()
{
return $this->hasOne('App\IdCard');
}
}
class IdCard extends Model
{
/**
* 获得拥有此身份证的用户。
*/
public function user()
{
return $this->belongsTo('App\User');
}
}
从上面的代码中我们可以看到有且只有一个
分别被转化为了 hasOne
与 belongsTo
, 单独从这两个单词的字面意思上就可以很清楚的理解为什么是这样,拥有
、属于
。用户有一个身份证、身份证属于一个人。在整个模型关系中,凡是为身份证这种有且只有一个主体
的时候,都是使用belongsTo
。
如何去使用呢 ?很简单!
$idCard = User::find(1)->idCard;
$user = IdCard::find(1)->user;
其实反向关联并不是强制要求的,看自己需求,像现在通过手机号去拿用户也不是很常用。
注 :关联外键就不在这里详细说明了
一对多关联
已知我们有两张表 users
和 socks
继续从生活常识中获取信息:
- 用户 -> 袜子有很多
- 袜子 -> 有且只有一个用户 ( 依赖用户而存在 )
从上面的信息中大家有没有发现,袜子和上面一对一模型关联
中的身份证的类型是一致的,是的,你的袜子总是你一个人的吧,难不成和别人共袜? 哈哈,所以可以轻易得出袜子的反向关联为:
class Sock extends Model
{
/**
* 获得拥有此袜子的用户。
*/
public function user()
{
return $this->belongsTo('App\User');
}
}
至于用户嘛,有很多袜子:
class User extends Model
{
/**
* 获取与用户关联的袜子。
*/
public function socks()
{
return $this->hasMany('App\Sock');
}
}
一目了然哈,有很多袜子,大家在写多
关联的时候,记得加s
.......习惯要养好。
模型关系定义好了,接下来是如何使用:
这里是好多双袜子
$socks = User::find(1)->socks;
$user = Sock::find(1)->user;
了解完一对一模型关联
和一对多模型关联
后,不知道大家是不是有种后悔早点进入 Laravel 世界的感觉,优雅吧,哈哈。
多对多关联
现在我们接到了一个需求:实现用户角色管理
从需求中我们知道了如下信息:
- 一个用户有可以有多个角色
- 一个角色有多个用户
这两条信息告诉我们,用户和角色的关联类型是一一致的
那么我们现在来确定关联表的设计:
用户表:users
角色表:roles
用户角色中间表:role_user
我们可能首先想到的是 hasMany
,但是毕竟他们两者没有绝对的主体客体之分,两个 hasMany
岂不是两边一直在争主体呀,不如两者都退后一步 belongsToMany
,退一步海阔天空嘛,哈哈。现在我们来看看具体的实现:
class User extends Model
{
/**
* 用户的角色
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
class Role extends Model
{
/**
* 拥有此角色的用户
*/
public function users()
{
return $this->belongsToMany('App\User');
}
}
有没有很和谐的感觉。
关系建立好了,现在我们来看看如何去查询使用。
获取用户角色:
$roles = User::find(1)->roles;
获取角色用户:
$users = Role::find(1)->users;
如果需要获取用户绑定角色的时间呢,这个时候就会有点蒙了,改怎么查呢,Laravel 已经跟我们处理好了,通过pivot
来获取中间表的数据:
$user = App\User::find(1);
foreach ($user->roles as $role) {
echo $role->pivot->created_at;
}
一般来说中间表都是只存两个表的外键,如果有需要更多的字段呢可以使用 withPivot
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');
至此,多对多的模型关联
就结束了,当然里面还有很多细节的东西噢,后面的文章会更加详细的讲解,哈哈哈。
多态关联
顾名思义,并不局限于单独的一种类型状态,下面我们就来见识见识什么叫多态关联。
现在的需求是:文章、视频都可评论。
当得到这个需求的时候,我们可能会思考评论表中是不是该有个字段用来存储是对哪种类型的评论。!对,当想到这里的时候,多态就来了,类型可以跟着评论的主体而变化。
从需求中我们知道了如下信息:
- 一篇文章可以有多个评论
- 一个视频可以有多个评论
- 一个评论可以有一个文章主体或一个视频主体
好,到这里我们开始设计表了:
视频表:videos
文章表:posts
评论表:comments
评论表中有这两个字段:commentable_type
、commentable_id
分别存储评论主体信息
第一时间我们想到的是 hasMany
,但是为了区分多态,Laravel 提供给我们的模型关联方法为 morphMany
,其实有点不太好理解了哈,变形为很多是个什么意思,我们来看一下 comments
的反向关联就知道了。
class Comment extends Model
{
/**
* 获得拥有此评论的模型。
*/
public function commentable()
{
return $this->morphTo();
}
}
morphTo
,“变形为”。因为不确定评论的主体类型是什么,所以我们给了一个模糊的 commentable
,通过morphTo
变形为所需的评论模型,至此 morphMany
的由来也说得通了,哈哈。现在来看一下文章和视频模型中对评论模型的关联处理:
class Post extends Model
{
/**
* 获得此文章的所有评论。
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
class Video extends Model
{
/**
* 获得此视频的所有评论。
*/
public function comments()
{
return $this->morphMany('App\Comment', 'commentable');
}
}
可以看到 morphMany
里面都有接收两个参数,第一个是评论模型,第二个是用来存储主体类型和Id的 name
,也就是说要是 comments
表中用来存储主体类型和Id的名称为 a_type
、a_id
这个时候第二个参数就为 a
。具体的使用和上面的是一致的,这里就不多说了。
总的来说呢,多态关联就是主体不确定,客体需要存储主体类型加以判断。
多对多多态关联
理解上面的多态关联之后,就可以很快的理解现在要讲的 多对多多态关联
了, 多对多多态关联
揉和了多对多关联与多态关联。这样我们就可以得到两个关键信息:
- 类型不确定
- 中间表
下面我们就用一个实例来讲解一下:
要求:文章、视频都有标签
你可能会想,这不是和上面的多态关联是一样的吗,其实不一样,一个评论只对应一个文章或视频,但是一个标签可能对应多篇文章、多个视频,这样就能理解中间表的作用了吧,现在我们来设计表:
视频表:videos
文章表:posts
标签表:tags
中间表:taggables
中间表中有这三个字段:tag_id
、taggable_id
、taggable_type
具体的实现:
class Post extends Model
{
/**
* 获取文章标签
*/
public function tags()
{
return $this->morphToMany('App\Tag', 'taggable');
}
}
class Tag extends Model
{
/**
* 获取拥有这个标签的文章
*/
public function posts()
{
return $this->morphedByMany('App\Post', 'taggable');
}
/**
*获取拥有这个标签的视频
*/
public function videos()
{
return $this->morphedByMany('App\Video', 'taggable');
}
}
具体的使用方法,和上面是一样的。
如何选择合适的模型关联 ?
如何去选择合适的模型关联呢?下面给一些自己的想法给大家参考一下:
-
行为日志
- 主体不确定
- 一个行为日志对应一个主体
----- 通过这两点可以得出模型关联为:多态关联
-
用户文章
- 主体确定
- 一个文章一个用户
----- 通过这两点可以得出模型关联为:1对多关联
-
标签
- 主体不确定
- 一个标签多个主体
----- 通过这两点可以得出模型关联为:多对多多态关联
。。。。。。
以上只是冰山一角,还有更关联模型组合,待我慢慢总结完善,哈哈。。。。
这是小豪的迈出的第一步,文章中不够完善严谨的地方还请大家指正,共同进步,我后面也会慢慢去完善改进这篇文章。
本作品采用《CC 协议》,转载必须注明作者和本文链接
可以哦
总结的不错!
讲的很清晰
@overtrue 都是超哥教的好,哈哈
写错字了
@showcj 呀,谢谢大佬,马上去更正
很有用,谢谢大佬
对于新手还是很有帮助
学习了
深入浅出,很好!
似乎多打了个括号.

@sakyavarro :relaxed: 哈哈
讲得蛮好的
文章写的通俗易懂 .可以考虑在模型中声明模型关系的时候使用
:class
语法,感觉大部分人还是习惯这么写@Flourishing 其实平时开发也是用的
User::class
这种形式,只不过要是这样的话得加一个use App\User;
了,偷懒直接这样写了,哈哈,只要大家都能看明白就好了呢。茅塞顿开 :+1: :smile:
:+1:受教了
请教下,有没有试过多态的按条件查询?我试着对查询的多态对象进行条件筛选,提示不支持。有没有其他办法呢?
has() and whereHas() do not support MorphTo relationships.
通俗易懂,很赞
文章很棒,马克一下