• 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/Serializer/Mapping/Loader/PropertyMetadataLoader.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\Serializer\Mapping\Loader;
15

16
use ApiPlatform\Metadata\ApiProperty;
17
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
18
use Illuminate\Database\Eloquent\Model;
19
use Symfony\Component\Serializer\Attribute\Context;
20
use Symfony\Component\Serializer\Attribute\DiscriminatorMap;
21
use Symfony\Component\Serializer\Attribute\Groups;
22
use Symfony\Component\Serializer\Attribute\Ignore;
23
use Symfony\Component\Serializer\Attribute\MaxDepth;
24
use Symfony\Component\Serializer\Attribute\SerializedName;
25
use Symfony\Component\Serializer\Attribute\SerializedPath;
26
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
27
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
28
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
29
use Symfony\Component\Serializer\Mapping\ClassMetadataInterface;
30
use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface;
31
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
32

33
/**
34
 * Loader for PHP attributes using ApiProperty.
35
 */
36
final class PropertyMetadataLoader implements LoaderInterface
37
{
38
    public function __construct(
39
        private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory,
40
        private readonly ?NameConverterInterface $nameConverter = null,
41
    ) {
UNCOV
42
    }
×
43

44
    public function loadClassMetadata(ClassMetadataInterface $classMetadata): bool
45
    {
46
        // It's very weird to grab Eloquent's properties in that case as they're never serialized
47
        // the Serializer makes a call on the abstract class, let's save some unneeded work with a condition
48
        if (Model::class === $classMetadata->getName()) {
×
49
            return false;
×
50
        }
51

52
        $refl = $classMetadata->getReflectionClass();
×
53
        $attributes = [];
×
54
        $classGroups = [];
×
55
        $classContextAnnotation = null;
×
56

57
        foreach ($refl->getAttributes(ApiProperty::class) as $clAttr) {
×
58
            $this->addAttributeMetadata($clAttr->newInstance(), $attributes);
×
59
        }
60

61
        $attributesMetadata = $classMetadata->getAttributesMetadata();
×
62

63
        foreach ($refl->getAttributes() as $a) {
×
64
            $attribute = $a->newInstance();
×
65
            if ($attribute instanceof DiscriminatorMap) {
×
66
                $classMetadata->setClassDiscriminatorMapping(new ClassDiscriminatorMapping(
×
67
                    $attribute->getTypeProperty(),
×
68
                    $attribute->getMapping()
×
69
                ));
×
70
                continue;
×
71
            }
72

73
            if ($attribute instanceof Groups) {
×
74
                $classGroups = $attribute->getGroups();
×
75

76
                continue;
×
77
            }
78

79
            if ($attribute instanceof Context) {
×
80
                $classContextAnnotation = $attribute;
×
81
            }
82
        }
83

84
        foreach ($refl->getProperties() as $reflProperty) {
×
85
            foreach ($reflProperty->getAttributes(ApiProperty::class) as $propAttr) {
×
86
                $this->addAttributeMetadata($propAttr->newInstance()->withProperty($reflProperty->name), $attributes);
×
87
            }
88
        }
89

90
        foreach ($refl->getMethods() as $reflMethod) {
×
91
            foreach ($reflMethod->getAttributes(ApiProperty::class) as $methodAttr) {
×
92
                $this->addAttributeMetadata($methodAttr->newInstance()->withProperty($reflMethod->getName()), $attributes);
×
93
            }
94
        }
95

96
        foreach ($this->propertyNameCollectionFactory->create($classMetadata->getName()) as $propertyName) {
×
97
            if (!isset($attributesMetadata[$propertyName])) {
×
98
                $attributesMetadata[$propertyName] = new AttributeMetadata($propertyName);
×
99
                $classMetadata->addAttributeMetadata($attributesMetadata[$propertyName]);
×
100
            }
101

102
            foreach ($classGroups as $group) {
×
103
                $attributesMetadata[$propertyName]->addGroup($group);
×
104
            }
105

106
            if ($classContextAnnotation) {
×
107
                $this->setAttributeContextsForGroups($classContextAnnotation, $attributesMetadata[$propertyName]);
×
108
            }
109

110
            if (!isset($attributes[$propertyName])) {
×
111
                continue;
×
112
            }
113

114
            $attributeMetadata = $attributesMetadata[$propertyName];
×
115

116
            // This code is adapted from Symfony\Component\Serializer\Mapping\Loader\AttributeLoader
117
            foreach ($attributes[$propertyName] as $attr) {
×
118
                if ($attr instanceof Groups) {
×
119
                    foreach ($attr->getGroups() as $group) {
×
120
                        $attributeMetadata->addGroup($group);
×
121
                    }
122
                    continue;
×
123
                }
124

UNCOV
125
                match (true) {
×
126
                    $attr instanceof MaxDepth => $attributeMetadata->setMaxDepth($attr->getMaxDepth()),
×
127
                    $attr instanceof SerializedName => $attributeMetadata->setSerializedName($attr->getSerializedName()),
×
128
                    $attr instanceof SerializedPath => $attributeMetadata->setSerializedPath($attr->getSerializedPath()),
×
129
                    $attr instanceof Ignore => $attributeMetadata->setIgnore(true),
×
130
                    $attr instanceof Context => $this->setAttributeContextsForGroups($attr, $attributeMetadata),
×
131
                    default => null,
×
UNCOV
132
                };
×
133
            }
134
        }
135

136
        return true;
×
137
    }
138

139
    /**
140
     * @param array<string, array<mixed>> $attributes
141
     */
142
    private function addAttributeMetadata(ApiProperty $attribute, array &$attributes): void
143
    {
144
        if (($prop = $this->nameConverter?->denormalize($attribute->getProperty()) ?? $attribute->getProperty()) && ($value = $attribute->getSerialize())) {
×
145
            $attributes[$prop] = $value;
×
146
        }
147
    }
148

149
    private function setAttributeContextsForGroups(Context $annotation, AttributeMetadataInterface $attributeMetadata): void
150
    {
151
        $context = $annotation->getContext();
×
152
        $groups = $annotation->getGroups();
×
153
        $normalizationContext = $annotation->getNormalizationContext();
×
154
        $denormalizationContext = $annotation->getDenormalizationContext();
×
155
        if ($normalizationContext || $context) {
×
156
            $attributeMetadata->setNormalizationContextForGroups($normalizationContext ?: $context, $groups);
×
157
        }
158

159
        if ($denormalizationContext || $context) {
×
160
            $attributeMetadata->setDenormalizationContextForGroups($denormalizationContext ?: $context, $groups);
×
161
        }
162
    }
163
}
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