我有一个Account
实体,其中包含Section
个实体的集合。每个Section
实体都有Element
个实体的集合(OneToMany关联)。我的问题是,我不想获取属于某个部分的所有元素,而是想获取属于和部分与特定帐户相关联的所有元素。下面是我的数据库模型。
因此,当我获取一个帐户时,我希望能够遍历其关联的部分(这部分没有问题),并且对于每个部分,我想循环遍历与获取的帐户关联的元素。现在我有以下代码。
$repository = $this->objectManager->getRepository('MyModule\Entity\Account');
$account = $repository->find(1);
foreach ($account->getSections() as $section) {
foreach ($section->getElements() as $element) {
echo $element->getName() . PHP_EOL;
}
}
问题在于它获取属于给定部分的所有元素,无论它们与哪个帐户相关联。生成的用于获取节的元素的SQL如下所示。
SELECT t0.id AS id1, t0.name AS name2, t0.section_id AS section_id3
FROM mydb.element t0
WHERE t0.section_id = ?
我需要它做的事情如下(可能是任何其他方法)。使用SQL完成过滤非常重要。
SELECT e.id, e.name, e.section_id
FROM element AS e
INNER JOIN account_element AS ae ON (ae.element_id = e.id)
WHERE ae.account_id = ?
AND e.section_id = ?
我知道我可以在自定义存储库中编写方法getElementsBySection($accountId)
或类似方法并使用DQL。如果我可以这样做并以某种方式覆盖getElements()
实体上的Section
方法,那么这将是完美的。我非常希望是否有办法通过关联映射或至少通过使用现有的getter方法来实现这一点。理想情况下,当使用帐户对象时,我希望能够像上面的代码片段一样循环,以便在使用对象时抽象出“帐户约束”。也就是说,对象的用户不需要在getElementsByAccount()
对象上调用Section
或类似对象,因为它似乎不太直观。
我查看了Criteria
对象,但据我记忆,它不能用于过滤关联。
那么,实现这一目标的最佳方法是什么?是否可以通过使用DQL查询“手动”组装Section
实体元素?我目前(和缩短)的源代码如下所示。非常感谢提前!
/**
* @ORM\Entity
*/
class Account
{
/**
* @var int
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue
*/
protected $id;
/**
* @var string
* @ORM\Column(type="string", length=50, nullable=false)
*/
protected $name;
/**
* @var ArrayCollection
* @ORM\ManyToMany(targetEntity="MyModule\Entity\Section")
* @ORM\JoinTable(name="account_section",
* joinColumns={@ORM\JoinColumn(name="account_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="section_id", referencedColumnName="id")}
* )
*/
protected $sections;
public function __construct()
{
$this->sections = new ArrayCollection();
}
// Getters and setters
}
/**
* @ORM\Entity
*/
class Section
{
/**
* @var int
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @var string
* @ORM\Column(type="string", length=50, nullable=false)
*/
protected $name;
/**
* @var ArrayCollection
* @ORM\OneToMany(targetEntity="MyModule\Entity\Element", mappedBy="section")
*/
protected $elements;
public function __construct()
{
$this->elements = new ArrayCollection();
}
// Getters and setters
}
/**
* @ORM\Entity
*/
class Element
{
/**
* @var int
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
protected $id;
/**
* @var string
* @ORM\Column(type="string", length=50, nullable=false)
*/
protected $name;
/**
* @var Section
* @ORM\ManyToOne(targetEntity="MyModule\Entity\Section", inversedBy="elements")
* @ORM\JoinColumn(name="section_id", referencedColumnName="id")
*/
protected $section;
/**
* @var \MyModule\Entity\Account
* @ORM\ManyToMany(targetEntity="MyModule\Entity\Account")
* @ORM\JoinTable(name="account_element",
* joinColumns={@ORM\JoinColumn(name="element_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="account_id", referencedColumnName="id")}
* )
*/
protected $account;
// Getters and setters
}
答案 0 :(得分:1)
如果我理解正确,您希望能够检索Element
的所有Section
中的所有Account
,但前提是这些Element
是关联的使用Account
,这来自帐户中的getter。
首先:实体永远不应该知道存储库。这打破了一个帮助您更换持久层的设计原则。这就是为什么你不能从实体内部简单地访问存储库的原因。
仅限吸毒者
如果您只想在实体中使用getter,可以通过添加以下两种方法来解决此问题:
class Section
{
/**
* @param Account $accout
* @return Element[]
*/
public function getElementsByAccount(Account $accout)
{
$elements = array();
foreach ($this->getElements() as $element) {
if ($element->getAccount() === $account) {
$elements[] = $element->getAccount();
}
}
return $elements;
}
}
class Account
{
/**
* @return Element[]
*/
public function getMyElements()
{
$elements = array()
foreach ($this->getSections() as $section) {
foreach ($section->getElementsByAccount($this) as $element) {
$elements[] = $element;
}
}
return $elements;
}
}
<强>存储库强>
上述解决方案可能会执行多次查询,具体金额取决于与Section
相关联的Element
和Account
的数量。
当您使用Repository方法时,您可能会获得性能提升,因此您可以优化用于检索所需内容的查询/查询。
一个例子:
class ElementRepository extends EntityRepository
{
/**
* @param Account $account [description]
* @return Element[]
*/
public function findElementsByAccount(Account $account)
{
$dql = <<< 'EOQ'
SELECT e FROM Element e
JOIN e.section s
JOIN s.accounts a
WHERE e.account = ?1 AND a.id = ?2
EOQ;
$q = $this->getEntityManager()->createQuery($dql);
$q->setParameters(array(
1 => $account->getId(),
2 => $account->getId()
));
return $q->getResult();
}
}
PS:要使此查询生效,您需要将Section
和Account
之间的ManyToMany关联定义为双向关联。
代理方法
混合解决方案是向Account
添加代理方法,将代理方法转发到您传递给它的存储库。
class Account
{
/**
* @param ElementRepository $repository
* @return Element[]
*/
public function getMyElements(ElementRepository $repository)
{
return $repository->findElementsByAccount($this);
}
}
通过这种方式,实体仍然不知道存储库,但您可以将其传递给它。
执行此操作时,请勿ElementRepository
EntityRepository
EntityRepository
{{1}} {{1}}创建{{1}}。这样,您仍然可以在不改变实体的情况下更换持久层。