如何更快的找到自己所需的模型关联类型?

@这是小豪的第一篇文章
这遍文章主要的目的就是让大家能够更快捷的找到自己所需的模型关联类型。

模型关联有哪些关联类型 ?

一对一关联

已知我们有两张表 usersid_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');
    }
}

从上面的代码中我们可以看到有且只有一个分别被转化为了 hasOnebelongsTo , 单独从这两个单词的字面意思上就可以很清楚的理解为什么是这样,拥有属于。用户有一个身份证、身份证属于一个人。在整个模型关系中,凡是为身份证这种有且只有一个主体的时候,都是使用belongsTo

如何去使用呢 ?很简单!

$idCard = User::find(1)->idCard;
$user = IdCard::find(1)->user;

其实反向关联并不是强制要求的,看自己需求,像现在通过手机号去拿用户也不是很常用。

:关联外键就不在这里详细说明了

一对多关联

已知我们有两张表 userssocks
继续从生活常识中获取信息:

  • 用户 -> 袜子有很多
  • 袜子 -> 有且只有一个用户 ( 依赖用户而存在 )

从上面的信息中大家有没有发现,袜子和上面一对一模型关联中的身份证的类型是一致的,是的,你的袜子总是你一个人的吧,难不成和别人共袜? 哈哈,所以可以轻易得出袜子的反向关联为:

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_typecommentable_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_typea_id 这个时候第二个参数就为 a。具体的使用和上面的是一致的,这里就不多说了。

总的来说呢,多态关联就是主体不确定,客体需要存储主体类型加以判断。

多对多多态关联

理解上面的多态关联之后,就可以很快的理解现在要讲的 多对多多态关联 了, 多对多多态关联 揉和了多对多关联与多态关联。这样我们就可以得到两个关键信息:

  • 类型不确定
  • 中间表

下面我们就用一个实例来讲解一下:
要求:文章、视频都有标签
你可能会想,这不是和上面的多态关联是一样的吗,其实不一样,一个评论只对应一个文章或视频,但是一个标签可能对应多篇文章、多个视频,这样就能理解中间表的作用了吧,现在我们来设计表:

视频表:videos
文章表:posts
标签表:tags
中间表:taggables
中间表中有这三个字段:tag_idtaggable_idtaggable_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. 行为日志

    • 主体不确定
    • 一个行为日志对应一个主体
      ----- 通过这两点可以得出模型关联为:多态关联
  2. 用户文章

    • 主体确定
    • 一个文章一个用户
      ----- 通过这两点可以得出模型关联为:1对多关联
  3. 标签

    • 主体不确定
    • 一个标签多个主体
      ----- 通过这两点可以得出模型关联为:多对多多态关联

    。。。。。。

以上只是冰山一角,还有更关联模型组合,待我慢慢总结完善,哈哈。。。。

这是小豪的迈出的第一步,文章中不够完善严谨的地方还请大家指正,共同进步,我后面也会慢慢去完善改进这篇文章。

本作品采用《CC 协议》,转载必须注明作者和本文链接
finecho # Lhao
本帖由系统于 5年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 20

可以哦

5年前 评论

总结的不错!

5年前 评论

讲的很清晰

5年前 评论
finecho

@overtrue 都是超哥教的好,哈哈

5年前 评论
finecho

@showcj 呀,谢谢大佬,马上去更正

5年前 评论

很有用,谢谢大佬

5年前 评论
kevin_yang

对于新手还是很有帮助

5年前 评论

深入浅出,很好!

5年前 评论

似乎多打了个括号.
file

5年前 评论
finecho

@sakyavarro :relaxed: 哈哈

5年前 评论
allen9009

讲得蛮好的

5年前 评论

文章写的通俗易懂 .可以考虑在模型中声明模型关系的时候使用:class语法,感觉大部分人还是习惯这么写

    /**
     * 获得拥有此袜子的用户。
     */
    public function user()
    {
        return $this->belongsTo(User::class);
    }
5年前 评论
finecho

@Flourishing 其实平时开发也是用的 User::class 这种形式,只不过要是这样的话得加一个 use App\User; 了,偷懒直接这样写了,哈哈,只要大家都能看明白就好了呢。

5年前 评论

请教下,有没有试过多态的按条件查询?我试着对查询的多态对象进行条件筛选,提示不支持。有没有其他办法呢?has() and whereHas() do not support MorphTo relationships.

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!