我使用多态关系来创建多个类型。我有一个节点和边模型来描述一个网络,每个节点都可以是多个类型中的一个。
节点模型具有...
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查询不是指向可节点模型(即获取可节点),而是指向节点模型本身。我向参与此票证的任何人道歉。但我从尝试帮助中学到了一些东西,所以谢谢。
1条答案
按热度按时间pb3s4cty1#
当你调用
$this->nodeable
的时候,如果资源没有被加载,那么它就会被取回,所以你的whenLoaded
调用并没有做太多的事情,因为你现在尝试先取回它,然后检查它是否被加载。因此,您可以将代码改进为如下形式:
如果这不能解决你的n+1问题,那么其他的事情正在发生。