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

TransferPersister::persistAssociation()   B

Complexity

Conditions 8
Paths 6

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 19
rs 7.7777
c 1
b 0
f 0
cc 8
eloc 9
nc 6
nop 4
1
<?php
2
3
namespace Vox\Webservice;
4
5
use Doctrine\Common\Collections\Collection;
6
use Metadata\MetadataFactoryInterface;
7
use ProxyManager\Proxy\AccessInterceptorValueHolderInterface;
8
use Traversable;
9
use Vox\Metadata\PropertyMetadata;
10
use Vox\Webservice\Mapping\BelongsTo;
11
use Vox\Webservice\Mapping\HasMany;
12
use Vox\Webservice\Mapping\HasOne;
13
use Vox\Webservice\Metadata\TransferMetadata;
14
use Zend\Code\Exception\RuntimeException;
15
16
/**
17
 * The transfer persister will do the work of persisting and assuring the objects are on the
18
 * correct state
19
 * 
20
 * @author Jhonatan Teixeira <[email protected]>
21
 */
22
class TransferPersister implements TransferPersisterInterface
23
{
24
    use MetadataTrait;
25
    
26
    /**
27
     * @var MetadataFactoryInterface
28
     */
29
    private $metadataFactory;
30
    
31
    /**
32
     * @var UnityOfWorkInterface
33
     */
34
    private $unityOfWork;
35
    
36
    /**
37
     * @var WebserviceClientInterface
38
     */
39
    private $webserviceClient;
40
    
41
    public function __construct(
42
        MetadataFactoryInterface $metadataFactory,
43
        UnityOfWorkInterface $unityOfWork,
44
        WebserviceClientInterface $webserviceClient
45
    ) {
46
        $this->metadataFactory  = $metadataFactory;
47
        $this->unityOfWork      = $unityOfWork;
48
        $this->webserviceClient = $webserviceClient;
49
    }
50
51
    public function save($object, $owner = null)
52
    {
53
        $transfer = $object;
54
        
55
        $metadata = $this->getClassMetadata($transfer);
56
        
57
        foreach ($metadata->associations as $name => $association) {
58
            $assocValue = $association->getValue($transfer);
59
            
60
            if (!$assocValue) {
61
                continue;
62
            }
63
            
64
            $this->persistAssociation($object, $assocValue, $metadata, $association);
65
        }
66
        
67
        $this->persistTransfer($object, $owner);
68
    }
69
    
70
    private function persistAssociation($object, $association, TransferMetadata $metadata, PropertyMetadata $property)
71
    {
72
        if (is_array($association) || $association instanceof Traversable || $association instanceof Collection) {
73
            foreach ($association as $transfer) {
74
                $this->persistAssociation($object, $transfer, $metadata, $property);
75
            }
76
77
            return;
78
        }
79
80
        if ($this->unityOfWork->isDetached($association)) {
81
            $this->unityOfWork->attach($association);
82
        }
83
84
        if (!$this->unityOfWork->isNew($association) && !$this->unityOfWork->isDirty($association)) {
85
            return;
86
        }
87
88
        $this->save($association, $object);
89
    }
90
91
    private function updateRelationshipsIds($object, $owner)
92
    {
93
        $objectMetadata = $this->getClassMetadata($object);
94
95
        if ($owner && $objectMetadata->id->isMultiId()) {
96
            $ownerMetadata = $this->getClassMetadata($owner);
97
98
            foreach ($ownerMetadata->associations as $association) {
99
                if ($association->type == $objectMetadata->name) {
100
                    $this->updateMultiBelongsToIds($owner, $object);
101
                }
102
            }
103
        }
104
105
        foreach ($objectMetadata->associations as $associationMetadata) {
106
            $association = $associationMetadata->getValue($object);
107
108
            if ($association && $associationMetadata->hasAnnotation(BelongsTo::class)) {
109
                $this->updateBelongsToId($object, $association, $objectMetadata, $associationMetadata);
110
111
                return;
112
            }
113
114
            if ($association){
115
                if ($associationMetadata->hasAnnotation(HasMany::class)) {
116
                    $annotation = $associationMetadata->getAnnotation(HasMany::class);
117
                } elseif ($associationMetadata->hasAnnotation(HasOne::class)) {
118
                    $annotation = $associationMetadata->getAnnotation(HasOne::class);
119
                } else {
120
                    throw new RuntimeException('inavlid relationship declaration');
121
                }
122
123
                $this->updateHasIds($object, $association, $associationMetadata, $annotation);
124
            }
125
        }
126
    }
127
128
    private function updateBelongsToId($object, $association, TransferMetadata $metadata, PropertyMetadata $property)
129
    {
130
        /* @var $belongsTo BelongsTo */
131
        $belongsTo       = $property->getAnnotation(BelongsTo::class);
132
        $foreignProperty = $metadata->propertyMetadata[$belongsTo->foreignField];
133
        $foreignId       = $foreignProperty->getValue($object);
134
        $currentId       = $this->getIdValue($association);
135
136
        if ($foreignId !== $currentId) {
137
            $foreignProperty->setValue($object, $currentId);
138
        }
139
    }
140
141
    private function updateMultiBelongsToIds($object, $association)
142
    {
143
        $id             = $this->getIdValue($association);
144
        $objectMetadata = $this->getClassMetadata($object);
145
146
        if (empty($id)) {
147
            foreach ($this->getClassMetadata($association)->id->getIds() as $idProperty) {
148
                $idProperty->setValue(
149
                    $association,
150
                    $objectMetadata->propertyMetadata[$idProperty->name]->getValue($object)
151
                );
152
            }
153
        } else {
154
            foreach ($this->getClassMetadata($association)->id->getIds() as $idProperty) {
155
                $objectMetadata->propertyMetadata[$idProperty->name]
156
                    ->setValue($object, $idProperty->getValue($association));
157
            }
158
        }
159
    }
160
    
161
    private function updateHasIds($object, $association, PropertyMetadata $property, $annotation)
162
    {
163
        $type                  = preg_replace('/\[\]$/', '', $property->type);
164
        $relationClassMetadata = $this->metadataFactory->getMetadataForClass($type);
165
        $foreignProperty       = $relationClassMetadata->propertyMetadata[$annotation->foreignField];
0 ignored issues
show
Bug introduced by
The property propertyMetadata does not seem to exist on Metadata\ClassHierarchyMetadata.
Loading history...
166
167
        if ($association instanceof Traversable || is_array($association)) {
168
            foreach ($association as $relationItem) {
169
                $this->setIdValueOnAssociation($object, $relationItem, $foreignProperty);
170
            }
171
172
            return;
173
        }
174
175
        $this->setIdValueOnAssociation($object, $association, $foreignProperty);
176
    }
177
178
    private function setIdValueOnAssociation($object, $association, PropertyMetadata $foreignProperty)
179
    {
180
        $assocId  = $foreignProperty->getValue($association);
181
        $objectId = $this->getIdValue($object);
182
183
        if ($assocId !== $objectId) {
184
            $foreignProperty->setValue($association, $objectId);
185
        }
186
    }
187
    
188
    private function persistTransfer($object, $owner = null)
189
    {
190
        if ($this->unityOfWork->isNew($object)) {
191
            $this->updateRelationshipsIds($object, $owner);
192
            $this->webserviceClient->post($object);
193
            $this->renewState($object);
194
            
195
            return;
196
        }
197
198
        if ($this->unityOfWork->isRemoved($object)) {
199
            $this->webserviceClient->delete(get_class($object), $this->getIdValue($object));
200
            $this->unityOfWork->detach($object);
201
202
            return;
203
        }
204
205
        if ($this->unityOfWork->isDirty($object)) {
206
            $this->updateRelationshipsIds($object, $owner);
207
            $this->webserviceClient->put($object);
208
            $this->renewState($object);
209
        }
210
    }
211
    
212
    private function renewState($object)
213
    {
214
        $this->unityOfWork->detach($object);
215
        $this->unityOfWork->attach($object);
216
    }
217
}
218