Completed
Push — master ( 589f5d...2e0cd6 )
by Pavel
07:02
created

DoctrineObjectNormalizer::setAttributeValue()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 29
rs 8.439
cc 5
eloc 16
nc 5
nop 5
1
<?php
2
3
namespace ScayTrase\Api\Cruds\Adaptors\DoctrineOrm;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\Common\Persistence\ManagerRegistry;
7
use Doctrine\Common\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
8
use Doctrine\Common\Persistence\ObjectManager;
9
use Doctrine\ORM\EntityManagerInterface;
10
use Doctrine\ORM\Mapping\ClassMetadata;
11
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
12
13
class DoctrineObjectNormalizer extends ObjectNormalizer
14
{
15
    /** @var ManagerRegistry */
16
    private $registry;
17
18
    public function setRegistry(ManagerRegistry $registry)
19
    {
20
        $this->registry = $registry;
21
    }
22
23
    public function supportsNormalization($data, $format = null)
24
    {
25
        return is_object($data) && null !== $this->registry->getManagerForClass(get_class($data));
26
    }
27
28
    protected function getAttributeValue($object, $attribute, $format = null, array $context = [])
29
    {
30
        $rawValue = parent::getAttributeValue($object, $attribute, $format, $context);
31
32
        if (null === $rawValue) {
33
            return null;
34
        }
35
36
        $metadata = $this->getObjectMetadata($object, $attribute);
37
        if (null === $metadata) {
38
            return $rawValue;
39
        }
40
41
        $assocMetadata = $this->getAssocMetadata($metadata, $attribute);
42
43
        if ($metadata->isSingleValuedAssociation($attribute)) {
44
            return $this->getAssociationValue($assocMetadata, $rawValue);
45
        }
46
47
        $result = [];
48
        foreach ($rawValue as $item) {
49
            $result[] = $this->getAssociationValue($assocMetadata, $item);
50
        }
51
52
        return $result;
53
    }
54
55
    protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = [])
56
    {
57
        if (null === $value) {
58
            return parent::setAttributeValue($object, $attribute, $value, $format, $context);
59
        }
60
61
        $rawValue = $value;
62
63
        $metadata = $this->getObjectMetadata($object, $attribute);
64
        if (null === $metadata) {
65
            return $rawValue;
66
        }
67
68
        $assocMetadata = $this->getAssocMetadata($metadata, $attribute);
69
70
        $manager = $this->registry->getManagerForClass($assocMetadata->getName());
71
        if ($metadata->isSingleValuedAssociation($attribute)) {
72
            $value = $this->getObjectByValue($manager, $assocMetadata, $rawValue);
0 ignored issues
show
Bug introduced by
It seems like $manager defined by $this->registry->getMana...socMetadata->getName()) on line 70 can be null; however, ScayTrase\Api\Cruds\Adap...zer::getObjectByValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
73
74
            return parent::setAttributeValue($object, $attribute, $value, $format, $context);
75
        }
76
77
        $objects = [];
78
        foreach ($rawValue as $item) {
79
            $objects[] = $this->getObjectByValue($manager, $assocMetadata, $item);
0 ignored issues
show
Bug introduced by
It seems like $manager defined by $this->registry->getMana...socMetadata->getName()) on line 70 can be null; however, ScayTrase\Api\Cruds\Adap...zer::getObjectByValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
80
        }
81
82
        return parent::setAttributeValue($object, $attribute, new ArrayCollection($objects), $format, $context);
83
    }
84
85
    /**
86
     * @param ObjectManager          $manager
87
     * @param ClassMetadataInterface $assocMetadata
88
     * @param                        $rawValue
89
     *
90
     * @return object
91
     */
92
    protected function getObjectByValue(ObjectManager $manager, ClassMetadataInterface $assocMetadata, $rawValue)
93
    {
94
        if ($manager instanceof EntityManagerInterface) {
95
            return $manager->getReference($assocMetadata->getName(), $rawValue);
96
        }
97
98
        return $manager->find($assocMetadata->getName(), $rawValue);
99
    }
100
101
    /**
102
     * @param $assocMetadata
103
     * @param $rawValue
104
     *
105
     * @return mixed
106
     */
107
    private function getAssociationValue(ClassMetadataInterface $assocMetadata, $rawValue)
108
    {
109
        $identifier = $assocMetadata->getIdentifierValues($rawValue);
110
111
        if (!$assocMetadata instanceof ClassMetadata || $assocMetadata->isIdentifierComposite) {
112
            return $identifier;
113
        }
114
115
        return array_shift($identifier);
116
    }
117
118
    private function getObjectMetadata($object, $attribute)
119
    {
120
        $class = get_class($object);
121
122
        $manager = $this->registry->getManagerForClass($class);
123
        if (null === $manager) {
124
            return null;
125
        }
126
127
        $metadata = $manager->getClassMetadata($class);
128
        if (!$metadata->hasAssociation($attribute)) {
129
            return null;
130
        }
131
132
        return $metadata;
133
    }
134
135
    private function getAssocMetadata(ClassMetadataInterface $metadata, $attribute)
136
    {
137
        $assoc        = $metadata->getAssociationTargetClass($attribute);
138
        $assocManager = $this->registry->getManagerForClass($assoc);
139
140
        if (null === $assocManager) {
141
            throw new \LogicException($metadata->getName() .
142
                                      '::$' .
143
                                      $attribute .
144
                                      ' references non-existent managed entity');
145
        }
146
147
        return $assocManager->getClassMetadata($assoc);
148
    }
149
}
150