Passed
Pull Request — master (#1253)
by Alexander
12:07
created

DoctrineObjectConstructor::construct()   D

Complexity

Conditions 18
Paths 19

Size

Total Lines 78
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 18

Importance

Changes 0
Metric Value
cc 18
eloc 36
nc 19
nop 5
dl 0
loc 78
ccs 23
cts 23
cp 1
crap 18
rs 4.8666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Construction;
6
7
use Doctrine\Persistence\ManagerRegistry;
8
use JMS\Serializer\DeserializationContext;
9
use JMS\Serializer\Exception\InvalidArgumentException;
10
use JMS\Serializer\Exception\ObjectConstructionException;
11
use JMS\Serializer\Exclusion\ExpressionLanguageExclusionStrategy;
12
use JMS\Serializer\Metadata\ClassMetadata;
13
use JMS\Serializer\Metadata\PropertyMetadata;
14
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
15
16
/**
17
 * Doctrine object constructor for new (or existing) objects during deserialization.
18
 */
19
final class DoctrineObjectConstructor implements ObjectConstructorInterface
20
{
21
    public const ON_MISSING_NULL = 'null';
22
    public const ON_MISSING_EXCEPTION = 'exception';
23
    public const ON_MISSING_FALLBACK = 'fallback';
24
    /**
25
     * @var string
26
     */
27
    private $fallbackStrategy;
28
29
    /**
30
     * @var ManagerRegistry
31
     */
32
    private $managerRegistry;
33
34
    /**
35
     * @var ObjectConstructorInterface
36
     */
37 10
    private $fallbackConstructor;
38
39 10
    /**
40 10
     * @var ExpressionLanguageExclusionStrategy|null
41 10
     */
42 10
    private $expressionLanguageExclusionStrategy;
43
44
    /**
45
     * @param ManagerRegistry $managerRegistry     Manager registry
46
     * @param ObjectConstructorInterface $fallbackConstructor Fallback object constructor
47 10
     */
48
    public function __construct(
49
        ManagerRegistry $managerRegistry,
50 10
        ObjectConstructorInterface $fallbackConstructor,
51
        string $fallbackStrategy = self::ON_MISSING_NULL,
52 10
        ?ExpressionLanguageExclusionStrategy $expressionLanguageExclusionStrategy = null
53
    ) {
54
        $this->managerRegistry = $managerRegistry;
55
        $this->fallbackConstructor = $fallbackConstructor;
56
        $this->fallbackStrategy = $fallbackStrategy;
57
        $this->expressionLanguageExclusionStrategy = $expressionLanguageExclusionStrategy;
58 10
    }
59
60 10
    /**
61
     * {@inheritdoc}
62
     */
63
    public function construct(DeserializationVisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context): ?object
64
    {
65
        // Locate possible ObjectManager
66 10
        $objectManager = $this->managerRegistry->getManagerForClass($metadata->name);
67
68 1
        if (!$objectManager) {
69
            // No ObjectManager found, proceed with normal deserialization
70
            return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
71
        }
72 9
73 9
        // Locate possible ClassMetadata
74
        $classMetadataFactory = $objectManager->getMetadataFactory();
75 9
76
        if ($classMetadataFactory->isTransient($metadata->name)) {
77 9
            // No ClassMetadata found, proceed with normal deserialization
78 1
            return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
79
        }
80 8
81
        // Managed entity, check for proxy load
82
        if (!\is_array($data) && !(is_object($data) && 'SimpleXMLElement' === get_class($data))) {
83 9
            // Single identifier, load proxy
84 1
            return $objectManager->getReference($metadata->name, $data);
0 ignored issues
show
Bug introduced by
The method getReference() does not exist on Doctrine\Persistence\ObjectManager. It seems like you code against a sub-type of said class. However, the method does not exist in Doctrine\Persistence\ObjectManagerDecorator. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

84
            return $objectManager->/** @scrutinizer ignore-call */ getReference($metadata->name, $data);
Loading history...
85
        }
86 8
87
        // Fallback to default constructor if missing identifier(s)
88
        $classMetadata = $objectManager->getClassMetadata($metadata->name);
89
        $identifierList = [];
90 8
91
        foreach ($classMetadata->getIdentifierFieldNames() as $name) {
92 8
            // Avoid calling objectManager->find if some identification properties are excluded
93 5
            if (!isset($metadata->propertyMetadata[$name])) {
94 5
                return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
95 1
            }
96 4
97 1
            $propertyMetadata = $metadata->propertyMetadata[$name];
98 3
99 2
            // Avoid calling objectManager->find if some identification properties are excluded by some exclusion strategy
100
            if ($this->isIdentifierFieldExcluded($propertyMetadata, $context)) {
101 1
                return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
102
            }
103
104
            if (is_array($data) && !array_key_exists($propertyMetadata->serializedName, $data)) {
105 3
                return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
106
            } elseif (is_object($data) && !property_exists($data, $propertyMetadata->serializedName)) {
107 3
                return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
108
            }
109
110
            $identifierList[$name] = $data[$propertyMetadata->serializedName];
111
        }
112
113
        if (empty($identifierList)) {
114
            // $classMetadataFactory->isTransient() fails on embeddable class with file metadata driver
115
            // https://github.com/doctrine/persistence/issues/37
116
            return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
117
        }
118
119
        // Entity update, load it from database
120
        $object = $objectManager->find($metadata->name, $identifierList);
121
122
        if (null === $object) {
123
            switch ($this->fallbackStrategy) {
124
                case self::ON_MISSING_NULL:
125
                    return null;
126
127
                case self::ON_MISSING_EXCEPTION:
128
                    throw new ObjectConstructionException(sprintf('Entity %s can not be found', $metadata->name));
129
130
                case self::ON_MISSING_FALLBACK:
131
                    return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
132
133
                default:
134
                    throw new InvalidArgumentException('The provided fallback strategy for the object constructor is not valid');
135
            }
136
        }
137
138
        $objectManager->initializeObject($object);
139
140
        return $object;
141
    }
142
143
    private function isIdentifierFieldExcluded(PropertyMetadata $propertyMetadata, DeserializationContext $context): bool
144
    {
145
        $exclusionStrategy = $context->getExclusionStrategy();
146
        if (null !== $exclusionStrategy && $exclusionStrategy->shouldSkipProperty($propertyMetadata, $context)) {
147
            return true;
148
        }
149
150
        return null !== $this->expressionLanguageExclusionStrategy && $this->expressionLanguageExclusionStrategy->shouldSkipProperty($propertyMetadata, $context);
151
    }
152
}
153