Completed
Pull Request — master (#181)
by Vincent
03:38
created

EntityHydrator   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 28
c 4
b 1
f 0
lcom 1
cbo 2
dl 0
loc 146
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
B hydrate() 0 32 5
A completeRequired() 0 4 1
C completeFields() 0 52 11
A complete() 0 14 4
A format() 0 9 2
A formatFromMapping() 0 19 4
1
<?php
2
3
namespace Knp\FriendlyContexts\Doctrine;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\Common\Persistence\ObjectManager;
7
use Doctrine\DBAL\Types\Type as DBALType;
8
use Doctrine\ORM\Mapping\ClassMetadata;
9
use Knp\FriendlyContexts\Guesser\GuesserManager;
10
use Knp\FriendlyContexts\Utils\TextFormater;
11
use Knp\FriendlyContexts\Utils\UniqueCache;
12
use Symfony\Component\PropertyAccess\PropertyAccess;
13
14
class EntityHydrator
15
{
16
    public function __construct(TextFormater $formater, GuesserManager $guesserManager, EntityResolver $resolver, UniqueCache $cache)
17
    {
18
        $this->formater       = $formater;
0 ignored issues
show
Bug introduced by
The property formater does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
19
        $this->guesserManager = $guesserManager;
0 ignored issues
show
Bug introduced by
The property guesserManager does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
20
        $this->resolver       = $resolver;
0 ignored issues
show
Bug introduced by
The property resolver does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
21
        $this->cache          = $cache;
0 ignored issues
show
Bug introduced by
The property cache does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
22
    }
23
24
    public function hydrate(ObjectManager $em, $entity, $values)
25
    {
26
        foreach ($values as $property => $value) {
27
            if (false !== $mapping = $this->resolver->getMetadataFromProperty($em, $entity, $property)) {
28
                $this->formatFromMapping($mapping, $property, $value);
29
            }
30
31
            try {
32
                PropertyAccess::getPropertyAccessor()
0 ignored issues
show
Bug introduced by
The method getPropertyAccessor() does not seem to exist on object<Symfony\Component...yAccess\PropertyAccess>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
33
                    ->setValue(
34
                        $entity,
35
                        $this->formater->toCamelCase($property),
36
                        $value
37
                    )
38
                ;
39
            } catch (\Exception $e) {
40
                if (!($value instanceof ArrayCollection)) {
41
                    throw $e;
42
                }
43
44
                PropertyAccess::getPropertyAccessor()
0 ignored issues
show
Bug introduced by
The method getPropertyAccessor() does not seem to exist on object<Symfony\Component...yAccess\PropertyAccess>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
45
                    ->setValue(
46
                        $entity,
47
                        $this->formater->toCamelCase($property),
48
                        $value->toArray()
49
                    )
50
                ;
51
            }
52
        }
53
54
        return $this;
55
    }
56
57
    public function completeRequired(ObjectManager $em, $entity)
58
    {
59
        $this->completeFields($em, $entity);
60
    }
61
62
    public function completeFields(ObjectManager $em, $entity)
63
    {
64
        $accessor = PropertyAccess::getPropertyAccessor();
0 ignored issues
show
Bug introduced by
The method getPropertyAccessor() does not seem to exist on object<Symfony\Component...yAccess\PropertyAccess>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
65
66
        $metadata = $this->resolver->getMetadataFromObject($em, $entity);
67
68
        foreach ($metadata->getColumnNames() as $columnName) {
69
            $property = $metadata->getFieldName($columnName);
70
            if (false === $metadata->isNullable($property)) {
71
                try {
72
                    if (null === $accessor->getValue($entity, $property)) {
73
                        $accessor->setValue(
74
                            $entity,
75
                            $property,
76
                            $this->complete($metadata->getFieldMapping($property), $metadata->getName())
77
                        );
78
                    }
79
                } catch (\Exception $ex) {
80
                    unset($ex);
81
                }
82
            }
83
        }
84
85
        // Parse associations
86
        foreach ($metadata->getAssociationNames() as $associationName) {
87
            $mapping = $metadata->getAssociationMapping($associationName);
88
            // Ignore if association is a collection (ManyToMany, OneToMany), nullable, or already has a value
89
            if ($metadata->isCollectionValuedAssociation($associationName) ||
90
                !isset($mapping['joinColumns'][0]['nullable']) ||
91
                $mapping['joinColumns'][0]['nullable'] === true ||
92
                $accessor->getValue($entity, $associationName) !== null
93
            ) {
94
                continue;
95
            }
96
            try {
97
                // Create association object
98
                $relatedClass = $metadata->getAssociationTargetClass($associationName);
99
                $property = new $relatedClass;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
100
                // Complete required fields
101
                $this->completeRequired($em, $property);
102
                // Persist association object (prevent cascade persist forgetfulness)
103
                $em->persist($property);
104
105
                // Set entity association value
106
                $accessor->setValue($entity, $associationName, $property);
107
            } catch (\Exception $ex) {
108
                unset($ex);
109
            }
110
        }
111
112
        return $this;
113
    }
114
115
    protected function complete($mapping, $className)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
116
    {
117
        if (false === $guesser = $this->guesserManager->find($mapping)) {
118
            throw new \Exception(sprintf('There is no fake solution for "%s" typed fields', $mapping['type']));
119
        }
120
121
        if (isset($mapping['unique']) && true === $mapping['unique']) {
122
            return $this->cache->generate($className, $mapping['fieldName'], function () use ($guesser, $mapping) {
123
                return $guesser->fake($mapping);
124
            });
125
        }
126
127
        return $guesser->fake($mapping);
128
    }
129
130
    protected function format($mapping, $value)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
131
    {
132
        if (false === $guesser = $this->guesserManager->find($mapping)) {
133
134
            return $value;
135
        }
136
137
        return $guesser->transform($value, $mapping);
138
    }
139
140
    protected function formatFromMapping($mapping, &$property, &$value)
141
    {
142
        $property = $mapping['fieldName'];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 11 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
143
        $collectionRelation = in_array($mapping['type'], [ClassMetadata::ONE_TO_MANY, ClassMetadata::MANY_TO_MANY]);
144
        $arrayRelation = in_array($mapping['type'], [DBALType::TARRAY, DBALType::SIMPLE_ARRAY, DBALType::JSON_ARRAY]);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 6 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
145
146
        if ($collectionRelation || $arrayRelation) {
147
            $result = array_map(
148
                function ($e) use ($mapping) {
149
                    return $this->format($mapping, $e);
150
                },
151
                    $this->formater->listToArray($value)
152
                );
153
154
            $value = $collectionRelation ? new ArrayCollection($result) : $result;
155
        } else {
156
            $value = $this->format($mapping, $value);
157
        }
158
    }
159
}
160