Laravel多态关系导致n+1问题

toiithl6  于 2023-02-25  发布在  其他
关注(0)|答案(1)|浏览(193)

我使用多态关系来创建多个类型。我有一个节点和边模型来描述一个网络,每个节点都可以是多个类型中的一个。
节点模型具有...

public function nodeable()
{
    return $this->morphTo();
}

而Company模型(节点的“类型”之一)有......

public function node()
{
    return $this->morphOne(Node::class, 'nodeable');
}

当检索节点列表及其关联的“类型”模型时,我调用以下代码...

$nodes = Node::with('nodeable')->get();
return NodeResource::collection($nodes);

节点资源类NodeResource具有...

return [
    'id' => $this->id,
    'entity_type' => $this->nodeable_type,
    'entity' => $this->when($this->nodeable, function () {
        if ($this->nodeable instanceof \App\Models\Property) {
            return new PropertyResource($this->whenLoaded('nodeable'));
        }
        if ($this->nodeable instanceof \App\Models\Individual) {
            return new IndividualResource($this->whenLoaded('nodeable'));
        }
        if ($this->nodeable instanceof \App\Models\Company) {
            return new CompanyResource($this->whenLoaded('nodeable'));
        }
        if ($this->nodeable instanceof \App\Models\Trust) {
            return new TrustResource($this->whenLoaded('nodeable'));
        }
    }),
];

这看起来确实有效,但是当我查看在这个检索过程中运行的SQL时,我发现我有一个n+1问题。

select
  *
from
  `nodes`
where
  `nodes`.`nodeable_type` = 'App\Models\Property'
  and `nodes`.`nodeable_id` = 1
  and `nodes`.`nodeable_id` is not null
limit
  1

我的问题是,我不明白为什么当我在查询中有with('nodeable')时,我会得到n+1个看似毫无意义的查询?
在运行了上述的n+1个查询之后,会运行一些对我来说更有意义的查询....

select * from `individuals` where `individuals`.`id` in (1, 2, 3, 4)

select * from `trusts` where `trusts`.`id` in (1, 2, 3, 4, 5, 6, 7, 8)

select * from `companies` where `companies`.`id` in (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

select * from `properties` where `properties`.`id` in (1, 2, 3, 4, 5, 6)

select * from `nodes`

其他问题:上面的5个查询是不是一个快速加载的多态关系的样子(如果有4个可节点类型)?通常,我会期待一个连接,但我的感觉是多态快速加载必须这样工作
编辑:如果我不使用这个资源,n+1问题就消失了。我最终确实需要使用这个资源。在以前的项目中,我使用过同样的机制,它没有导致n+1
EDIT 2:在将$nodes数据传递给NodeResource之前查看它,发现存在nodeable数据,这意味着with('nodeable')起作用。
EDIT 3:我已经尝试过简化NodeResource以始终返回PropertyResource(因为现在所有类型都是相同的)...

'entity' => new PropertyResource($this->whenLoaded('nodeable')),

因为有人认为对NodeResource中$this-〉nodeable的调用可能是问题所在。这没有解决n+1
EDIT 4:我找到了罪魁祸首。在可节点资源类中,我调用了'node_id' =〉$this-〉node-〉id。我应该注意到错误的n+1查询不是指向可节点模型(即获取可节点),而是指向节点模型本身。我向参与此票证的任何人道歉。但我从尝试帮助中学到了一些东西,所以谢谢。

pb3s4cty

pb3s4cty1#

当你调用$this->nodeable的时候,如果资源没有被加载,那么它就会被取回,所以你的whenLoaded调用并没有做太多的事情,因为你现在尝试先取回它,然后检查它是否被加载。
因此,您可以将代码改进为如下形式:

return [
    'id' => $this->id,
    'entity_type' => $this->nodeable_type,
    'entity' => $this->when($this->resource->relationLoaded('nodeable'), function () {
        if ($this->nodeable instanceof \App\Models\Property) {
            return new PropertyResource($this->nodeable);
        }
        if ($this->nodeable instanceof \App\Models\Individual) {
            return new IndividualResource($this->nodeable);
        }
        if ($this->nodeable instanceof \App\Models\Company) {
            return new CompanyResource($this->nodeable);
        }
        if ($this->nodeable instanceof \App\Models\Trust) {
            return new TrustResource($this->nodeable);
        }
    }),
];

如果这不能解决你的n+1问题,那么其他的事情正在发生。

相关问题