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

api-platform / core / 14954769666

11 May 2025 10:14AM UTC coverage: 0.0% (-8.5%) from 8.457%
14954769666

Pull #7135

github

web-flow
Merge bf21e0bc7 into 4dd0cdfc4
Pull Request #7135: fix(symfony,laravel): InvalidUriVariableException status code (e400)

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

11040 existing lines in 370 files now uncovered.

0 of 48303 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/Doctrine/Common/State/PersistProcessor.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\Doctrine\Common\State;
15

16
use ApiPlatform\Metadata\HttpOperation;
17
use ApiPlatform\Metadata\Operation;
18
use ApiPlatform\Metadata\Util\ClassInfoTrait;
19
use ApiPlatform\State\ProcessorInterface;
20
use Doctrine\Persistence\ManagerRegistry;
21
use Doctrine\Persistence\ObjectManager as DoctrineObjectManager;
22

23
final class PersistProcessor implements ProcessorInterface
24
{
25
    use ClassInfoTrait;
26
    use LinksHandlerTrait;
27

28
    public function __construct(private readonly ManagerRegistry $managerRegistry)
29
    {
UNCOV
30
    }
×
31

32
    /**
33
     * @template T
34
     *
35
     * @param T $data
36
     *
37
     * @return T
38
     */
39
    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
40
    {
41
        if (
UNCOV
42
            !\is_object($data)
×
UNCOV
43
            || !$manager = $this->managerRegistry->getManagerForClass($class = $this->getObjectClass($data))
×
44
        ) {
45
            return $data;
×
46
        }
47

48
        // PUT: reset the existing object managed by Doctrine and merge data sent by the user in it
49
        // This custom logic is needed because EntityManager::merge() has been deprecated and UPSERT isn't supported:
50
        // https://github.com/doctrine/orm/issues/8461#issuecomment-1250233555
UNCOV
51
        if ($operation instanceof HttpOperation && 'PUT' === $operation->getMethod() && ($operation->getExtraProperties()['standard_put'] ?? true)) {
×
UNCOV
52
            \assert(method_exists($manager, 'getReference'));
×
53
            $newData = $data;
×
54
            $identifiers = array_reverse($uriVariables);
×
55
            $links = $this->getLinks($class, $operation, $context);
×
56
            $reflectionProperties = $this->getReflectionProperties($data);
×
57

58
            // TODO: the call to getReference is most likely to fail with complex identifiers
59
            if ($previousData = $context['previous_data']) {
×
60
                $classMetadata = $manager->getClassMetadata($class);
×
61
                $identifiers = $classMetadata->getIdentifierValues($previousData);
×
62
                $newData = 1 === \count($identifiers) ? $manager->getReference($class, current($identifiers)) : clone $previousData;
×
63

64
                foreach ($reflectionProperties as $propertyName => $reflectionProperty) {
×
65
                    // // Don't override the property if it's part of the subresource system
66
                    if (isset($identifiers[$propertyName]) || isset($uriVariables[$propertyName])) {
×
67
                        continue;
×
68
                    }
69

70
                    // Skip URI variables as sometime an uri variable is not the doctrine identifier
71
                    foreach ($links as $link) {
×
72
                        if (\in_array($propertyName, $link->getIdentifiers(), true)) {
×
73
                            continue 2;
×
74
                        }
75
                    }
76

77
                    if (($newValue = $reflectionProperty->getValue($data)) !== $reflectionProperty->getValue($newData)) {
×
78
                        $reflectionProperty->setValue($newData, $newValue);
×
79
                    }
80
                }
81
            } else {
82
                foreach (array_reverse($links) as $link) {
×
83
                    if ($link->getExpandedValue() || !$link->getFromClass()) {
×
84
                        continue;
×
85
                    }
86

87
                    $identifierProperties = $link->getIdentifiers();
×
88
                    $hasCompositeIdentifiers = 1 < \count($identifierProperties);
×
89

90
                    foreach ($identifierProperties as $identifierProperty) {
×
91
                        $reflectionProperty = $reflectionProperties[$identifierProperty];
×
92
                        $reflectionProperty->setValue($newData, $this->getIdentifierValue($identifiers, $hasCompositeIdentifiers ? $identifierProperty : null));
×
93
                    }
94
                }
95
            }
96

97
            $data = $newData;
×
98
        }
99

UNCOV
100
        if (!$manager->contains($data) || $this->isDeferredExplicit($manager, $data)) {
×
UNCOV
101
            $manager->persist($data);
×
102
        }
103

UNCOV
104
        $manager->flush();
×
UNCOV
105
        $manager->refresh($data);
×
106

UNCOV
107
        return $data;
×
108
    }
109

110
    /**
111
     * Checks if doctrine does not manage data automatically.
112
     */
113
    private function isDeferredExplicit(DoctrineObjectManager $manager, $data): bool
114
    {
115
        $classMetadata = $manager->getClassMetadata($this->getObjectClass($data));
×
116
        if ($classMetadata && method_exists($classMetadata, 'isChangeTrackingDeferredExplicit')) { // @phpstan-ignore-line metadata can be null
×
117
            return $classMetadata->isChangeTrackingDeferredExplicit();
×
118
        }
119

120
        return false;
×
121
    }
122

123
    /**
124
     * Get reflection properties indexed by property name.
125
     *
126
     * @return array<string, \ReflectionProperty>
127
     */
128
    private function getReflectionProperties(mixed $data): array
129
    {
130
        $ret = [];
×
131
        $r = new \ReflectionObject($data);
×
132

133
        do {
134
            $props = $r->getProperties(~\ReflectionProperty::IS_STATIC);
×
135

136
            foreach ($props as $prop) {
×
137
                $ret[$prop->getName()] = $prop;
×
138
            }
139
        } while ($r = $r->getParentClass());
×
140

141
        return $ret;
×
142
    }
143
}
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