Laravel Collection::toArray() 触发额外的数据库查询

Laravel Collection::toArray() triggers extra database queries

让我们使用典型的 posts/tags 示例。假设我想提取一些帖子的详细信息。我是一个聪明的开发者,想限制数据库查询,所以我急于将关系加载到标签。

<?php
class Post extends Model {
    public function tags() { return $this->belongsToMany(Tag::class); }
}

class Tag extends Model {
    public function posts() { return $this->belongsToMany(Post::class); }
}

$posts = Post::query()->with("tags")->where("title", "like", "foo%")->get();

到目前为止,还不错。我的数据库的查询日志显示两个查询:

select * from `posts` where `title` like 'foo%' and `posts`.`deleted_at` is null;
select `tags`.*, `post_tag`.`post_id` as `pivot_post_id`, `post_tag`.`tag_id` as `pivot_tag_id`
    from `tags` inner join `post_tag` on `tags`.`id` = `post_tag`.`tag_id`
    where `post_tag`.`post_id` in (19, 880, 1462, 2712, 2713, 2717);

我现在可以毫无问题地处理帖子和标签。例如,我可以 运行 此代码而无需任何进一步的数据库查询:

foreach ($posts as $post) {
    echo $post->title . "(" . implode(",", $post->tags->pluck("name")->all()) . ")";
}

这告诉我关系中的数据已按预期保存到集合 $posts 中。


所以,我的问题是:为什么调用 $posts->toArray()$posts->toJson() 会导致 另一个 查询我的枢轴 table for 集合中的每个项目,提取集合中已存储的相同数据?

select `tags`.*, `post_tag`.`post_id` as `pivot_post_id`, `post_tag`.`tag_id` as `pivot_tag_id`
from `tags` inner join `post_tag` on `tags`.`id` = `post_tag`.`tag_id`
where `post_tag`.`post_id` = 19 limit 1;
select `tags`.*, `post_tag`.`post_id` as `pivot_post_id`, `post_tag`.`tag_id` as `pivot_tag_id`
from `tags` inner join `post_tag` on `tags`.`id` = `post_tag`.`tag_id`
where `post_tag`.`post_id` = 880 limit 1;
...
select `tags`.*, `post_tag`.`post_id` as `pivot_post_id`, `post_tag`.`tag_id` as `pivot_tag_id`
from `tags` inner join `post_tag` on `tags`.`id` = `post_tag`.`tag_id`
where `post_tag`.`post_id` = 2717 limit 1;

有什么方法可以防止这些额外的查询发生吗?

您可能有一个访问器正在通过 $appends 属性 'appended' 访问模型数据。它可能正在使用关系方法并导致查询而不是使用关系的动态 属性。