pie*_*ard 3 sql doctrine eager-loading symfony
想象一组提案,其中每个用户都可以对提案投赞成票或反对票。
所以我有2个模型:
Proposal,Vote,与 a proposal、 auser和 an opinion(向上或向下)相关。现在我想显示我的所有提案,包括每个提案的额外信息:
这真的很容易实现,但这意味着每个提案显示 4 个请求(称为 n+1 查询问题)。我来自 Rails 世界,在那里可以使用预先加载或一些棘手的查询轻松解决此问题。但是我无法用教义解决它,需要一些帮助!
第一种解决方案是进行自定义查询:选择所有提案,包括赞成票、反对票的数量,当前用户是否赞成或反对。
查询现在返回4分列比模型来描述:count_upvotes,count_downvotes,has_user_upvoted和has_user_downvoted。
为了与学说一起使用,我必须将这些字段添加到我的Proposal实体中。
对我来说,这不是一个好的解决方案,因为它意味着始终使用此查询来避免混蛋模型,其中这些字段可以为空。
第二种解决方案是让视图使用另一个对象。这个对象是用一个参数创建的:当前用户。该对象由优化查询创建,包含以下方法:
getAllProposals()getUpvotes($a_proposal)getDownvotes($a_proposal)hasUserUpvoted($a_proposal)hasUserDownvoted($a_proposal)对我来说,仅仅为了视图优化而被迫创建一个对象真的太过分了。
这第三个解决方案使用唯一的查询来获取提案、他们的赞成票、反对票,并检查用户是否赞成或反对。
它创建了一个新的“扩展”实体,
public function __construct(
Proposal $badgeProposal,
$upvotesCount,
$downvotesCount,
$hasUserUpvoted,
$hasUserDownvoted
) {
Run Code Online (Sandbox Code Playgroud)
对我来说,这是更好的解决方案,但目前这行不通,因为我不知道如何简单地从 SQL 行中获取建议(但我几乎没有搜索)。
那么,还有什么其他解决方案呢?对于学说开发人员来说,是必须是一个众所周知的问题。
您是否已经对此进行了实验或只是考虑过?
对我来说,一个简单的方法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;
}
Run Code Online (Sandbox Code Playgroud)
实际上,我对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;
}
}
Run Code Online (Sandbox Code Playgroud)
正如我在开头提到的,这仍然只导致一个查询。然而,你在for这里看到了很多。如果您对每个提案有很多投票,您可能希望缓存结果(例如,只迭代投票一次,获取所有 countOpinion 和 userVote)
PS:不要忘记为具有 ArrayCollections (OneToMany) 的类添加构造函数