UnitOfWork   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 170
Duplicated Lines 0 %

Test Coverage

Coverage 94.87%

Importance

Changes 0
Metric Value
wmc 32
dl 0
loc 170
ccs 74
cts 78
cp 0.9487
rs 9.6
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A contains() 0 3 1
A remove() 0 7 2
A isRemoved() 0 3 1
A __construct() 0 5 1
A isDetached() 0 3 1
B fetchByParams() 0 12 5
A hasMultiFieldsRelationshipChanged() 0 18 2
A attach() 0 3 1
A isDirty() 0 3 2
A isNew() 0 5 1
C isEquals() 0 29 8
A detach() 0 6 2
A hasRelationshipChanged() 0 22 3
A getIterator() 0 3 1
A getOriginalObject() 0 3 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 UnitOfWork implements UnitOfWorkInterface
21
{
22
    use MetadataTrait;
0 ignored issues
show
Bug introduced by
The trait Vox\Webservice\MetadataTrait requires the property $id which is not provided by Vox\Webservice\UnitOfWork.
Loading history...
23
    
24
    /**
25
     * @var SplObjectStorage
26
     */
27
    private $managed;
28
    
29
    /**
30
     * @var SplObjectStorage
31
     */
32
    private $removedObjects;
33
34 40
    public function __construct(MetadataFactoryInterface $metadataFactory)
35
    {
36 40
        $this->metadataFactory = $metadataFactory;
37 40
        $this->managed         = new SplObjectStorage();
38 40
        $this->removedObjects  = new SplObjectStorage();
39 40
    }
40
41 17
    public function getIterator()
42
    {
43 17
        return new \ArrayIterator(iterator_to_array($this->managed));
44
    }
45
46 20
    public function contains($object): bool
47
    {
48 20
        return $this->managed->contains($object);
49
    }
50
51 24
    public function attach($object)
52
    {
53 24
        $this->managed->attach($object, clone $object);
54 24
    }
55
56 17
    public function detach($object)
57
    {
58 17
        $this->managed->detach($object);
59
60 17
        if ($this->removedObjects->contains($object)) {
61 4
            $this->removedObjects->detach($object);
62
        }
63 17
    }
64
65 13
    public function isEquals($object): bool
66
    {
67 13
        $metadata     = $this->getClassMetadata($object);
68 13
        $storedObject = $this->managed[$object];
69
        
70
        /* @var $propertyMetadata PropertyMetadata */
71 13
        foreach ($metadata->propertyMetadata as $name => $propertyMetadata) {
72 13
            $storedValue = $propertyMetadata->getValue($storedObject);
73 13
            $value       = $propertyMetadata->getValue($object);
74
75 13
            if ($propertyMetadata->hasAnnotation(BelongsTo::class)) {
76 5
                if (!empty($value)
77 5
                    && $this->hasRelationshipChanged($object, $value, $propertyMetadata, $metadata)) {
78 2
                    return false;
79
                }
80
81 5
                continue;
82
            }
83
84 13
            if ($propertyMetadata->hasAnnotation(HasOne::class) || $propertyMetadata->hasAnnotation(HasMany::class)) {
85 4
                continue;
86
            }
87
88 13
            if ($storedValue != $value) {
89 13
                return false;
90
            }
91
        }
92
93 5
        return true;
94
    }
95
96 2
    private function hasRelationshipChanged(
97
        $object,
98
        $related,
99
        PropertyMetadata $propertyMetadata,
100
        TransferMetadata $metadata
101
    ): bool {
102
        /* @var $belongsTo BelongsTo */
103 2
        $belongsTo  = $propertyMetadata->getAnnotation(BelongsTo::class);
104
105 2
        if (is_array($belongsTo->foreignField)) {
106 1
            return $this->hasMultiFieldsRelationshipChanged($object, $related, $belongsTo->foreignField);
107
        }
108
109 2
        $externalId = $this->getIdValue($related);
110 2
        $internalId = $metadata->propertyMetadata[$belongsTo->foreignField]->getValue($object);
111
        
112 2
        if (!is_integer($internalId)) {
113
            preg_match('/[^\/]+$/', $internalId, $matches);
114
            $internalId = $matches[0] ?? null;
115
        }
116
117 2
        return $externalId != $internalId;
118
    }
119
120 1
    private function hasMultiFieldsRelationshipChanged(
121
        $object,
122
        $related,
123
        array $fields
124
    ): bool {
125 1
        $objectValues  = [];
126 1
        $relatedValues = [];
127
128 1
        $objectMetadata  = $this->getClassMetadata($object);
129 1
        $relatedMetadata = $this->getClassMetadata($related);
130
131 1
        foreach ($fields as $field) {
132 1
            $objectValues[$field]  = $objectMetadata->propertyMetadata[$field]->getValue($object);
133 1
            $relatedValues[$field] = $relatedMetadata->propertyMetadata[$field]->getValue($related);
134
        }
135
136 1
        $diff = array_diff_assoc($objectValues, $relatedValues);
137 1
        return count($diff) > 0;
138
    }
139
140 15
    public function fetchByParams(...$params)
141
    {
142 15
        if (count($params) != 2) {
143
            throw new BadMethodCallException('this method needs two params, $className: string, $id: scalar');
144
        }
145
146 15
        $className = (string) $params[0];
147 15
        $id        = $params[1];
148
149 15
        foreach ($this->managed as $managed) {
150 10
            if ($managed instanceof $className && $id == $this->getIdValue($managed)) {
151 10
                return $managed;
152
            }
153
        }
154 15
    }
155
156 5
    public function remove($object)
157
    {
158 5
        if (!$this->managed->contains($object)) {
159
            throw new \RuntimeException('only managed object can be scheduled to deletion');
160
        }
161
162 5
        $this->removedObjects->attach($object);
163 5
    }
164
165 17
    public function isNew($object): bool
166
    {
167 17
        $id = $this->getIdValue($this->managed[$object]);
168
169 17
        return empty($id);
170
    }
171
172 13
    public function isDirty($object): bool
173
    {
174 13
        return !$this->isNew($object) && !$this->isEquals($object);
175
    }
176
177 15
    public function isRemoved($object): bool
178
    {
179 15
        return $this->removedObjects->contains($object);
180
    }
181
182 7
    public function isDetached($object): bool
183
    {
184 7
        return !$this->managed->contains($object);
185
    }
186
    
187 1
    public function getOriginalObject($object)
188
    {
189 1
        return $this->managed[$object];
190
    }
191
}
192