Test Failed
Branch refactoring (b0eef5)
by JHONATAN
03:33
created

UnityOfWork::fetchByParams()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 7
nc 4
nop 1
1
<?php
2
3
namespace Vox\Webservice;
4
5
use BadMethodCallException;
6
use Metadata\MetadataFactoryInterface;
7
use SplObjectStorage;
8
use Vox\Metadata\PropertyMetadata;
9
use Vox\Webservice\Mapping\BelongsTo;
10
use Vox\Webservice\Mapping\HasMany;
11
use Vox\Webservice\Mapping\HasOne;
12
use Vox\Webservice\Metadata\TransferMetadata;
13
14
/**
15
 * the unit of work keeps track of the transfers current state, works as a sort of memento pattern
16
 * its really important part of the persistence proccess
17
 * 
18
 * @author Jhonatan Teixeira <[email protected]>
19
 */
20
class UnityOfWork implements UnityOfWorkInterface
21
{
22
    use MetadataTrait;
23
    
24
    /**
25
     * @var SplObjectStorage
26
     */
27
    private $managed;
28
    
29
    /**
30
     * @var SplObjectStorage
31
     */
32
    private $removedObjects;
33
34
    public function __construct(MetadataFactoryInterface $metadataFactory)
35
    {
36
        $this->metadataFactory = $metadataFactory;
37
        $this->managed         = new SplObjectStorage();
38
        $this->removedObjects  = new SplObjectStorage();
39
    }
40
41
    public function getIterator()
42
    {
43
        return new \ArrayIterator(iterator_to_array($this->managed));
44
    }
45
46
    public function contains($object): bool
47
    {
48
        return $this->managed->contains($object);
49
    }
50
51
    public function attach($object)
52
    {
53
        $this->managed->attach($object, clone $object);
54
    }
55
56
    public function detach($object)
57
    {
58
        $this->managed->detach($object);
59
60
        if ($this->removedObjects->contains($object)) {
61
            $this->removedObjects->detach($object);
62
        }
63
    }
64
65
    public function isEquals($object): bool
66
    {
67
        $metadata     = $this->getClassMetadata($object);
68
        $storedObject = $this->managed[$object];
69
70
        /* @var $propertyMetadata PropertyMetadata */
71
        foreach ($metadata->propertyMetadata as $name => $propertyMetadata) {
72
            $storedValue = $propertyMetadata->getValue($storedObject);
73
            $value       = $propertyMetadata->getValue($object);
74
75
            if ($propertyMetadata->hasAnnotation(BelongsTo::class)) {
76
                if (!empty($value)
77
                    && $this->hasRelationshipChanged($object, $value, $propertyMetadata, $metadata)) {
78
                    return false;
79
                }
80
81
                continue;
82
            }
83
84
            if ($propertyMetadata->hasAnnotation(HasOne::class) || $propertyMetadata->hasAnnotation(HasMany::class)) {
85
                continue;
86
            }
87
88
            if ($storedValue != $value) {
89
                return false;
90
            }
91
        }
92
93
        return true;
94
    }
95
96
    private function hasRelationshipChanged(
97
        $object,
98
        $related,
99
        PropertyMetadata $propertyMetadata,
100
        TransferMetadata $metadata
101
    ): bool {
102
        /* @var $belongsTo BelongsTo */
103
        $belongsTo  = $propertyMetadata->getAnnotation(BelongsTo::class);
104
105
        if (is_array($belongsTo->foreignField)) {
106
            return $this->hasMultiFieldsRelationshipChanged($object, $related, $belongsTo->foreignField);
107
        }
108
109
        $externalId = $this->getIdValue($related);
110
        $internalId = $metadata->propertyMetadata[$belongsTo->foreignField]->getValue($object);
111
112
        return $externalId !== $internalId;
113
    }
114
115
    private function hasMultiFieldsRelationshipChanged(
116
        $object,
117
        $related,
118
        array $fields
119
    ): bool {
120
        $objectValues  = [];
121
        $relatedValues = [];
122
123
        $objectMetadata  = $this->getClassMetadata($object);
124
        $relatedMetadata = $this->getClassMetadata($related);
125
126
        foreach ($fields as $field) {
127
            $objectValues[$field]  = $objectMetadata->propertyMetadata[$field]->getValue($object);
128
            $relatedValues[$field] = $relatedMetadata->propertyMetadata[$field]->getValue($related);
129
        }
130
131
        $diff = array_diff_assoc($objectValues, $relatedValues);
132
        return count($diff) > 0;
133
    }
134
135
    public function fetchByParams(...$params)
136
    {
137
        if (count($params) != 2) {
138
            throw new BadMethodCallException('this method needs two params, $className: string, $id: scalar');
139
        }
140
141
        $className = (string) $params[0];
142
        $id        = $params[1];
143
144
        foreach ($this->managed as $managed) {
145
            if (get_class($managed) == $className && $id == $this->getIdValue($managed)) {
146
                return $managed;
147
            }
148
        }
149
    }
150
151
    public function remove($object)
152
    {
153
        if (!$this->managed->contains($object)) {
154
            throw new \RuntimeException('only managed object can be scheduled to deletion');
155
        }
156
157
        $this->removedObjects->attach($object);
158
    }
159
160
    public function isNew($object): bool
161
    {
162
        $id = $this->getIdValue($this->managed[$object]);
163
164
        return empty($id);
165
    }
166
167
    public function isDirty($object): bool
168
    {
169
        return !$this->isNew($object) && !$this->isEquals($object);
170
    }
171
172
    public function isRemoved($object): bool
173
    {
174
        return $this->removedObjects->contains($object);
175
    }
176
177
    public function isDetached($object): bool
178
    {
179
        return !$this->managed->contains($object);
180
    }
181
}
182