gpt4 book ai didi

php - 使用 Doctrine2 过滤多对多关联

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:47:41 25 4
gpt4 key购买 nike

我有一个 Account 实体,它有一个 Section 实体集合。每个 Section 实体都有一个 Element 实体的集合(OneToMany 关联)。我的问题是,我不想获取属于某个部分的所有元素,而是想获取属于某个部分的所有元素 并且 与特定帐户相关联。下面是我的数据库模型。

Database model

因此,当我获取一个帐户时,我希望能够遍历其关联的部分(这部分没有问题),并且对于每个部分,我想遍历其与获取的帐户相关联的元素。现在我有以下代码。

$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。如果我能做到这一点并以某种方式重写 Section 实体上的 getElements() 方法,那将是完美的。如果有一种方法可以通过关联映射或至少使用现有的 getter 方法来做到这一点,我会非常愿意。理想情况下,在使用帐户对象时,我希望能够像上面的代码片段一样循环,以便在使用该对象时抽象出“帐户约束”。也就是说,对象的用户不需要在 Section 对象上调用 getElementsByAccount() 或类似方法,因为它看起来不太直观。

我查看了 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
}

最佳答案

如果我理解正确,您希望能够检索Account 的所有Section 的所有Element,但前提是这些 Element 与那个 Account 相关联,这来自 Account 中的 getter。

首先:实体不应该知道存储库。这打破了一个设计原则,可以帮助您换掉持久层。这就是为什么您不能从实体内部简单地访问存储库的原因。

仅限 setter/getter

如果你只想在实体中使用 getter,你可以通过添加以下 2 个方法来解决这个问题:

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;
}
}

存储库

上面的解决方案可能会执行多个查询,具体数量取决于有多少SectionElementAccount相关联>.

当您使用 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:要使此查询正常工作,您需要将 SectionAccount 之间的 ManyToMany 关联定义为双向关联。

代理方法

混合解决方案是向 Account 添加代理方法,将调用转发到您传递给它的存储库。

class Account
{
/**
* @param ElementRepository $repository
* @return Element[]
*/
public function getMyElements(ElementRepository $repository)
{
return $repository->findElementsByAccount($this);
}
}

这样实体仍然不知道存储库,但您允许将一个传递给它。

实现时,不要让 ElementRepository 扩展 EntityRepository,而是 inject EntityRepository 创建时。这样,您仍然可以在不更改实体的情况下换出持久层。

关于php - 使用 Doctrine2 过滤多对多关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23564897/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com