Completed
Push — master ( 29b346...1faa7f )
by Amrouche
19s
created

src/Bridge/Doctrine/Orm/Filter/OrderFilter.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace ApiPlatform\Core\Bridge\Doctrine\Orm\Filter;
15
16
use ApiPlatform\Core\Bridge\Doctrine\Orm\Util\QueryNameGeneratorInterface;
17
use Doctrine\Common\Persistence\ManagerRegistry;
18
use Doctrine\ORM\QueryBuilder;
19
use Psr\Log\LoggerInterface;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\RequestStack;
22
23
/**
24
 * Order the collection by given properties.
25
 *
26
 * The ordering is done in the same sequence as they are specified in the query,
27
 * and for each property a direction value can be specified.
28
 *
29
 * For each property passed, if the resource does not have such property or if the
30
 * direction value is different from "asc" or "desc" (case insensitive), the property
31
 * is ignored.
32
 *
33
 * @author Kévin Dunglas <[email protected]>
34
 * @author Théo FIDRY <[email protected]>
35
 */
36
class OrderFilter extends AbstractFilter
37
{
38
    const NULLS_SMALLEST = 'nulls_smallest';
39
    const NULLS_LARGEST = 'nulls_largest';
40
    const NULLS_DIRECTION_MAP = [
41
        self::NULLS_SMALLEST => [
42
            'ASC' => 'ASC',
43
            'DESC' => 'DESC',
44
        ],
45
        self::NULLS_LARGEST => [
46
            'ASC' => 'DESC',
47
            'DESC' => 'ASC',
48
        ],
49
    ];
50
51
    /**
52
     * @var string Keyword used to retrieve the value
53
     */
54
    protected $orderParameterName;
55
56
    public function __construct(ManagerRegistry $managerRegistry, RequestStack $requestStack, string $orderParameterName, LoggerInterface $logger = null, array $properties = null)
57
    {
58
        if (null !== $properties) {
59
            $properties = array_map(function ($propertyOptions) {
60
                // shorthand for default direction
61
                if (is_string($propertyOptions)) {
62
                    $propertyOptions = [
63
                        'default_direction' => $propertyOptions,
64
                    ];
65
                }
66
67
                return $propertyOptions;
68
            }, $properties);
69
        }
70
71
        parent::__construct($managerRegistry, $requestStack, $logger, $properties);
72
73
        $this->orderParameterName = $orderParameterName;
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function getDescription(string $resourceClass): array
80
    {
81
        $description = [];
82
83
        $properties = $this->properties;
84
        if (null === $properties) {
85
            $properties = array_fill_keys($this->getClassMetadata($resourceClass)->getFieldNames(), null);
86
        }
87
88
        foreach ($properties as $property => $propertyOptions) {
89
            if (!$this->isPropertyMapped($property, $resourceClass)) {
90
                continue;
91
            }
92
93
            $description[sprintf('%s[%s]', $this->orderParameterName, $property)] = [
94
                'property' => $property,
95
                'type' => 'string',
96
                'required' => false,
97
            ];
98
        }
99
100
        return $description;
101
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106
    protected function filterProperty(string $property, $direction, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, string $operationName = null)
107
    {
108
        if (!$this->isPropertyEnabled($property, $resourceClass) || !$this->isPropertyMapped($property, $resourceClass)) {
109
            return;
110
        }
111
112
        if (empty($direction) && null !== $defaultDirection = $this->properties[$property]['default_direction'] ?? null) {
113
            // fallback to default direction
114
            $direction = $defaultDirection;
115
        }
116
117
        $direction = strtoupper($direction);
118
        if (!in_array($direction, ['ASC', 'DESC'], true)) {
119
            return;
120
        }
121
122
        $alias = 'o';
123
        $field = $property;
124
125 View Code Duplication
        if ($this->isPropertyNested($property, $resourceClass)) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
126
            list($alias, $field) = $this->addJoinsForNestedProperty($property, $alias, $queryBuilder, $queryNameGenerator, $resourceClass);
127
        }
128
129
        if (null !== $nullsComparison = $this->properties[$property]['nulls_comparison'] ?? null) {
130
            $nullsDirection = self::NULLS_DIRECTION_MAP[$nullsComparison][$direction];
131
132
            $nullRankHiddenField = sprintf('_%s_%s_null_rank', $alias, $field);
133
134
            $queryBuilder->addSelect(sprintf('CASE WHEN %s.%s IS NULL THEN 0 ELSE 1 END AS HIDDEN %s', $alias, $field, $nullRankHiddenField));
135
            $queryBuilder->addOrderBy($nullRankHiddenField, $nullsDirection);
136
        }
137
138
        $queryBuilder->addOrderBy(sprintf('%s.%s', $alias, $field), $direction);
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    protected function extractProperties(Request $request/*, string $resourceClass*/): array
145
    {
146
        $properties = $request->query->get($this->orderParameterName, []);
147
        if (!is_array($properties)) {
148
            return [];
149
        }
150
151
        return $properties;
152
    }
153
}
154