语境
想象一组提案,其中每个用户都可以对提案投赞成票或反对票。
所以我有2个模型:
Proposal
, Vote
, 与 proposal
有关, user
, 和 opinion
(上或下)。 现在我想显示我的所有提案,包括每个提案的额外信息:
问题
这真的很容易实现,但这意味着每个提案显示 4 个请求(称为 n+1 查询问题)。
我来自 Rails 世界,在那里可以使用预先加载或一些棘手的查询轻松解决这个问题。但我无法用 Doctrine 解决它,需要一些帮助!
我试过的
使用魔法领域
第一种解决方案是进行自定义查询:选择所有提案,包括赞成票、反对票的数量、当前用户是否赞成或反对。
查询现在返回的列比模型描述的多 4 列:
count_upvotes
, count_downvotes
, has_user_upvoted
和 has_user_downvoted
.为了与 Doctrine 一起使用,我必须将这些字段添加到我的
Proposal
中。实体。对我来说,这不是一个好的解决方案,因为它意味着始终使用此查询来避免 SCSS 模型,其中这些字段可以为空。
使用摘要
第二种解决方案是让 View 使用另一个对象。这个对象是用一个参数创建的:当前用户。该对象由优化查询创建,包含以下方法:
getAllProposals()
getUpvotes($a_proposal)
getDownvotes($a_proposal)
hasUserUpvoted($a_proposal)
hasUserDownvoted($a_proposal)
对我来说,仅仅为了 View 优化而被迫创建一个对象真的太过分了。
使用扩展实体
这第三个解决方案使用唯一的查询来获取提案、他们的赞成票、反对票,并检查用户是否赞成或反对。
它创建了一个新的“扩展”实体,
public function __construct(
Proposal $badgeProposal,
$upvotesCount,
$downvotesCount,
$hasUserUpvoted,
$hasUserDownvoted
) {
对我来说,这是更好的解决方案,但目前这行不通,因为我不知道如何简单地从 SQL 行中获取建议(但我几乎没有搜索)。轮到你了
那么,还有什么其他解决方案呢?对于 Doctrine 开发人员来说,是必须是一个众所周知的问题。
最佳答案
您是否已经对此进行了实验或只是考虑过?
对我来说,一个简单的 fetch="EAGER"
解决了这个问题,结果正好是一个查询。使用以下设置
class Proposal {
/**
* @ORM\OneToMany(targetEntity="Vote", mappedBy="proposals", fetch="EAGER")
*/
protected $votes;
}
class Vote {
/**
* @ORM\ManyToOne(targetEntity="Proposal", inversedBy="votes")
* @ORM\JoinColumn(..)
*/
protected $proposal;
/**
* @ORM\ManyToOne(targetEntity="Opinion", inversedBy="votes")
* @ORM\JoinColumn(..)
*/
protected $opinion;
/**
* @ORM\ManyToOne(targetEntity="User", inversedBy="votes")
* @ORM\JoinColumn(..)
*/
protected $user;
}
class Opinion/User {
/**
* @ORM\OneToMany(targetEntity="Vote", mappedBy="proposals")
*/
protected $votes;
}
实际上,我编辑了
Proposal
实体更多,添加以下方法class Proposal {
public function getCountOpinion($id) {
$count = 0;
foreach ($this->getVotes() as $vote) {
if ($vote->getOpinion()->getId() === $id) {
$count++;
}
}
return $count;
}
public function getUserVote($user) {
foreach ($this->getVotes() as $vote) {
if ($vote->getUser() == $user) {
return $vote;
}
}
return null;
}
}
正如我在开头提到的,这仍然只导致一个查询。但是,您会看到很多
for
这里。如果您对每个提案都有很多投票,您可能希望缓存结果(例如,只迭代一次投票,获取所有的 countOpinion 和 userVote)PS:不要忘记为具有 ArrayCollections (OneToMany) 的类添加构造函数
关于sql - Symfony/Doctrine : Optimize n+1 queries,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37497843/