Laravel学习笔记基础系列--(二十九)Laravel 关联模型的相关操作

作者: 温新

分类: 【Laravel】

阅读: 1815

时间: 2021-07-30 14:52:28

作者:温新

时间:2021-07-03

hi,我是温新,一名PHPer

本篇文章记录完成后,模型之间的关联关系就告一段落了。

本篇文章所使用的模型是基于前三篇文章所生成的模型及数据作为演示。如下将进行模型的关联查询、插入、和更新操作。

关联查询

关于关联模型,其实已经使用过了,看如操作就会恍然大悟,原来早已经使用了。

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(170, 85, 0)">// 使用关联模型查询</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 85, 170)">$postTag</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26)">=</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">Post</span>::<span style="box-sizing: border-box;color: rgb(0, 0, 0)">with</span>(<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'mangTags'</span>)<span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">find</span>(<span style="box-sizing: border-box;color: rgb(17, 102, 68)">1</span>);</span>

关联查询分为两种:懒惰式加载(动态属性)渴求式加载 (with方法)

从性能上来看:渴求式加载优于懒惰式加载,它会一次性取出所有的关联数据;懒惰式加载则是调用时才会去查询取出关联数据,因此还存在一个1+N的问题。

懒惰式加载

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(170, 85, 0)">// 一对多查询</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(170, 85, 0)">// 先查询用户信息</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 85, 170)">$user</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26)">=</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">User</span>::<span style="box-sizing: border-box;color: rgb(0, 0, 0)">find</span>(<span style="box-sizing: border-box;color: rgb(17, 102, 68)">1</span>);</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(170, 85, 0)">// 调用时查询,若有N篇文章,查询N次</span></span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 85, 170)">$userPosts</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26)">=</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$user</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">posts</span>;</span>

除了通过动态属性来获取所有关联数据外,还可以Post模型中定义的posts方法。当使用posts方法时,返回的是一个关联关系的实例,这个实例中包含了Builder查询构造器,因此可以通过链式操作构建复杂的查询。如需要查询该用户发布的id大于10的所有文件:

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 85, 170)">$user</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26)">=</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">User</span>::<span style="box-sizing: border-box;color: rgb(0, 0, 0)">find</span>(<span style="box-sizing: border-box;color: rgb(17, 102, 68)">1</span>);</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 85, 170)">$user</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">posts</span>()<span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">where</span>(<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'id'</span>,<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'>'</span>,<span style="box-sizing: border-box;color: rgb(17, 102, 68)">10</span>)<span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">get</span>()</span>

使用动态属性查询与使用方法查询有什么区别?

当前posts方法当作动态属性使用时,查询的是所有数据;当posts作为方法使用时,可以构建更复杂的查询。

渴求式加载

渴求式加载使用with方法,解决了1+N问题。

$posts = Post::with('userInfo')->where('user_id',1)->get();

懒惰渴求式加载

对于有些情况而言,渴求式加载一次取出所有数据有点浪费,此时可以通过条件来判断进行的加载方法,其通过load方法实现。

<span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 85, 170)">$users</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26)">=</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0)">User</span>::<span style="box-sizing: border-box;color: rgb(0, 0, 0)">all</span>();</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(0, 85, 170)">$condition</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26)">=</span> <span style="box-sizing: border-box;color: rgb(34, 17, 153)">true</span>;</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px"><span style="box-sizing: border-box;color: rgb(119, 0, 136)">if</span> (<span style="box-sizing: border-box;color: rgb(0, 85, 170)">$condition</span>) {</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">    <span style="box-sizing: border-box;color: rgb(0, 85, 170)">$users</span><span style="box-sizing: border-box;color: rgb(152, 26, 26)">-></span><span style="box-sizing: border-box;color: rgb(0, 0, 0)">load</span>(<span style="box-sizing: border-box;color: rgb(170, 17, 17)">'posts'</span>);</span><br></br><span style="box-sizing: border-box;padding-right: 0.1px">}</span>

查询已存在的关联

准备工作:在用户表中手动添加一条新用户且没有发布过文章。

has()方法 查询结果过滤

在所有用户中,需要查询所有发布过文章的用户:

$allUserPosts = User::has('posts')->get();

查询发布文章数据大于25的用户

$allUserPosts = User::has('posts','>',25)->get();

has()嵌套查询

关联查询文章被评论过的用户

$allUserPosts = User::has('posts.comments')->get();

orHas()查询

查询被评论或有标签的文章

$postTag = Post::has('comments')->orHas('tags')->get();

复杂查询 whereHas/orWhereHas

这两个方法基于闭包函数定义的查询条件进行查询。

如:查询用户发布的文章中标题含包含字母D的所有文章。

$userPosts = User::whereHas('posts', function($query){
    $query->where('title','like','%D%');
})->get();

无结果过滤查询doesntHave/orDoesntHave

如查询所有,没有发布过文章的用户

$userPosts = User::doesntHave('posts')->get();

查询没有被评论的文章或没有标签的文章

$posts = Post::doesntHave('comments')->orDoesntHave('tags')->get();

统计关联查询

withCount()不加载关联模型的情况下统计关联结果数据。

如:查询某篇文章评论的总数。

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

查询结果在对应的模型实例中会有一个comments_count属性,这就是该文章评论总数。

关联模型的插入与更新

一对多关联记录的插入

save()插入一条

$post = Post::find(10);
$comment = new Comment();
$comment->content = '这是通过关联插入的评论。好文章';
$comment->user_id = 5;
// 通过实例关联插入
$post->comments()->save($comment);

saveMany()批量插入

Comment模型中设置白名单protected $guarded = [];

public function demo()
{
    $post = Post::find(10);
    $post->comments()->saveMany([
        new Comment(['content'=>'批量评论1','user_id'=>10]),
        new Comment(['content'=>'批量评论1','user_id'=>10]),
        new Comment(['content'=>'批量评论1','user_id'=>10]),
    ]);
}

commentable_idcommentable_type这两个字段不用管,Laravel底层自动维护。

create/createMany

这两个方法与save/saveMany类似,不同的是,这两个说法接收的参数是数组

$post = Post::find(10);
// 插入一条数据
$post->comments()->create([
    'content'=>'create方法插入',
    'user_id'=>10,
]);
// 插入多条数据
$post->comments()->createMany([
    ['content'=>'createMany方法插入1','user_id'=>10],
    ['content'=>'createMany方法插入2','user_id'=>10],
]);

更新一对所属模型外键

$user = User::find(10);
$post = Post::find(99);
$post->userInfo()->associate($user);
$post->save();

dissociate方法,解除当前模型与所属模型之间的关联

$post->userInfo()->dissociate();
$post->save();

user_id字段不为null时,会报错

多对多的插入与更新

插入关联数据

多对多的插入也可以像一对多一样。这里以文章与标签为例。

// app/Models/Tag.php
protected $guarded = [];

// 控制器
$post = Post::find(5);
$post->tags()->create([
    'name'  =>  'JS'
]);

插入的方法与一对多使用插入的方法是一样的,这里就不演示了。

更新关联数据

attach方法添加关联关系。会在中间表添加对应的字段

// 更新中间表
$post = Post::find(1);
$tag = Tag::find(5);
$post->tags()->attach($tag->id)

处理添加单个,还能添加多个,如attach([1,2,3]);

detach方法删除中间关联关系

$post = Post::find(1);
$tag = Tag::find(5);
$post->tags()->detach($tag->id);

一次删除多个关联中间值detach([1,2,3])

sync不存在则添加,存在则更新

$post = Post::find(1);
$tag = Tag::find(5);
$post->tags()->sync($tag->id);

更新父模型时间

当一个模型与另一个模型有从属关系时(如Comment模型归属于Post模型),当子模型更新时,触发父模型的时间更新。Eloquent提供了touches属性,该属性需要在子模型中配置。

第一步:子模型定义触发更新属性

文件:app\Models\Comment.php

protected $touches = ['commentable'];

第二步:更新评论内容

我是在控制器中进行演示

$comment = Comment::find(10);
$comment->content = '自如初';
$comment->save();

第三步:查询数据表

查询数据表会发现,id为10的文章的时间被更新了。

到这里关于模型关联关系的记录已经全部完毕。记录模型花费的时间比较多,这个过程越是能够感受到模型的强大之处。好用有一个重要的前提,那就是要提前缕清模型间的关系,只有缕清了模型间的关系,操作起起来才得心顺手,否则可能会跟无头苍蝇一样,甚至觉得,卧槽,模型这么难用,算了,我还是用DB去。

关于模型关联,有写方法是没有记录到的,因此,遇到没有记录到的,请查阅文档。

我是温新,模型关系到此结束。是时候该休息下眼睛了。

我是温新

每天进步一点点,就一点点

请登录后再评论