Passed
Push — master ( 8bd912...d93388 )
by Alan
06:58 queued 02:20
created

DataProvider/Filter/AbstractFilter.php (1 issue)

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\Elasticsearch\DataProvider\Filter;
15
16
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
17
use ApiPlatform\Core\Bridge\Elasticsearch\Util\FieldDatatypeTrait;
18
use ApiPlatform\Core\Exception\PropertyNotFoundException;
19
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
20
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
21
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
22
use Symfony\Component\PropertyInfo\Type;
23
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
24
25
/**
26
 * Abstract class with helpers for easing the implementation of a filter.
27
 *
28
 * @experimental
29
 *
30
 * @author Baptiste Meyer <[email protected]>
31
 */
32
abstract class AbstractFilter implements FilterInterface
33
{
34
    use FieldDatatypeTrait { getNestedFieldPath as protected; }
35
36
    protected $properties;
37
    protected $propertyNameCollectionFactory;
38
    protected $nameConverter;
39
40
    public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, ?NameConverterInterface $nameConverter = null, ?array $properties = null)
41
    {
42
        $this->propertyNameCollectionFactory = $propertyNameCollectionFactory;
43
        $this->propertyMetadataFactory = $propertyMetadataFactory;
44
        $this->resourceClassResolver = $resourceClassResolver;
45
        $this->nameConverter = $nameConverter;
46
        $this->properties = $properties;
47
    }
48
49
    /**
50
     * Gets all enabled properties for the given resource class.
51
     */
52
    protected function getProperties(string $resourceClass): \Traversable
53
    {
54
        if (null !== $this->properties) {
55
            return yield from array_keys($this->properties);
56
        }
57
58
        try {
59
            yield from $this->propertyNameCollectionFactory->create($resourceClass);
60
        } catch (ResourceClassNotFoundException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
61
        }
62
    }
63
64
    /**
65
     * Is the given property enabled?
66
     */
67
    protected function hasProperty(string $resourceClass, string $property): bool
68
    {
69
        return \in_array($property, iterator_to_array($this->getProperties($resourceClass)), true);
70
    }
71
72
    /**
73
     * Gets info about the decomposed given property for the given resource class.
74
     *
75
     * Returns an array with the following info as values:
76
     *   - the {@see Type} of the decomposed given property
77
     *   - is the decomposed given property an association?
78
     *   - the resource class of the decomposed given property
79
     *   - the property name of the decomposed given property
80
     */
81
    protected function getMetadata(string $resourceClass, string $property): array
82
    {
83
        $noop = [null, null, null, null];
84
85
        if (!$this->hasProperty($resourceClass, $property)) {
86
            return $noop;
87
        }
88
89
        $properties = explode('.', $property);
90
        $totalProperties = \count($properties);
91
        $currentResourceClass = $resourceClass;
92
        $hasAssociation = false;
93
        $currentProperty = null;
94
        $type = null;
95
96
        foreach ($properties as $index => $currentProperty) {
97
            try {
98
                $propertyMetadata = $this->propertyMetadataFactory->create($currentResourceClass, $currentProperty);
99
            } catch (PropertyNotFoundException $e) {
100
                return $noop;
101
            }
102
103
            if (null === $type = $propertyMetadata->getType()) {
104
                return $noop;
105
            }
106
107
            ++$index;
108
            $builtinType = $type->getBuiltinType();
109
110
            if (Type::BUILTIN_TYPE_OBJECT !== $builtinType && Type::BUILTIN_TYPE_ARRAY !== $builtinType) {
111
                if ($totalProperties === $index) {
112
                    break;
113
                }
114
115
                return $noop;
116
            }
117
118
            if ($type->isCollection() && null === $type = $type->getCollectionValueType()) {
119
                return $noop;
120
            }
121
122
            if (Type::BUILTIN_TYPE_ARRAY === $builtinType && Type::BUILTIN_TYPE_OBJECT !== $type->getBuiltinType()) {
123
                if ($totalProperties === $index) {
124
                    break;
125
                }
126
127
                return $noop;
128
            }
129
130
            if (null === $className = $type->getClassName()) {
131
                return $noop;
132
            }
133
134
            if ($isResourceClass = $this->resourceClassResolver->isResourceClass($className)) {
135
                $currentResourceClass = $className;
136
            } elseif ($totalProperties !== $index) {
137
                return $noop;
138
            }
139
140
            $hasAssociation = $totalProperties === $index && $isResourceClass;
141
        }
142
143
        return [$type, $hasAssociation, $currentResourceClass, $currentProperty];
144
    }
145
}
146