-
-
Notifications
You must be signed in to change notification settings - Fork 139
Expand file tree
/
Copy pathDeliveryOrderFilter.php
More file actions
109 lines (90 loc) · 4.09 KB
/
DeliveryOrderFilter.php
File metadata and controls
109 lines (90 loc) · 4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<?php
namespace AppBundle\Api\Filter;
use AppBundle\Entity\Delivery;
use AppBundle\Entity\Task;
use ApiPlatform\Doctrine\Orm\Filter\FilterInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ParameterNotFound;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Query\Expr\Join;
class DeliveryOrderFilter implements FilterInterface
{
public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
{
if (Delivery::class !== $resourceClass) {
return;
}
$parameter = $context['parameter'] ?? null;
$value = $parameter?->getValue();
// The parameter may not be present
if ($value instanceof ParameterNotFound || null === $value) {
return;
}
// Value should be the direction (asc/desc)
$direction = $value;
$rootAlias = $queryBuilder->getRootAliases()[0];
$parts = $queryBuilder->getDQLPart('join');
$itemsAlias = null;
if (isset($parts[$rootAlias])) {
foreach ($parts[$rootAlias] as $join) {
/** @var Join $join */
if (sprintf('%s.items', $rootAlias) === $join->getJoin()) {
$itemsAlias = $join->getAlias();
break;
}
}
}
if (null === $itemsAlias) {
$itemsAlias = $queryNameGenerator->generateJoinAlias('items');
$queryBuilder->innerJoin(sprintf('%s.items', $rootAlias), $itemsAlias, Join::WITH);
}
$expr = $queryBuilder->expr();
// WARNING
// The result set returned by Doctrine is like below
//
// id | task_id | task_type | position
// ----+---------+-----------+----------
// 13 | 26 | PICKUP | 0
// 13 | 27 | DROPOFF | 1
// 15 | 30 | PICKUP | 0
// 15 | 31 | DROPOFF | 1
// 16 | 32 | PICKUP | 0
// 16 | 33 | DROPOFF | 1
// 14 | 28 | PICKUP | 0
// 14 | 29 | DROPOFF | 1
// 12 | 24 | PICKUP | 0
// 12 | 25 | DROPOFF | 1
// 11 | 22 | PICKUP | 0
// 11 | 23 | DROPOFF | 1
//
// This allows Doctrine to hydrate the Delivery at once
// It seems important to keep the order of rows (pickup, dropoff)
// As we want to order by dropoff date,
// we need to have the dropoff date *BOTH* in the pickup & dropoff row
//
// This class will add a JOIN clause like below
//
// LEFT JOIN task ON (task_collection_item.parent_id = task.delivery_id AND task.type = 'DROPOFF')
$taskAlias = $queryNameGenerator->generateJoinAlias('task');
$condition = $expr->andX(
$expr->eq(sprintf('%s.parent', $itemsAlias), sprintf('%s.delivery', $taskAlias)),
$expr->eq(sprintf('%s.type', $taskAlias), ':task_type')
);
// Write the condition as string or FilterEagerLoadingExtension will break
$condition = (string) $condition;
$queryBuilder->leftJoin(Task::class, $taskAlias, Join::WITH, $condition);
$queryBuilder->setParameter('task_type', 'DROPOFF');
$queryBuilder->addOrderBy(sprintf('%s.doneBefore', $taskAlias), $direction);
// TODO Do not rely on task.delivery_id
// It could be rewritten using a subquery like below, but left join + subquery does not work in Doctrine DQL
// LEFT JOIN (
// SELECT task.* FROM task INNER JOIN task_collection_item task_collection_item.task_id = task.id WHERE task.type = 'DROPOFF'
// ) AS dropoff_task ON dropoff_task.parent_id = delivery.id
}
public function getDescription(string $resourceClass): array
{
// For BC, this function is not useful anymore when documentation occurs on the Parameter
return [];
}
}