Doctrine ORM:根据嵌套关系的值排除记录

Doctrine ORM: Excluding Records Based on Values of Nested Relationships

快速概览

大家好!我正在开发一款能够让 Order 包含多个 OrderStatus 的应用程序,这些 OrderStatus 然后与 Status 实体本身有关系。这是一种简化的 ERD 图:

题目:

我想要做的是获取 Orders,其中最后一个 OrderStatus 没有匹配特定标签的状态,这是一个字符串。所以没有订单的最后 OrderStatusStatus 标签为“已完成”、“待定”等。我几乎使用 OrderStatus 作为一种 link table 因为我需要能够跟踪状态更改之间的时间/保留历史记录。否则我会生成它作为多对多关系。

简化实体定义:

Order.php

/**
 * @ORM\Entity(repositoryClass=OrderRepository::class)
 * @ORM\Table(name="`order`")
 */
class Order
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\OneToMany(targetEntity=OrderStatus::class, mappedBy="cust_order", orphanRemoval=true, cascade={"persist", "remove"}, fetch="EAGER")
     */
    private $orderStatuses;
...

OrderStatus.php

/**
 * @ORM\Entity(repositoryClass=OrderStatusRepository::class)
 */
class OrderStatus
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity=Status::class, fetch="EAGER")
     * @ORM\JoinColumn(nullable=false)
     */
    private $status;

    /**
     * @ORM\ManyToOne(targetEntity=Order::class, inversedBy="orderStatuses")
     * @ORM\JoinColumn(nullable=false)
     */
    private $cust_order;

    /**
     * @ORM\Column(type="datetime")
     */
    private $created;

    public function __construct(Order $order = null, Status $status = null) {
        $this->created = new DateTime("NOW");
        $this->cust_order = $order; 
        $this->status = $status; 
    }...

Status.php

/**
 * @ORM\Entity(repositoryClass=StatusRepository::class)
 */
class Status
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $label;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $tag;

如果能得到任何帮助,我将不胜感激!我不是最熟悉像这样的更复杂的查询。我相当确定这可以通过使用查询构建器的子查询以某种方式解决,但我无法完全理解如何去做,因为我很少需要使用子查询。

感谢您提供的任何帮助!

编辑: 这是我尝试过的东西,但所有这些实际上所做的只是过滤掉实际状态本身。我需要过滤掉整个订单:

            $qb
                ->innerJoin("e.orderStatuses", "os")
                ->innerJoin("os.status", "s")
                ->andWhere("s.tag IN (:nonPendingStatuses)")
                ->setParameter("nonPendingStatuses", [
                    "new","in_progress","awaiting_pickup","fulfilled"
                ], \Doctrine\DBAL\Connection::PARAM_STR_ARRAY);

更新

这是我目前的尝试。我想我已经接近了,但我还没有完全掌握它。当前出现数组到字符串的转换错误。

        /**
         * Get orders awaiting fulfillment 
         */

         public function getActiveOrders(){
             $qb = $this->repository->createQueryBuilder("o");
            //  $sub_qb = $em->createQueryBuilder();
             $qb-> 
                andWhere(
                    $qb->expr()->notIn(
                        $qb->select('os.id')
                        ->from("App\Entity\OrderStatus", "os")
                        ->andWhere("os.custOrder = o")
                        ->orderBy("os.created", "desc")
                        ->setMaxResults(1)
                        ->getQuery()->getResult()
                        ,
                        $qb
                        ->select('s.id')
                        ->from("App\Entity\Status", "s")
                        ->orWhere("s.tag = 'fulfilled'")
                        ->orWhere("s.tag = 'pending'")
                        // ->andWhere($qb->expr()->in("s.tag",":exclusions"))
                        // ->setParameter(":exclusions", ['pending', 'fulfilled'])
                        ->getQuery()->getResult()
                    )
                )
                ;
                $query = $qb->getQuery();
            return $query->getResult();
         }

更新 这是我正在尝试实现的 SQL 查询:

SELECT *
FROM   `order` o
WHERE  (SELECT status_id
        FROM   order_status os
        WHERE  os.cust_order_id = o.id
        ORDER  BY os.created DESC
        LIMIT  1) NOT IN (SELECT id
                          FROM   status s
                          WHERE  s.tag IN ( 'pending', 'fulfilled' )); 

What I want to do is get Orders where the last OrderStatus doesn't have a status matching a specific tag

我假设将根据较高的 created 列值选择最新的 OrderStatus

要为每个订单选择最新的 OrderStatus,我们需要 逻辑。为每个 group/order

只选择一个最新记录

纯SQL可以通过window functions or

实现

SQL

SELECT o.*
FROM `order` o
JOIN order_status os ON o.id = os.cust_order_id
LEFT JOIN order_status os1 ON os.cust_order_id = os1.cust_order_id
                           AND os.created < os1.created
JOIN STATUS s ON s.id = os.status_id
WHERE os1.created IS NULL
AND s.tag NOT IN ('pending')

在查询生成器中可以写成

$this->createQueryBuilder('o')
    ->innerJoin("o.orderStatuses", "os")
    ->innerJoin("os.status", "s")
    ->leftJoin(
        'Bundle\Entity\OrderStatus',
        'os1',
        'WITH',
        'os.cust_order = os1.cust_order AND os.created < os1.created'
    )
    ->where('os1.created IS NULL')
    ->andWhere("s.tag NOT IN (:pendingStatus)")
    ->setParameter("pendingStatus", ["pending"], \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
    ->getQuery()
    ->getResult();