Completed
Push — master ( c0ae32...7019ae )
by JHONATAN
02:56
created

ObjectStorage::attach()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 2.0185
1
<?php
2
3
namespace Vox\Webservice;
4
5
use InvalidArgumentException;
6
use Metadata\MetadataFactoryInterface;
7
use BadMethodCallException;
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
 * A object storage using an id hashmap pattern
16
 * 
17
 * @author Jhonatan Teixeira <[email protected]>
18
 */
19
class ObjectStorage implements ObjectStorageInterface
20
{
21
    use MetadataTrait;
22
    
23
    private $storage = [];
24
    
25 25
    public function __construct(MetadataFactoryInterface $metadataFactory)
26
    {
27 25
        $this->metadataFactory = $metadataFactory;
28 25
    }
29
    
30 14
    public function contains($object): bool
31
    {
32 14
        return isset($this->storage[get_class($object)][$this->getIdValue($object)]);
33
    }
34
    
35 13
    public function attach($object)
36
    {
37 13
        $className = get_class($object);
38 13
        $id        = $this->getIdValue($object);
39
        
40 13
        if (!$id) {
41
            throw new InvalidArgumentException('object has no id value');
42
        }
43
        
44 13
        $this->storage[$className][$id] = $object;
45 13
    }
46
    
47 9
    public function detach($object)
48
    {
49 9
        unset($this->storage[get_class($object)][$this->getIdValue($object)]);
50 9
    }
51
52 11
    public function getIterator()
53
    {
54 11
        $items = [];
55
        
56 11
        foreach ($this->storage as $transferData) {
57 9
            foreach ($transferData as $item) {
58 9
                $items[] = $item;
59
            }
60
        }
61
        
62 11
        return new \ArrayIterator($items);
63
    }
64
65 8
    public function isEquals($object): bool
66
    {
67 8
        $className    = get_class($object);
68 8
        $metadata     = $this->getClassMetadata($object);
69 8
        $idValue      = $this->getIdValue($object);
70 8
        $storedObject = $this->storage[$className][$idValue];
71
72
        /* @var $propertyMetadata PropertyMetadata */
73 8
        foreach ($metadata->propertyMetadata as $name => $propertyMetadata) {
74 8
            $storedValue = $propertyMetadata->getValue($storedObject);
75 8
            $value       = $propertyMetadata->getValue($object);
76
77 8
            if ($propertyMetadata->hasAnnotation(BelongsTo::class)) {
78 2
                if (!empty($value)
79 2
                    && $this->hasRelationshipChanged($object, $value, $propertyMetadata, $metadata)) {
80 2
                    return false;
81
                }
82
83 2
                continue;
84
            }
85
86 8
            if ($propertyMetadata->hasAnnotation(HasOne::class) || $propertyMetadata->hasAnnotation(HasMany::class)) {
87 2
                continue;
88
            }
89
90 8
            if ($storedValue != $value) {
91 8
                return false;
92
            }
93
        }
94
95 2
        return true;
96
    }
97
98 2
    private function hasRelationshipChanged(
99
        $object,
100
        $related,
101
        PropertyMetadata $propertyMetadata,
102
        TransferMetadata $metadata
103
    ): bool {
104
        /* @var $belongsTo BelongsTo */
105 2
        $belongsTo  = $propertyMetadata->getAnnotation(BelongsTo::class);
106
107 2
        if (is_array($belongsTo->foreignField)) {
108 1
            return $this->hasMultiFieldsRelationshipChanged($object, $related, $belongsTo->foreignField);
109
        }
110
111 2
        $externalId = $this->getIdValue($related);
112 2
        $internalId = $metadata->propertyMetadata[$belongsTo->foreignField]->getValue($object);
113
114 2
        if ($externalId !== $internalId) {
115 2
            $metadata->propertyMetadata[$belongsTo->foreignField]->setValue($object, $externalId);
116 2
            return true;
117
        }
118
119 2
        return false;
120
    }
121
122 1
    private function hasMultiFieldsRelationshipChanged(
123
        $object,
124
        $related,
125
        array $fields
126
    ): bool {
127 1
        $objectValues  = [];
128 1
        $relatedValues = [];
129
130 1
        $objectMetadata  = $this->getClassMetadata($object);
131 1
        $relatedMetadata = $this->getClassMetadata($related);
132
133 1
        foreach ($fields as $field) {
134 1
            $objectValues[$field]  = $objectMetadata->propertyMetadata[$field]->getValue($object);
135 1
            $relatedValues[$field] = $relatedMetadata->propertyMetadata[$field]->getValue($related);
136
        }
137
138 1
        $has = count(array_diff_assoc($objectValues, $relatedValues)) > 0;
139
140 1
        if ($has) {
141 1
            foreach ($relatedValues as $field => $value) {
142 1
                $objectMetadata->propertyMetadata[$field]->setValue($object, $value);
143
            }
144
        }
145
146 1
        return $has;
147
    }
148
149
    /**
150
     * @param string $className
151
     * @param scalar $id
152
     *
153
     * @return object
154
     */
155 7
    public function fetchByParams(...$params)
156
    {
157 7
        if (count($params) != 2) {
158
            throw new BadMethodCallException('this method needs two params, $className: string, $id: scalar');
159
        }
160
161 7
        $className = (string) $params[0];
162 7
        $id        = $params[1];
163
164 7
        if (!is_scalar($id)) {
165
            throw new BadMethodCallException('$id must be scalar value');
166
        }
167
168 7
        return $this->storage[$className][$id] ?? null;
169
    }
170
}
171