• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 14954769666

11 May 2025 10:14AM UTC coverage: 0.0% (-8.5%) from 8.457%
14954769666

Pull #7135

github

web-flow
Merge bf21e0bc7 into 4dd0cdfc4
Pull Request #7135: fix(symfony,laravel): InvalidUriVariableException status code (e400)

0 of 2 new or added lines in 2 files covered. (0.0%)

11040 existing lines in 370 files now uncovered.

0 of 48303 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Doctrine/Common/Filter/SearchFilterTrait.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
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\Doctrine\Common\Filter;
15

16
use ApiPlatform\Doctrine\Common\PropertyHelperTrait;
17
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
18
use ApiPlatform\Metadata\IdentifiersExtractorInterface;
19
use ApiPlatform\Metadata\IriConverterInterface;
20
use Psr\Log\LoggerInterface;
21
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
22

23
/**
24
 * Trait for filtering the collection by given properties.
25
 *
26
 * @author Kévin Dunglas <dunglas@gmail.com>
27
 * @author Alan Poulain <contact@alanpoulain.eu>
28
 */
29
trait SearchFilterTrait
30
{
31
    use PropertyHelperTrait;
32

33
    protected IriConverterInterface $iriConverter;
34
    protected PropertyAccessorInterface $propertyAccessor;
35
    protected ?IdentifiersExtractorInterface $identifiersExtractor = null;
36

37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function getDescription(string $resourceClass): array
41
    {
UNCOV
42
        $description = [];
×
43

UNCOV
44
        $properties = $this->getProperties();
×
UNCOV
45
        if (null === $properties) {
×
46
            $properties = array_fill_keys($this->getClassMetadata($resourceClass)->getFieldNames(), null);
×
47
        }
48

UNCOV
49
        foreach ($properties as $property => $strategy) {
×
UNCOV
50
            if (!$this->isPropertyMapped($property, $resourceClass, true)) {
×
UNCOV
51
                continue;
×
52
            }
53

UNCOV
54
            if ($this->isPropertyNested($property, $resourceClass)) {
×
UNCOV
55
                $propertyParts = $this->splitPropertyParts($property, $resourceClass);
×
UNCOV
56
                $field = $propertyParts['field'];
×
UNCOV
57
                $metadata = $this->getNestedMetadata($resourceClass, $propertyParts['associations']);
×
58
            } else {
UNCOV
59
                $field = $property;
×
UNCOV
60
                $metadata = $this->getClassMetadata($resourceClass);
×
61
            }
62

UNCOV
63
            $propertyName = $this->normalizePropertyName($property);
×
UNCOV
64
            if ($metadata->hasField($field)) {
×
UNCOV
65
                $typeOfField = $this->getType($metadata->getTypeOfField($field));
×
UNCOV
66
                $strategy = $this->getProperties()[$property] ?? self::STRATEGY_EXACT;
×
UNCOV
67
                $filterParameterNames = [$propertyName];
×
68

UNCOV
69
                if (\in_array($strategy, [self::STRATEGY_EXACT, self::STRATEGY_IEXACT], true)) {
×
UNCOV
70
                    $filterParameterNames[] = $propertyName.'[]';
×
71
                }
72

UNCOV
73
                foreach ($filterParameterNames as $filterParameterName) {
×
UNCOV
74
                    $description[$filterParameterName] = [
×
UNCOV
75
                        'property' => $propertyName,
×
UNCOV
76
                        'type' => $typeOfField,
×
UNCOV
77
                        'required' => false,
×
UNCOV
78
                        'strategy' => $strategy,
×
UNCOV
79
                        'is_collection' => str_ends_with((string) $filterParameterName, '[]'),
×
UNCOV
80
                    ];
×
81
                }
UNCOV
82
            } elseif ($metadata->hasAssociation($field)) {
×
UNCOV
83
                $filterParameterNames = [
×
UNCOV
84
                    $propertyName,
×
UNCOV
85
                    $propertyName.'[]',
×
UNCOV
86
                ];
×
87

UNCOV
88
                foreach ($filterParameterNames as $filterParameterName) {
×
UNCOV
89
                    $description[$filterParameterName] = [
×
UNCOV
90
                        'property' => $propertyName,
×
UNCOV
91
                        'type' => 'string',
×
UNCOV
92
                        'required' => false,
×
UNCOV
93
                        'strategy' => self::STRATEGY_EXACT,
×
UNCOV
94
                        'is_collection' => str_ends_with((string) $filterParameterName, '[]'),
×
UNCOV
95
                    ];
×
96
                }
97
            }
98
        }
99

UNCOV
100
        return $description;
×
101
    }
102

103
    /**
104
     * Converts a Doctrine type in PHP type.
105
     */
106
    abstract protected function getType(string $doctrineType): string;
107

108
    abstract protected function getProperties(): ?array;
109

110
    abstract protected function getLogger(): LoggerInterface;
111

112
    abstract protected function getIriConverter(): IriConverterInterface;
113

114
    abstract protected function getPropertyAccessor(): PropertyAccessorInterface;
115

116
    abstract protected function normalizePropertyName(string $property): string;
117

118
    /**
119
     * Gets the ID from an IRI or a raw ID.
120
     */
121
    protected function getIdFromValue(string $value): mixed
122
    {
123
        try {
124
            $iriConverter = $this->getIriConverter();
×
125
            $item = $iriConverter->getResourceFromIri($value, ['fetch_data' => false]);
×
126

127
            if (null === $this->identifiersExtractor) {
×
128
                return $this->getPropertyAccessor()->getValue($item, 'id');
×
129
            }
130

131
            $identifiers = $this->identifiersExtractor->getIdentifiersFromItem($item);
×
132

133
            return 1 === \count($identifiers) ? array_pop($identifiers) : $identifiers;
×
134
        } catch (InvalidArgumentException) {
×
135
            // Do nothing, return the raw value
136
        }
137

138
        return $value;
×
139
    }
140

141
    /**
142
     * Normalize the values array.
143
     */
144
    protected function normalizeValues(array $values, string $property): ?array
145
    {
UNCOV
146
        foreach ($values as $key => $value) {
×
UNCOV
147
            if (!\is_int($key) || !(\is_string($value) || \is_int($value))) {
×
148
                unset($values[$key]);
×
149
            }
150
        }
151

UNCOV
152
        if (empty($values)) {
×
153
            $this->getLogger()->notice('Invalid filter ignored', [
×
154
                'exception' => new InvalidArgumentException(\sprintf('At least one value is required, multiple values should be in "%1$s[]=firstvalue&%1$s[]=secondvalue" format', $property)),
×
155
            ]);
×
156

157
            return null;
×
158
        }
159

UNCOV
160
        return array_values($values);
×
161
    }
162

163
    /**
164
     * When the field should be an integer, check that the given value is a valid one.
165
     */
166
    protected function hasValidValues(array $values, ?string $type = null): bool
167
    {
UNCOV
168
        foreach ($values as $value) {
×
UNCOV
169
            if (null !== $value && \in_array($type, (array) self::DOCTRINE_INTEGER_TYPE, true) && false === filter_var($value, \FILTER_VALIDATE_INT)) {
×
170
                return false;
×
171
            }
172
        }
173

UNCOV
174
        return true;
×
175
    }
176
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc