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

api-platform / core / 11229883409

08 Oct 2024 06:32AM UTC coverage: 7.833% (+0.03%) from 7.804%
11229883409

push

github

web-flow
test(laravel): call factories, debug is now false by default (#6702)

1 of 79 new or added lines in 11 files covered. (1.27%)

1730 existing lines in 107 files now uncovered.

12939 of 165183 relevant lines covered (7.83%)

27.01 hits per line

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

93.94
/src/Hydra/Serializer/CollectionFiltersNormalizer.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\Hydra\Serializer;
15

16
use ApiPlatform\Doctrine\Odm\State\Options as ODMOptions;
17
use ApiPlatform\Doctrine\Orm\State\Options;
18
use ApiPlatform\JsonLd\Serializer\HydraPrefixTrait;
19
use ApiPlatform\Metadata\FilterInterface;
20
use ApiPlatform\Metadata\Parameter;
21
use ApiPlatform\Metadata\Parameters;
22
use ApiPlatform\Metadata\QueryParameterInterface;
23
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
24
use ApiPlatform\Metadata\ResourceClassResolverInterface;
25
use Psr\Container\ContainerInterface;
26
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
27
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
28
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
29
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
30

31
/**
32
 * Enhances the result of collection by adding the filters applied on collection.
33
 *
34
 * @author Samuel ROZE <samuel.roze@gmail.com>
35
 */
36
final class CollectionFiltersNormalizer implements NormalizerInterface, NormalizerAwareInterface
37
{
38
    use HydraPrefixTrait;
39
    private ?ContainerInterface $filterLocator = null;
40

41
    /**
42
     * @param ContainerInterface   $filterLocator  The new filter locator or the deprecated filter collection
43
     * @param array<string, mixed> $defaultContext
44
     */
45
    public function __construct(
46
        private readonly NormalizerInterface $collectionNormalizer,
47
        private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory,
48
        private readonly ResourceClassResolverInterface $resourceClassResolver,
49
        ?ContainerInterface $filterLocator = null,
50
        private readonly array $defaultContext = [],
51
    ) {
52
        $this->filterLocator = $filterLocator;
2,622✔
53
    }
54

55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
59
    {
60
        return $this->collectionNormalizer->supportsNormalization($data, $format, $context);
1,059✔
61
    }
62

63
    public function getSupportedTypes($format): array
64
    {
65
        return $this->collectionNormalizer->getSupportedTypes($format);
2,319✔
66
    }
67

68
    /**
69
     * {@inheritdoc}
70
     */
71
    public function normalize(mixed $object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
72
    {
73
        if (($context[AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS] ?? false) && $object instanceof \ArrayObject && !\count($object)) {
1,059✔
UNCOV
74
            return $object;
3✔
75
        }
76

77
        $data = $this->collectionNormalizer->normalize($object, $format, $context);
1,059✔
78
        if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
1,059✔
79
            return $data;
708✔
80
        }
81

82
        if (!\is_array($data)) {
617✔
83
            throw new UnexpectedValueException('Expected data to be an array');
×
84
        }
85
        $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
617✔
86
        $operation = $context['operation'] ?? $this->resourceMetadataCollectionFactory->create($resourceClass)->getOperation($context['operation_name'] ?? null);
617✔
87

88
        $parameters = $operation->getParameters();
617✔
89
        $resourceFilters = $operation->getFilters();
617✔
90
        if (!$resourceFilters && !$parameters) {
617✔
91
            return $data;
235✔
92
        }
93

94
        $requestParts = parse_url($context['request_uri'] ?? '');
382✔
95
        if (!\is_array($requestParts)) {
382✔
96
            return $data;
×
97
        }
98
        $currentFilters = [];
382✔
99
        foreach ($resourceFilters as $filterId) {
382✔
100
            if ($filter = $this->getFilter($filterId)) {
352✔
101
                $currentFilters[] = $filter;
352✔
102
            }
103
        }
104

105
        if ($options = $operation->getStateOptions()) {
382✔
106
            if ($options instanceof Options && $options->getEntityClass()) {
358✔
107
                $resourceClass = $options->getEntityClass();
4✔
108
            }
109

110
            if ($options instanceof ODMOptions && $options->getDocumentClass()) {
358✔
UNCOV
111
                $resourceClass = $options->getDocumentClass();
4✔
112
            }
113
        }
114

115
        if ($currentFilters || ($parameters && \count($parameters))) {
382✔
116
            $hydraPrefix = $this->getHydraPrefix($context + $this->defaultContext);
382✔
117
            $data[$hydraPrefix.'search'] = $this->getSearch($resourceClass, $requestParts, $currentFilters, $parameters, $hydraPrefix);
382✔
118
        }
119

120
        return $data;
382✔
121
    }
122

123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function setNormalizer(NormalizerInterface $normalizer): void
127
    {
128
        if ($this->collectionNormalizer instanceof NormalizerAwareInterface) {
2,622✔
129
            $this->collectionNormalizer->setNormalizer($normalizer);
2,622✔
130
        }
131
    }
132

133
    /**
134
     * Returns the content of the Hydra search property.
135
     *
136
     * @param FilterInterface[]        $filters
137
     * @param array<string, Parameter> $parameters
138
     */
139
    private function getSearch(string $resourceClass, array $parts, array $filters, array|Parameters|null $parameters, string $hydraPrefix): array
140
    {
141
        $variables = [];
382✔
142
        $mapping = [];
382✔
143
        foreach ($filters as $filter) {
382✔
144
            foreach ($filter->getDescription($resourceClass) as $variable => $data) {
352✔
145
                $variables[] = $variable;
351✔
146
                $mapping[] = ['@type' => 'IriTemplateMapping', 'variable' => $variable, 'property' => $data['property'] ?? null, 'required' => $data['required'] ?? false];
351✔
147
            }
148
        }
149

150
        foreach ($parameters ?? [] as $key => $parameter) {
382✔
151
            // Each IriTemplateMapping maps a variable used in the template to a property
152
            if (!$parameter instanceof QueryParameterInterface || false === $parameter->getHydra()) {
334✔
153
                continue;
316✔
154
            }
155

156
            if (!($property = $parameter->getProperty()) && ($filterId = $parameter->getFilter()) && ($filter = $this->getFilter($filterId))) {
30✔
157
                foreach ($filter->getDescription($resourceClass) as $variable => $description) {
6✔
158
                    // This is a practice induced by PHP and is not necessary when implementing URI template
159
                    if (str_ends_with((string) $variable, '[]')) {
6✔
160
                        continue;
6✔
161
                    }
162

163
                    // :property is a pattern allowed when defining parameters
164
                    $k = str_replace(':property', $description['property'], $key);
6✔
165
                    $variable = str_replace($description['property'], $k, $variable);
6✔
166
                    $variables[] = $variable;
6✔
167
                    $m = ['@type' => 'IriTemplateMapping', 'variable' => $variable, 'property' => $description['property'], 'required' => $description['required']];
6✔
168
                    if (null !== ($required = $parameter->getRequired())) {
6✔
169
                        $m['required'] = $required;
×
170
                    }
171
                    $mapping[] = $m;
6✔
172
                }
173

174
                continue;
6✔
175
            }
176

177
            if (!$property) {
30✔
178
                continue;
24✔
179
            }
180

181
            $m = ['@type' => 'IriTemplateMapping', 'variable' => $key, 'property' => $property];
12✔
182
            $variables[] = $key;
12✔
183
            if (null !== ($required = $parameter->getRequired())) {
12✔
184
                $m['required'] = $required;
6✔
185
            }
186
            $mapping[] = $m;
12✔
187
        }
188

189
        return ['@type' => $hydraPrefix.'IriTemplate', $hydraPrefix.'template' => \sprintf('%s{?%s}', $parts['path'], implode(',', $variables)), $hydraPrefix.'variableRepresentation' => 'BasicRepresentation', $hydraPrefix.'mapping' => $mapping];
382✔
190
    }
191

192
    /**
193
     * Gets a filter with a backward compatibility.
194
     */
195
    private function getFilter(string $filterId): ?FilterInterface
196
    {
197
        if ($this->filterLocator && $this->filterLocator->has($filterId)) {
358✔
198
            return $this->filterLocator->get($filterId);
358✔
199
        }
200

201
        return null;
×
202
    }
203
}
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