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

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

小豪 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 了呢?哈哈。

结束语

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

刘小豪

本帖由系统于 1个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 4

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

1个月前
JasonG

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

1个月前

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

1个月前
郝合心
$comments = Post::find(1)->comments;

改为

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

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

2周前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!