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

api-platform / core / 18116719337

30 Sep 2025 02:25AM UTC coverage: 0.0% (-22.0%) from 21.956%
18116719337

Pull #7397

github

web-flow
Merge ad9be8ad8 into 55fd65795
Pull Request #7397: fix(jsonschema/jsonld): make `@id` and `@type` properties required only in the JSON-LD schema for output

0 of 15 new or added lines in 1 file covered. (0.0%)

12143 existing lines in 402 files now uncovered.

0 of 53916 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/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\Hydra\IriTemplateMapping;
17
use ApiPlatform\Hydra\State\Util\SearchHelperTrait;
18
use ApiPlatform\JsonLd\Serializer\HydraPrefixTrait;
19
use ApiPlatform\Metadata\FilterInterface;
20
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
21
use ApiPlatform\Metadata\ResourceClassResolverInterface;
22
use ApiPlatform\State\Util\StateOptionsTrait;
23
use Psr\Container\ContainerInterface;
24
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
25
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
26
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
27
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
28

29
/**
30
 * Enhances the result of collection by adding the filters applied on collection.
31
 *
32
 * @author Samuel ROZE <samuel.roze@gmail.com>
33
 */
34
final class CollectionFiltersNormalizer implements NormalizerInterface, NormalizerAwareInterface
35
{
36
    use HydraPrefixTrait;
37
    use SearchHelperTrait;
38
    use StateOptionsTrait;
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
    ) {
UNCOV
52
        $this->filterLocator = $filterLocator;
×
53
    }
54

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

63
    /**
64
     * @param string|null $format
65
     */
66
    public function getSupportedTypes($format): array
67
    {
UNCOV
68
        return $this->collectionNormalizer->getSupportedTypes($format);
×
69
    }
70

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

UNCOV
80
        $data = $this->collectionNormalizer->normalize($object, $format, $context);
×
UNCOV
81
        if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
×
UNCOV
82
            return $data;
×
83
        }
84

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

UNCOV
91
        $parameters = $operation->getParameters();
×
UNCOV
92
        $resourceFilters = $operation->getFilters();
×
UNCOV
93
        if (!$resourceFilters && !$parameters) {
×
UNCOV
94
            return $data;
×
95
        }
96

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

UNCOV
108
        $resourceClass = $this->getStateOptionsClass($operation, $resourceClass);
×
109

UNCOV
110
        if ($currentFilters || ($parameters && \count($parameters))) {
×
UNCOV
111
            $hydraPrefix = $this->getHydraPrefix($context + $this->defaultContext);
×
UNCOV
112
            ['mapping' => $mapping, 'keys' => $keys] = $this->getSearchMappingAndKeys($operation, $resourceClass, $currentFilters, $parameters, [$this, 'getFilter']);
×
UNCOV
113
            $data[$hydraPrefix.'search'] = [
×
UNCOV
114
                '@type' => $hydraPrefix.'IriTemplate',
×
UNCOV
115
                $hydraPrefix.'template' => \sprintf('%s{?%s}', $requestParts['path'], implode(',', $keys)),
×
UNCOV
116
                $hydraPrefix.'variableRepresentation' => 'BasicRepresentation',
×
UNCOV
117
                $hydraPrefix.'mapping' => $this->convertMappingToArray($mapping),
×
UNCOV
118
            ];
×
119
        }
120

UNCOV
121
        return $data;
×
122
    }
123

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

134
    /**
135
     * @param list<IriTemplateMapping> $mapping
136
     *
137
     * @return array<array<string, mixed>>
138
     */
139
    private function convertMappingToArray(array $mapping): array
140
    {
UNCOV
141
        $convertedMapping = [];
×
UNCOV
142
        foreach ($mapping as $m) {
×
UNCOV
143
            $converted = [
×
UNCOV
144
                '@type' => 'IriTemplateMapping',
×
UNCOV
145
                'variable' => $m->variable,
×
UNCOV
146
                'property' => $m->property,
×
UNCOV
147
            ];
×
148

UNCOV
149
            if (null !== ($r = $m->required)) {
×
UNCOV
150
                $converted['required'] = $r;
×
151
            }
152

UNCOV
153
            $convertedMapping[] = $converted;
×
154
        }
155

UNCOV
156
        return $convertedMapping;
×
157
    }
158

159
    /**
160
     * Gets a filter with a backward compatibility.
161
     */
162
    private function getFilter(string $filterId): ?FilterInterface
163
    {
UNCOV
164
        if ($this->filterLocator && $this->filterLocator->has($filterId)) {
×
UNCOV
165
            return $this->filterLocator->get($filterId);
×
166
        }
167

168
        return null;
×
169
    }
170
}
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