Completed
Push — master ( 6751ec...bd2805 )
by JHONATAN
03:37
created

UnityOfWork::detach()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 1
crap 2
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 27
    public function __construct(MetadataFactoryInterface $metadataFactory)
35
    {
36 27
        $this->metadataFactory = $metadataFactory;
37 27
        $this->managed         = new SplObjectStorage();
38 27
        $this->removedObjects  = new SplObjectStorage();
39 27
    }
40
41 13
    public function getIterator()
42
    {
43 13
        return new \ArrayIterator(iterator_to_array($this->managed));
44
    }
45
46 12
    public function contains($object): bool
47
    {
48 12
        return $this->managed->contains($object);
49
    }
50
51 16
    public function attach($object)
52
    {
53 16
        $this->managed->attach($object, clone $object);
54 16
    }
55
56 13
    public function detach($object)
57
    {
58 13
        $this->managed->detach($object);
59
60 13
        if ($this->removedObjects->contains($object)) {
61 2
            $this->removedObjects->detach($object);
62
        }
63 13
    }
64
65 9
    public function isEquals($object): bool
66
    {
67 9
        $metadata     = $this->getClassMetadata($object);
68 9
        $storedObject = $this->managed[$object];
69
70
        /* @var $propertyMetadata PropertyMetadata */
71 9
        foreach ($metadata->propertyMetadata as $name => $propertyMetadata) {
72 9
            $storedValue = $propertyMetadata->getValue($storedObject);
73 9
            $value       = $propertyMetadata->getValue($object);
74
75 9
            if ($propertyMetadata->hasAnnotation(BelongsTo::class)) {
76 3
                if (!empty($value)
77 3
                    && $this->hasRelationshipChanged($object, $value, $propertyMetadata, $metadata)) {
78 2
                    return false;
79
                }
80
81 3
                continue;
82
            }
83
84 9
            if ($propertyMetadata->hasAnnotation(HasOne::class) || $propertyMetadata->hasAnnotation(HasMany::class)) {
85 3
                continue;
86
            }
87
88 9
            if ($storedValue != $value) {
89 9
                return false;
90
            }
91
        }
92
93 3
        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
        return $externalId !== $internalId;
113
    }
114
115 1
    private function hasMultiFieldsRelationshipChanged(
116
        $object,
117
        $related,
118
        array $fields
119
    ): bool {
120 1
        $objectValues  = [];
121 1
        $relatedValues = [];
122
123 1
        $objectMetadata  = $this->getClassMetadata($object);
124 1
        $relatedMetadata = $this->getClassMetadata($related);
125
126 1
        foreach ($fields as $field) {
127 1
            $objectValues[$field]  = $objectMetadata->propertyMetadata[$field]->getValue($object);
128 1
            $relatedValues[$field] = $relatedMetadata->propertyMetadata[$field]->getValue($related);
129
        }
130
131 1
        $diff = array_diff_assoc($objectValues, $relatedValues);
132 1
        return count($diff) > 0;
133
    }
134
135 8
    public function fetchByParams(...$params)
136
    {
137 8
        if (count($params) != 2) {
138
            throw new BadMethodCallException('this method needs two params, $className: string, $id: scalar');
139
        }
140
141 8
        $className = (string) $params[0];
142 8
        $id        = $params[1];
143
144 8
        foreach ($this->managed as $managed) {
145 4
            if (get_class($managed) == $className && $id == $this->getIdValue($managed)) {
146 4
                return $managed;
147
            }
148
        }
149 8
    }
150
151 3
    public function remove($object)
152
    {
153 3
        if (!$this->managed->contains($object)) {
154
            throw new \RuntimeException('only managed object can be scheduled to deletion');
155
        }
156
157 3
        $this->removedObjects->attach($object);
158 3
    }
159
160 13
    public function isNew($object): bool
161
    {
162 13
        $id = $this->getIdValue($this->managed[$object]);
163
164 13
        return empty($id);
165
    }
166
167 9
    public function isDirty($object): bool
168
    {
169 9
        return !$this->isNew($object) && !$this->isEquals($object);
170
    }
171
172 11
    public function isRemoved($object): bool
173
    {
174 11
        return $this->removedObjects->contains($object);
175
    }
176
177 5
    public function isDetached($object): bool
178
    {
179 5
        return !$this->managed->contains($object);
180
    }
181
}
182