模型关联给我们带来了哪些便利

@这是小豪的第二篇文章
上周已经给大家简单的介绍过 《 如何更快的找到自己所需的模型关联类型? 》,现在我们来了解一下,如何更好的发挥模型关联所带给我们的便利。

小豪 Tip

withloadattachdetachsync...... 当这些关键词第一次出现在我眼前的时候,我是一脸蒙的,不知道到底该如何去使用,更不知道他们到底与模型关联存在着怎样的联系,就照着项目中原有的方式照搬,结果就是造成很多奇怪的问题,自己也活在混沌之中。所以在做之前得了解为什么要这样做!

今天的文章就是围绕着上面的几个关键词来展开的,从这几个关键词的中文解释中可以清晰的分为两类:查询、增删改,下面我们就来一一解读。

with

我们用文章和评论来举例:

class Comment extends Model
{
    /**
     * 获得拥有此评论的文章。
     */
    public function post()
    {
        return $this->belongsTo('App\Post');
    }
}
class Post extends Model
{
    /**
     * 获得与文章有关联的评论。
     */
    public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

从上篇文章中我们知道了如何通过文章获取评论,如下:

 $comments = Post::find(1)->comments;

如果获取多篇文章的评论呢:

 $posts = Post::all();

 foreach ($posts as $post) {
    foreach ($post->comments as $comment) {
        echo $comment->content;
    }
 }

这个循环会执行一条语句去数据库查询所有的评论,然后为每一篇文章执行一条语句去获取评论。这个时候 with 就体现他的作用了:

 $posts = Post::with('comments')->get();

 foreach ($posts as $post) {
    foreach ($post->comments as $comment) {
        echo $comment->content;
    }
 }

在这次执行中,仅产生了两条语句:

 select * from posts

 select * from comments where comments.post_id in (1, 2, 3, 4, 5, ...)

这种方式叫渴求式加载。有没有感觉性能优化就是如此的简单,哈哈。大家要记住 with 是得与模型关联联系起来用的,如果 App\Post 中没有定义 comments , with('comments') 是无法使用的噢。也可以在模型内部申明一个 with 数组,这样也也可以直接达到渴求式加载的效果,就不用每次在查询语句中使用 with 了,一般直接在模型中 with 的数据是必须加载的数据,不必须的就别写在里面了,如下:

class Post extends Model
{
  protected $with = [
      'comments'
  ];
}

with 是可以接收数组的,像这样Post::with(['comments','nodes'])->get();,需要注意的是 with 不可接收空字符串,否则会报错,如果需要传空的话,传递一个空数组即可。 with 还有很多用法的,大家可以在文档上面搜罗一番。

load

如果理解了 with ,那么 load 就很好理解的,他们之间的区别就在于:load 是对已经查出的模型使用,立即加载某个关联关系,可以动态的加载关联数据,而 withLaravel 帮你做好关联关系的查询,两者都是渴求式加载,且查询的 sql 是一模一样的 。

代码如下:

$posts = App\Post::all();

if ($someCondition) {
    $posts->load('comments', 'nodes');
}

当关联关系没有被加载时,你可以使用使用 loadMissing 方法,这是文档中给出的 loadMissing 的使用场景,其实刚开始看这句话的时候,我是不能理解的,既然关联关系没有被加载,我 load 一下不就行了吗?仔细了解一下之后发现,loadMissing 的主要作用是:加载从未加载过的关联,意思就是过滤了已经加载的关联。

到这里查询基本上讲完了,细节还有一些具体的使用方法,大家可以细读文档,会发现不一样的惊喜。

下面讲的是:附加 / 分离 / 同步 ,他们有一个共同点就是所属的模型关联为 多对多关联 ,简单点来说就是有中间表的那种关联。

attach

我们用用户和角色来举例:

class User extends Model
{
    /**
     * 用户的角色
     */
    public function roles()
    {
        return $this->belongsToMany('App\Role');
    }
}
class Role extends Model
{
    /**
     * 拥有此角色的用户
     */
    public function users()
    {
        return $this->belongsToMany('App\User');
    }
}

不太清楚这种关联模式的,先去看上一篇文章哦,我就不再解释多对多关联了。

顾名思义,attach 是就是“附加”的意思。单纯对这个案例来讲,大家或许就能想到,这里的附加就是用户与角色的关系附加到中间表去,Laravel 是如何处理的呢,现在来看一下:

$user = App\User::find(1);

$user->roles()->attach($roleId);

// 传递一组要插入到中间表中的附加数据
$user->roles()->attach($roleId, ['expires' => $expires]);

可以看到代码思路为:给指定用户的角色中附加一条新的角色。就是这么的清晰,哈哈。注意哦,这里是附加,主要是适用于添加关联的。

detach

有时也需要移除用户的角色。可以使用 detach 移除多对多关联记录。detach 方法将会移除中间表对应的记录。
见代码:

$user = App\User::find(1);

//  移除用户的一个角色...
$user->roles()->detach($roleId);

//  移除用户的所有角色...
$user->roles()->detach();

关系分离很简单,过一下就好了。

sync

现在我们来看一下关系同步,你们可能会问,syncattach 有什么区别 ?他们两个最大的区别在于一个是更新、添加另一个是只添加,不知道这样说大家能不能理解,现在我们来通过代码来理解:

$user = App\User::find(1);

// 步骤一:同步角色 2、3、4
$user->roles()->sync([2, 3, 4]);

// 步骤二:附加角色 1、2、3
$user->roles()->attach([1, 2, 3]);

从上面代码中如果我们进行这样的操作:

  1. 先执行 步骤一 再执行 步骤二 ,得出的结果是:
    用户绑定的角色为:角色2角色3角色4角色1角色2角色3

  2. 先执行 步骤二 再执行 步骤一 ,得出的结果是:
    用户绑定的角色为:角色2角色3角色4

  3. 当前数据表中已经存在用户绑定了 角色1角色2角色3,当执行 步骤一 后,得出的结果是:
    用户绑定的角色为:角色2角色3角色4(其实这种情况和第二种是一样的 )

大家有没有发现,只要执行了 sync 之后,都是以 sync 的角色为主,原有角色绑定数据都移除了。所以现在大家是否了解 sync 了呢?哈哈。

结束语

已经讲完哒,文笔粗糙不知道能不能帮到大家,哈哈。有错误的地方还请大家指正一下。

本作品采用《CC 协议》,转载必须注明作者和本文链接
finecho # Lhao
本帖由系统于 5年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 4
JasonG

个人觉得需要强调一下 sync 会移除原有数据中不在本次 sync 数组里的id

5年前 评论
$comments = Post::find(1)->comments;

改为

$comments = optional(Post::find(1))->comments;

会不会好一点,这样能保证不会报错

5年前 评论

支持下作者,排版清晰,看得出来认真。

5年前 评论
JasonG

个人觉得需要强调一下 sync 会移除原有数据中不在本次 sync 数组里的id

5年前 评论
finecho

@JasonG 嗯呢是的,忘记强调这个点了,谢谢大佬提醒

5年前 评论
$comments = Post::find(1)->comments;

改为

$comments = optional(Post::find(1))->comments;

会不会好一点,这样能保证不会报错

5年前 评论

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