Doctrine ORM:根据嵌套关系的值排除记录
Doctrine ORM: Excluding Records Based on Values of Nested Relationships
快速概览
大家好!我正在开发一款能够让 Order
包含多个 OrderStatus
的应用程序,这些 OrderStatus
然后与 Status
实体本身有关系。这是一种简化的 ERD 图:
题目:
我想要做的是获取 Orders
,其中最后一个 OrderStatus
没有匹配特定标签的状态,这是一个字符串。所以没有订单的最后 OrderStatus
有 Status
标签为“已完成”、“待定”等。我几乎使用 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
,我们需要 greatest-n-per-group 逻辑。为每个 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();
快速概览
大家好!我正在开发一款能够让 Order
包含多个 OrderStatus
的应用程序,这些 OrderStatus
然后与 Status
实体本身有关系。这是一种简化的 ERD 图:
题目:
我想要做的是获取 Orders
,其中最后一个 OrderStatus
没有匹配特定标签的状态,这是一个字符串。所以没有订单的最后 OrderStatus
有 Status
标签为“已完成”、“待定”等。我几乎使用 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
,我们需要 greatest-n-per-group 逻辑。为每个 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();