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

Hydra/Serializer/CollectionFiltersNormalizer.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\Hydra\Serializer;
15
16
use ApiPlatform\Core\Api\FilterCollection;
17
use ApiPlatform\Core\Api\FilterInterface;
18
use ApiPlatform\Core\Api\FilterLocatorTrait;
19
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
20
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
21
use Psr\Container\ContainerInterface;
22
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
23
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
24
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
25
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
26
27
/**
28
 * Enhances the result of collection by adding the filters applied on collection.
29
 *
30
 * @author Samuel ROZE <[email protected]>
31
 */
32
final class CollectionFiltersNormalizer implements NormalizerInterface, NormalizerAwareInterface, CacheableSupportsMethodInterface
33
{
34
    use FilterLocatorTrait;
35
36
    private $collectionNormalizer;
37
    private $resourceMetadataFactory;
38
    private $resourceClassResolver;
39
40
    /**
41
     * @param ContainerInterface|FilterCollection $filterLocator The new filter locator or the deprecated filter collection
42
     */
43
    public function __construct(NormalizerInterface $collectionNormalizer, ResourceMetadataFactoryInterface $resourceMetadataFactory, ResourceClassResolverInterface $resourceClassResolver, $filterLocator)
44
    {
45
        $this->setFilterLocator($filterLocator);
46
47
        $this->collectionNormalizer = $collectionNormalizer;
48
        $this->resourceMetadataFactory = $resourceMetadataFactory;
49
        $this->resourceClassResolver = $resourceClassResolver;
50
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55
    public function supportsNormalization($data, $format = null)
56
    {
57
        return $this->collectionNormalizer->supportsNormalization($data, $format);
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function hasCacheableSupportsMethod(): bool
64
    {
65
        return $this->collectionNormalizer instanceof CacheableSupportsMethodInterface && $this->collectionNormalizer->hasCacheableSupportsMethod();
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function normalize($object, $format = null, array $context = [])
72
    {
73
        $data = $this->collectionNormalizer->normalize($object, $format, $context);
74
        if (!\is_array($data)) {
75
            throw new UnexpectedValueException('Expected data to be an array');
76
        }
77
78
        if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
79
            return $data;
80
        }
81
82
        $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
83
        $resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
84
85
        $operationName = $context['collection_operation_name'] ?? null;
86
        if (null === $operationName) {
87
            $resourceFilters = $resourceMetadata->getAttribute('filters', []);
88
        } else {
89
            $resourceFilters = $resourceMetadata->getCollectionOperationAttribute($operationName, 'filters', [], true);
90
        }
91
92
        if (!$resourceFilters) {
93
            return $data;
94
        }
95
96
        $requestParts = parse_url($context['request_uri'] ?? '');
97
        if (!\is_array($requestParts)) {
0 ignored issues
show
The condition is_array($requestParts) is always true.
Loading history...
98
            return $data;
99
        }
100
101
        $currentFilters = [];
102
        foreach ($resourceFilters as $filterId) {
103
            if ($filter = $this->getFilter($filterId)) {
104
                $currentFilters[] = $filter;
105
            }
106
        }
107
108
        if ($currentFilters) {
109
            $data['hydra:search'] = $this->getSearch($resourceClass, $requestParts, $currentFilters);
110
        }
111
112
        return $data;
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118
    public function setNormalizer(NormalizerInterface $normalizer)
119
    {
120
        if ($this->collectionNormalizer instanceof NormalizerAwareInterface) {
121
            $this->collectionNormalizer->setNormalizer($normalizer);
122
        }
123
    }
124
125
    /**
126
     * Returns the content of the Hydra search property.
127
     *
128
     * @param FilterInterface[] $filters
129
     */
130
    private function getSearch(string $resourceClass, array $parts, array $filters): array
131
    {
132
        $variables = [];
133
        $mapping = [];
134
        foreach ($filters as $filter) {
135
            foreach ($filter->getDescription($resourceClass) as $variable => $data) {
136
                $variables[] = $variable;
137
                $mapping[] = [
138
                    '@type' => 'IriTemplateMapping',
139
                    'variable' => $variable,
140
                    'property' => $data['property'],
141
                    'required' => $data['required'],
142
                ];
143
            }
144
        }
145
146
        return [
147
            '@type' => 'hydra:IriTemplate',
148
            'hydra:template' => sprintf('%s{?%s}', $parts['path'], implode(',', $variables)),
149
            'hydra:variableRepresentation' => 'BasicRepresentation',
150
            'hydra:mapping' => $mapping,
151
        ];
152
    }
153
}
154