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

api-platform / core / 15133993414

20 May 2025 09:30AM UTC coverage: 26.313% (-1.2%) from 27.493%
15133993414

Pull #7161

github

web-flow
Merge e2c03d45f into 5459ba375
Pull Request #7161: fix(metadata): infer parameter string type from schema

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

11019 existing lines in 363 files now uncovered.

12898 of 49018 relevant lines covered (26.31%)

34.33 hits per line

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

1.61
/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

32
/**
33
 * Loader for PHP attributes using ApiProperty.
34
 */
35
final class PropertyMetadataLoader implements LoaderInterface
36
{
37
    public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory)
38
    {
UNCOV
39
    }
1✔
40

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

49
        $refl = $classMetadata->getReflectionClass();
×
50
        $attributes = [];
×
51
        $classGroups = [];
×
52
        $classContextAnnotation = null;
×
53

54
        foreach ($refl->getAttributes(ApiProperty::class) as $clAttr) {
×
55
            $this->addAttributeMetadata($clAttr->newInstance(), $attributes);
×
56
        }
57

58
        $attributesMetadata = $classMetadata->getAttributesMetadata();
×
59

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

70
            if ($attribute instanceof Groups) {
×
71
                $classGroups = $attribute->getGroups();
×
72

73
                continue;
×
74
            }
75

76
            if ($attribute instanceof Context) {
×
77
                $classContextAnnotation = $attribute;
×
78
            }
79
        }
80

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

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

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

99
            foreach ($classGroups as $group) {
×
100
                $attributesMetadata[$propertyName]->addGroup($group);
×
101
            }
102

103
            if ($classContextAnnotation) {
×
104
                $this->setAttributeContextsForGroups($classContextAnnotation, $attributesMetadata[$propertyName]);
×
105
            }
106

107
            if (!isset($attributes[$propertyName])) {
×
108
                continue;
×
109
            }
110

111
            $attributeMetadata = $attributesMetadata[$propertyName];
×
112

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

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

133
        return true;
×
134
    }
135

136
    /**
137
     * @param ApiProperty[] $attributes
138
     */
139
    private function addAttributeMetadata(ApiProperty $attribute, array &$attributes): void
140
    {
141
        if (($prop = $attribute->getProperty()) && ($value = $attribute->getSerialize())) {
×
142
            $attributes[$prop] = $value;
×
143
        }
144
    }
145

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

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