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

api-platform / core / 18089937549

29 Sep 2025 07:56AM UTC coverage: 21.764% (-0.3%) from 22.093%
18089937549

Pull #7416

github

web-flow
Merge 061bcc790 into abe0438be
Pull Request #7416: fix(laravel): serializer attributes on Eloquent methods

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

5028 existing lines in 173 files now uncovered.

11889 of 54626 relevant lines covered (21.76%)

25.32 hits per line

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

0.0
/src/Elasticsearch/Filter/AbstractFilter.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\Elasticsearch\Filter;
15

16
use ApiPlatform\Elasticsearch\Util\FieldDatatypeTrait;
17
use ApiPlatform\Metadata\Exception\PropertyNotFoundException;
18
use ApiPlatform\Metadata\Exception\ResourceClassNotFoundException;
19
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
20
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
21
use ApiPlatform\Metadata\ResourceClassResolverInterface;
22
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
23
use Symfony\Component\PropertyInfo\Type as LegacyType;
24
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
25
use Symfony\Component\TypeInfo\Type;
26
use Symfony\Component\TypeInfo\Type\CollectionType;
27
use Symfony\Component\TypeInfo\Type\CompositeTypeInterface;
28
use Symfony\Component\TypeInfo\Type\ObjectType;
29
use Symfony\Component\TypeInfo\Type\WrappingTypeInterface;
30

31
/**
32
 * Abstract class with helpers for easing the implementation of a filter.
33
 *
34
 * @experimental
35
 *
36
 * @author Baptiste Meyer <baptiste.meyer@gmail.com>
37
 */
38
abstract class AbstractFilter implements FilterInterface
39
{
40
    use FieldDatatypeTrait {
41
        getNestedFieldPath as protected;
42
    }
43

44
    public function __construct(protected PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, protected ?NameConverterInterface $nameConverter = null, protected ?array $properties = null)
45
    {
UNCOV
46
        $this->propertyMetadataFactory = $propertyMetadataFactory;
×
47
        $this->resourceClassResolver = $resourceClassResolver;
×
48
    }
49

50
    /**
51
     * Gets all enabled properties for the given resource class.
52
     */
53
    protected function getProperties(string $resourceClass): \Traversable
54
    {
UNCOV
55
        if (null !== $this->properties) {
×
UNCOV
56
            return yield from array_keys($this->properties);
×
57
        }
58

59
        try {
UNCOV
60
            yield from $this->propertyNameCollectionFactory->create($resourceClass);
×
UNCOV
61
        } catch (ResourceClassNotFoundException) {
×
62
        }
63
    }
64

65
    /**
66
     * Is the given property enabled?
67
     */
68
    protected function hasProperty(string $resourceClass, string $property): bool
69
    {
UNCOV
70
        return \in_array($property, iterator_to_array($this->getProperties($resourceClass)), true);
×
71
    }
72

73
    /**
74
     * Gets info about the decomposed given property for the given resource class.
75
     *
76
     * Returns an array with the following info as values:
77
     *   - the {@see Type} of the decomposed given property
78
     *   - is the decomposed given property an association?
79
     *   - the resource class of the decomposed given property
80
     *   - the property name of the decomposed given property
81
     *
82
     * @return array{0: ?Type, 1: ?bool, 2: ?class-string, 3: ?string}
83
     */
84
    protected function getMetadata(string $resourceClass, string $property): array
85
    {
86
        if (!method_exists(PropertyInfoExtractor::class, 'getType')) {
×
87
            return $this->getLegacyMetadata($resourceClass, $property);
×
88
        }
89

UNCOV
90
        $noop = [null, null, null, null];
×
91

92
        if (!$this->hasProperty($resourceClass, $property)) {
×
93
            return $noop;
×
94
        }
95

96
        $properties = explode('.', $property);
×
UNCOV
97
        $totalProperties = \count($properties);
×
98
        $currentResourceClass = $resourceClass;
×
99
        $hasAssociation = false;
×
UNCOV
100
        $currentProperty = null;
×
UNCOV
101
        $type = null;
×
102

UNCOV
103
        foreach ($properties as $index => $currentProperty) {
×
104
            try {
UNCOV
105
                $propertyMetadata = $this->propertyMetadataFactory->create($currentResourceClass, $currentProperty);
×
106
            } catch (PropertyNotFoundException) {
×
UNCOV
107
                return $noop;
×
108
            }
109

110
            // check each type before deciding if it's noop or not
111
            // e.g: maybe the first type is noop, but the second is valid
112
            $isNoop = false;
×
113

UNCOV
114
            ++$index;
×
115

116
            $type = $propertyMetadata->getNativeType();
×
117

118
            if (null === $type) {
×
UNCOV
119
                return $noop;
×
120
            }
121

122
            foreach ($type instanceof CompositeTypeInterface ? $type->getTypes() : [$type] as $t) {
×
UNCOV
123
                $builtinType = $t;
×
124

UNCOV
125
                while ($builtinType instanceof WrappingTypeInterface) {
×
UNCOV
126
                    $builtinType = $builtinType->getWrappedType();
×
127
                }
128

129
                if (!$builtinType instanceof ObjectType && !$t instanceof CollectionType) {
×
UNCOV
130
                    if ($totalProperties === $index) {
×
UNCOV
131
                        break 2;
×
132
                    }
133

134
                    $isNoop = true;
×
135

UNCOV
136
                    continue;
×
137
                }
138

UNCOV
139
                if ($t instanceof CollectionType) {
×
140
                    $t = $t->getCollectionValueType();
×
UNCOV
141
                    $builtinType = $t;
×
142

143
                    while ($builtinType instanceof WrappingTypeInterface) {
×
144
                        $builtinType = $builtinType->getWrappedType();
×
145
                    }
146

UNCOV
147
                    if (!$builtinType instanceof ObjectType) {
×
148
                        if ($totalProperties === $index) {
×
UNCOV
149
                            break 2;
×
150
                        }
151

152
                        $isNoop = true;
×
153

154
                        continue;
×
155
                    }
156
                }
157

158
                $className = $builtinType->getClassName();
×
159

UNCOV
160
                if ($isResourceClass = $this->resourceClassResolver->isResourceClass($className)) {
×
UNCOV
161
                    $currentResourceClass = $className;
×
162
                } elseif ($totalProperties !== $index) {
×
UNCOV
163
                    $isNoop = true;
×
164

UNCOV
165
                    continue;
×
166
                }
167

UNCOV
168
                $hasAssociation = $totalProperties === $index && $isResourceClass;
×
UNCOV
169
                $isNoop = false;
×
170

UNCOV
171
                break;
×
172
            }
173
        }
174

UNCOV
175
        if ($isNoop) {
×
UNCOV
176
            return $noop;
×
177
        }
178

UNCOV
179
        return [$type, $hasAssociation, $currentResourceClass, $currentProperty];
×
180
    }
181

182
    protected function getLegacyMetadata(string $resourceClass, string $property): array
183
    {
UNCOV
184
        $noop = [null, null, null, null];
×
185

UNCOV
186
        if (!$this->hasProperty($resourceClass, $property)) {
×
UNCOV
187
            return $noop;
×
188
        }
189

UNCOV
190
        $properties = explode('.', $property);
×
UNCOV
191
        $totalProperties = \count($properties);
×
UNCOV
192
        $currentResourceClass = $resourceClass;
×
UNCOV
193
        $hasAssociation = false;
×
UNCOV
194
        $currentProperty = null;
×
UNCOV
195
        $type = null;
×
196

UNCOV
197
        foreach ($properties as $index => $currentProperty) {
×
198
            try {
UNCOV
199
                $propertyMetadata = $this->propertyMetadataFactory->create($currentResourceClass, $currentProperty);
×
UNCOV
200
            } catch (PropertyNotFoundException) {
×
UNCOV
201
                return $noop;
×
202
            }
203

UNCOV
204
            $types = $propertyMetadata->getBuiltinTypes();
×
205

UNCOV
206
            if (null === $types) {
×
UNCOV
207
                return $noop;
×
208
            }
209

UNCOV
210
            ++$index;
×
211

212
            // check each type before deciding if it's noop or not
213
            // e.g: maybe the first type is noop, but the second is valid
UNCOV
214
            $isNoop = false;
×
215

UNCOV
216
            foreach ($types as $type) {
×
UNCOV
217
                $builtinType = $type->getBuiltinType();
×
218

UNCOV
219
                if (LegacyType::BUILTIN_TYPE_OBJECT !== $builtinType && LegacyType::BUILTIN_TYPE_ARRAY !== $builtinType) {
×
UNCOV
220
                    if ($totalProperties === $index) {
×
UNCOV
221
                        break 2;
×
222
                    }
223

UNCOV
224
                    $isNoop = true;
×
225

UNCOV
226
                    continue;
×
227
                }
228

UNCOV
229
                if ($type->isCollection() && null === $type = $type->getCollectionValueTypes()[0] ?? null) {
×
UNCOV
230
                    $isNoop = true;
×
231

UNCOV
232
                    continue;
×
233
                }
234

UNCOV
235
                if (LegacyType::BUILTIN_TYPE_ARRAY === $builtinType && LegacyType::BUILTIN_TYPE_OBJECT !== $type->getBuiltinType()) {
×
UNCOV
236
                    if ($totalProperties === $index) {
×
UNCOV
237
                        break 2;
×
238
                    }
239

UNCOV
240
                    $isNoop = true;
×
241

UNCOV
242
                    continue;
×
243
                }
244

UNCOV
245
                if (null === $className = $type->getClassName()) {
×
UNCOV
246
                    $isNoop = true;
×
247

UNCOV
248
                    continue;
×
249
                }
250

UNCOV
251
                if ($isResourceClass = $this->resourceClassResolver->isResourceClass($className)) {
×
UNCOV
252
                    $currentResourceClass = $className;
×
UNCOV
253
                } elseif ($totalProperties !== $index) {
×
UNCOV
254
                    $isNoop = true;
×
255

UNCOV
256
                    continue;
×
257
                }
258

UNCOV
259
                $hasAssociation = $totalProperties === $index && $isResourceClass;
×
UNCOV
260
                $isNoop = false;
×
261

UNCOV
262
                break;
×
263
            }
264

UNCOV
265
            if ($isNoop) {
×
UNCOV
266
                return $noop;
×
267
            }
268
        }
269

UNCOV
270
        return [$type, $hasAssociation, $currentResourceClass, $currentProperty];
×
271
    }
272
}
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

© 2026 Coveralls, Inc