【问题标题】:Doctrine one-to-many UNIDIRECTIONAL find on owning side在拥有方找到一对多单向原则
【发布时间】:2020-03-25 00:10:22
【问题描述】:

问题定义

因此,鉴于 Doctrine 文档的this part(以及许多其他关于双向关联和持久性问题的文章),我有一个单向的一对多关联。它位于ClubStaff 成员之间。从域逻辑来看,Club 应该拥有Staff。例如,如果我使用文档存储,Staff 将是Club 的成员。在 99.9% 的域案例中,我有一个 club 并想要一份其员工名单。

但是,我在 Doctrine 中实施这个想法时遇到了麻烦。因为在教义中,一对多关系只有反面。因此,即使从概念上讲,以下实体定义也是错误的,但我不知道更好。

实体代码

<?php

class Club
{
    /**
     * @ORM\Id
     * @ORM\Column(type="uuid", unique=true)
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
     */
    public ?UuidInterface $id;

    /**
     * @ORM\Column(type="string", name="name")
     */
    public string $name;

    /**
     * @ORM\OneToMany(targetEntity="Staff", mappedBy="club")
     */
    public Collection $staff;

    public function __construct(string $name)
    {
        $this->id       = Uuid::uuid4();
        $this->name     = $name;
        $this->staff    = new ArrayCollection();
    }

    public function addStaff(Staff $staff): void
    {
        $this->staff->add($staff);
    }

}

class Staff
{
    public const ROLE_OWNER = 'owner';

    /**
     * @ORM\Id
     * @ORM\Column(type="uuid", unique=true)
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
     */
    public ?UuidInterface $id;

    /**
     * @ORM\Column(type="string", name="user_id")
     */
    public string $userId;

    /**
     * @ORM\Column(type="string", name="role")
     */
    public string $role;

    public function __construct(string $userId, string $role)
    {
        $this->id     = Uuid::uuid4();
        $this->userId = $userId;
        $this->role   = $role;
    }
}

但真正的问题来了。我有一个用例,我想为合适的员工找到俱乐部。这是一个 (ONE-)MANY-ONE 问题,需要注意的是,第一个 one 不是另一个 Doctrine 实体,而是 Staff 实体的 userId

所以我尝试了几种方法来获取给定userIdClub 实体:

尝试失败

<?php
   // 1. Naive solution, just try to use the `ObjectRepository`
   // Will cause: You cannot search for the association field 'Club#staff', because it is the inverse side of an association. Find methods only work on owning side associations.
$staffers = $this->em->getRepository(Staff::class)->findBy(['userId' => $ownerId]);
$clubs = $this->em->getRepository(Club::class)->findBy(['staff' => $staffers]);

   // 2. Use `QueryBuilder` naively
   // Will cause: [Semantical Error] line 0, col 131 near 'staff = s.id': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected
         $qb = $this->em->createQueryBuilder();
         $query = $qb->select('c')
         ->from(Club::class, 'c')
         ->join(Staff::class, 's', Join::WITH, 'c.staff = s.id')
         ->where('s.userId = :id')
         ->setParameter('id', $ownerId)
         ->getQuery();

    // 3. Use `QueryBuilder` with knowledge if actual DB columns
    // Will cause: [Semantical Error] line 0, col 138 near 'club_id WHERE': Error: Class Staff has no field or association named club_id
         $qb = $this->em->createQueryBuilder();
         $query = $qb->select('c')
         ->from(Club::class, 'c')
         ->join(Staff::class, 's', Join::WITH, 'c.id = s.club_id')
         ->where('s.userId = :id')
         ->setParameter('id', $ownerId)
         ->getQuery();

    // 4. Create query directly
    // Will cause: [Semantical Error] line 0, col 125 near 'staff = s.id': Error: Invalid PathExpression. StateFieldPathExpression or SingleValuedAssociationField expected
                $query = $this->em->createQuery('SELECT c FROM ' . Club::class . ' c JOIN ' . Staff::class . ' s WITH c.staff = s.id WHERE s.userId = :id')->setParameter('id', $ownerId);

我在寻找什么?

  • ClubStaff 之间的单向关联,这样我就不必担心持久性、性能、不一致等问题。只需双向关联的所有问题。
  • 要么:
    • 实体/关联的可能返工
    • 一种检索Club实体的方法,给定staff.userId

【问题讨论】:

  • 好吧,如果你绝对必须避免双向,native query 怎么样?
  • @El_Vanja 我没有经验,但是从查看链接来看,这不会对表结构产生很大的依赖性吗?我正处于项目的快速开发阶段,所以实体和底层表结构经常变化。
  • 对不起,我错过了你的部分问题,并跳到前面写一个答案(现已删除)。对于您的情况,为什么不将其定义为双向?即使这是一个用例,我认为您不会产生很多开销。我肯定会倾向于保持代码比编写一行又一行的 dql 或本机查询更简单。
  • @AlexKarshin 如果我以一种方式有 100 个用例,而另一种方式有 1 个用例,那对我来说没有多大意义。这是一个权衡。您宁愿拥有一个 DQL/Native 查询,还是希望与它所需要的所有内容建立双向关联。现在我宁愿拥有 DQL。

标签: php orm doctrine-orm doctrine doctrine-query


【解决方案1】:

一个有效的解决方案,但我无法解释为什么这个有效而其他无效。

<?php
$this->em->createQueryBuilder()
            ->select('c')
            ->from(Club::class, 'c')
            ->innerJoin(Staff::class, 's')
            ->where('s.userId = :owner')
            ->setParameter('owner', $ownerId)
            ->getQuery()
            ->getResult();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    • 2013-09-15
    • 2017-10-11
    • 1970-01-01
    相关资源
    最近更新 更多