需要 Doctrine 多对多 QueryBuilder 查询 Return NOT In Common Rows

Need Doctrine Many to Many QueryBuilder Query to Return NOT In Common Rows

我有两个 tables 1) 价格和 2) 用户

所有者是价格 table 中的一列。

两个 table 在 price.users 和 users.price 之间存在多对多关系。

以下查询 returns owner1 和 user1 共享的所有价格。

问题:如何修复此查询,它只 returns 所有所有者 1 的价格未与用户 1 同步。

如果我使用 ->andWhere('u.id = :user1Id') 那么我只会得到 user1 的记录。

如果我使用 ->andWhere('u.id != :user1Id') 那么我会得到包括 user1 记录在内的所有所有者记录。

我再次想要除与 user1 同步的记录之外的所有所有者记录。 到目前为止,我已经尝试了以下方法:

1) $queryUsersPrices
                        ->innerJoin('p.owner', 'o')
                        ->leftJoin('p.users', 'u')
                        ->andWhere('o.id = :ownerId')
                        /*I need to Remove records for u.id from results*/
                        ->andWhere('u.id = :user1Id')
                        ->setParameter('owner1Id', $owner->getId())
                        ->setParameter('user1Id', $user->getId());

                    $userPrices = $queryUsersPrices->getQuery()->getResult();

2) $userPrices =   $repository->createQueryBuilder($alias);
                $userPrices
                    ->select("u.prices")
                    ->from("Price","p")
                    ->innerJoin('p.users', 'u')
                    ->andWhere('u.id = :userId')
                    ->getDQL();

   $query = $repository->createQueryBuilder($alias);
                $query
                    ->innerJoin($alias . '.owner', 'o')
                    ->innerJoin($alias . '.priceType', 'pt')
                    ->innerJoin($alias . '.model', 'm')
                    ->where(
                        $query->expr()->not(
                            $query->expr()->in(
                                'p.id',
                                $userPrices
                            )
                        )
                    )
                    ->andWhere('m.status = :m_status')
                    ->andWhere('o.id = :adminId')
                    ->andWhere('pt.site <> 1')
                    ->setParameter('m_status',  Model::STATUS_ACTIVE);

                $result = $query->getQuery()->getResult();

3) $query = $repository->createQueryBuilder($alias);
                $query
                    ->innerJoin($alias . '.owner', 'o')
                    ->innerJoin($alias . '.users', 'u', 'WITH', 'u.id = 
                     :userId')
                    ->innerJoin($alias . '.priceType', 'pt')
                    ->innerJoin($alias . '.model', 'm')
                    ->where('m.status = :m_status')
                    ->andWhere('o.id = :adminId')
                    ->andWhere('u.id IS NULL')
                    ->andWhere('pt.site <> 1')
                    ->setParameter('adminId', $adminUser->getId())
                    ->setParameter('userId',  $user->getId())
                    ->setParameter('m_status',  Model::STATUS_ACTIVE);

                $test = $query->getQuery()->getResult();

方法 #1 仅产生用户 1 的价格

方法 #2 导致此错误:错误:方法 Doctrine\Common\Collections\ArrayCollection::__toString() 不得抛出异常,已捕获 Symfony\Component\Debug\Exception\ContextErrorException:可捕获的致命错误:[ 的对象=47=] Doctrine\ORM\EntityManager 无法转换为字符串

方法 #3 仅产生所有者价格

这是基于 M Khalid Junaid 回答的实际结果

$userPrices =   $repository->createQueryBuilder('pr')
                    ->innerJoin('pr.users', 'u')
                    ->andWhere('u.id = :userId')
                    ->setParameter('userId',  $user->getId())
                    ->getDQL();

                $query = $repository->createQueryBuilder($alias);
                $query
                    ->innerJoin($alias . '.owner', 'o')
                    ->innerJoin($alias . '.priceType', 'pt')
                    ->innerJoin($alias . '.model', 'm')
                    ->where(
                        $query->expr()->not(
                            $query->expr()->in(
                                $alias . '.id',
                                $userPrices
                            )
                        )
                    )
                    ->andWhere('m.status = :m_status')
                    ->andWhere('o.id = :adminId')
                    ->andWhere('pt.site <> 1')
                    ->setParameter('m_status',  Model::STATUS_ACTIVE)
                    ->setParameter('adminId', $adminUser->getId())
                    ->setParameter('userId',  $user->getId());

                $result = $query->getQuery()->getResult();

我建议将您的逻辑分解为

属于$user->getId()的第select个价格为

$userPrices =   $this->createQueryBuilder("u")
                     ->select("u.prices")
                     ->from("YourBundleName:Prices","p")
                     ->innerJoin('p.users', 'u')
                     ->andWhere('u.id = :user1Id')
                     ->getDQL();

然后获取 $owner->getId() 所有者的价格,并从 $user->getId() 的子查询中排除价格作为

$qb = $this->createQueryBuilder("pr");
 $qb->select("pr")
    ->from("YourBundleName:Price", "pr")
    ->innerJoin('pr.owner', 'o')
    ->where(
        $qb->expr()->not(
            $qb->expr()->in(
            "pr.id",
            $userPrices
            )
        )
    )
    ->andWhere('o.id = :ownerId')
    ->setParameter('owner1Id', $owner->getId())
    ->setParameter('user1Id', $user->getId())
;
$query = $qb->getQuery();
$result = $query->getResult();

这更像是您的原始查询,但与我猜想的不完全一样,可能需要根据您的映射进行一些调整,但会给您一个继续进行此查询的想法

参考资料

  • Doctrine EXIST and NOT EXIST

我想直接在 DQL 中转换逻辑会非常方便,方法是使用 price.users 进行左连接,并在连接部分添加额外的过滤子句,这样它将仅连接用户 ID 所在价格的行是 $user->getId() 并且要排除属于 $user->getId() 的这些价格,我们可以使用 where 子句作为 u.id IS NULL

DQL

SELECT p
FROM Price p
JOIN p.owners o 
LEFT JOIN p.users u WITH u.id = :user1Id
WHERE u.id IS NULL
AND o.id = :ownerId

查询生成器会像

$qb =  $this->createQueryBuilder("p")
            ->select("p")
            ->from("Price", "p")
            ->innerJoin('p.owner', 'o')
            ->leftJoin(
                'p.users',
                'u',
                'WITH',
                'u.id = :user1Id'
            )
            ->where('u.id IS NULL')
            ->andWhere('o.id = :ownerId')
            ->setParameter('owner1Id', $owner->getId())
            ->setParameter('user1Id', $user->getId())
        ;
$query = $qb->getQuery();
$result = $query->getResult();