Passed
Push — master ( ce7fff...9920f1 )
by Alan
04:04
created

Hydra/Serializer/CollectionFiltersNormalizer.php (1 issue)

Labels
Severity
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();
0 ignored issues
show
The method hasCacheableSupportsMethod() does not exist on Symfony\Component\Serial...zer\NormalizerInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Symfony\Component\Serial...er\SerializerNormalizer or ApiPlatform\Core\Tests\F...DocumentationNormalizer or Symfony\Component\Serial...rmalizer\TestNormalizer or Symfony\Component\Serial...sonSerializerNormalizer or Symfony\Component\Serial...wareNormalizerInterface or Symfony\Component\Serial...ectSerializerNormalizer or Symfony\Component\Serializer\Serializer. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

65
        return $this->collectionNormalizer instanceof CacheableSupportsMethodInterface && $this->collectionNormalizer->/** @scrutinizer ignore-call */ hasCacheableSupportsMethod();
Loading history...
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)) {
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