TransferPersister   B
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 253
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 40
dl 0
loc 253
ccs 120
cts 120
cp 1
rs 8.2608
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
B persistAssociation() 0 19 8
A __construct() 0 12 1
A save() 0 17 3
A updateBelogsToId() 0 16 2
B persistTransfer() 0 34 6
A updateHasOneId() 0 13 2
A updateMultiBelongsToIds() 0 16 4
C updateRelationshipsIds() 0 37 8
B updateHasManyIds() 0 22 5
A renewState() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like TransferPersister often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TransferPersister, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Vox\Webservice;
4
5
use Doctrine\Common\Collections\Collection;
6
use Metadata\MetadataFactoryInterface;
7
use Traversable;
8
use Vox\Metadata\PropertyMetadata;
9
use Vox\Webservice\Event\DispatchEventTrait;
10
use Vox\Webservice\Event\LifecycleEvent;
11
use Vox\Webservice\Event\PersistenceEvents;
12
use Vox\Webservice\Mapping\BelongsTo;
13
use Vox\Webservice\Mapping\HasMany;
14
use Vox\Webservice\Mapping\HasOne;
15
use Vox\Webservice\Mapping\Resource;
16
use Vox\Webservice\Metadata\TransferMetadata;
17
18
/**
19
 * The transfer persister will do the work of persisting and assuring the objects are on the
20
 * correct state
21
 * 
22
 * @author Jhonatan Teixeira <[email protected]>
23
 */
24
class TransferPersister implements TransferPersisterInterface
25
{
26
    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\TransferPersister.
Loading history...
27
        DispatchEventTrait;
28
    
29
    /**
30
     * @var MetadataFactoryInterface
31
     */
32
    private $metadataFactory;
33
    
34
    /**
35
     * @var UnityOfWorkInterface
0 ignored issues
show
Bug introduced by
The type Vox\Webservice\UnityOfWorkInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36
     */
37
    private $unitOfWork;
38
    
39
    /**
40
     * @var WebserviceClientInterface
41
     */
42
    private $webserviceClient;
43
    
44
    /**
45
     * @var EventDispatcherInterface
46
     */
47
    private $eventDispatcher;
48
    
49
    /**
50
     * @var TransferManagerInterface
51
     */
52
    private $transferManager;
53
    
54 40
    public function __construct(
55
        MetadataFactoryInterface $metadataFactory,
56
        UnitOfWorkInterface $unitOfWork,
57
        WebserviceClientInterface $webserviceClient,
58
        TransferManagerInterface $transferManager = null,
59
        EventDispatcherInterface $eventDispatcher = null
60
    ) {
61 40
        $this->metadataFactory  = $metadataFactory;
62 40
        $this->unitOfWork       = $unitOfWork;
0 ignored issues
show
Documentation Bug introduced by
It seems like $unitOfWork of type Vox\Webservice\UnitOfWorkInterface is incompatible with the declared type Vox\Webservice\UnityOfWorkInterface of property $unitOfWork.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
63 40
        $this->webserviceClient = $webserviceClient;
64 40
        $this->transferManager  = $transferManager;
65 40
        $this->eventDispatcher  = $eventDispatcher;
66 40
    }
67
68 17
    public function save($object, $owner = null)
69
    {
70 17
        $transfer = $object;
71
        
72 17
        $metadata = $this->getClassMetadata($transfer);
73
        
74 17
        foreach ($metadata->associations as $name => $association) {
75 8
            $assocValue = $association->getValue($transfer);
76
            
77 8
            if (!$assocValue) {
78 7
                continue;
79
            }
80
            
81 7
            $this->persistAssociation($object, $assocValue, $metadata, $association);
82
        }
83
        
84 17
        $this->persistTransfer($object, $owner);
85 17
    }
86
    
87 7
    private function persistAssociation($object, $association, TransferMetadata $metadata, PropertyMetadata $property)
88
    {
89 7
        if (is_array($association) || $association instanceof Traversable || $association instanceof Collection) {
90 3
            foreach ($association as $transfer) {
91 3
                $this->persistAssociation($object, $transfer, $metadata, $property);
92
            }
93
94 3
            return;
95
        }
96
97 7
        if ($this->unitOfWork->isDetached($association)) {
98 4
            $this->unitOfWork->attach($association);
99
        }
100
101 7
        if (!$this->unitOfWork->isNew($association) && !$this->unitOfWork->isDirty($association)) {
102 4
            return;
103
        }
104
105 5
        $this->save($association, $object);
106 5
    }
107
108 17
    private function persistTransfer($object, $owner = null)
109
    {
110 17
        $event = $this->eventDispatcher ? new LifecycleEvent($object, $this->transferManager) : null;
111
        
112
        try {
113 17
            if ($this->unitOfWork->isNew($object)) {
114 10
                $this->updateRelationshipsIds($object, $owner);
115 10
                $this->webserviceClient->post($object);
116 10
                $this->renewState($object);
117 10
                $this->dispatchEvent(PersistenceEvents::POST_PERSIST, $event);
118
119 10
                return;
120
            }
121
122 15
            if ($this->unitOfWork->isRemoved($object)) {
123 4
                $this->dispatchEvent(PersistenceEvents::PRE_REMOVE, $event);
124 4
                $this->webserviceClient->delete(get_class($object), $this->getIdValue($object));
125 4
                $this->dispatchEvent(PersistenceEvents::POST_REMOVE, $event);
126 4
                $this->unitOfWork->detach($object);
127
128 4
                return;
129
            }
130
131 13
            if ($this->unitOfWork->isDirty($object)) {
132 11
                $this->updateRelationshipsIds($object, $owner);
133 11
                $this->dispatchEvent(PersistenceEvents::PRE_UPDATE, $event);
134 11
                $this->webserviceClient->put($object);
135 11
                $this->renewState($object);
136 13
                $this->dispatchEvent(PersistenceEvents::POST_UPDATE, $event);
137
            }
138 1
        } catch (\Throwable $ex) {
139 1
            $this->dispatchEvent(PersistenceEvents::ON_EXCEPTION, $event);
140
            
141 1
            throw $ex;
142
        }
143 13
    }
144
145 16
    private function updateRelationshipsIds($object, $owner = null)
146
    {
147 16
        if ($owner) {
148 5
            $this->updateRelationshipsIds($owner);
149
        }
150
151 16
        $objectMetadata = $this->getClassMetadata($object);
152
153 16
        foreach ($objectMetadata->associations as $associationProperty) {
154 8
            $association = $associationProperty->getValue($object);
155
156 8
            if (!$association) {
157 6
                continue;
158
            }
159
160 7
            $associationMetadata = $this->getClassMetadata($association);
161
162 7
            if ($associationProperty->hasAnnotation(BelongsTo::class)) {
163 6
                if ($associationMetadata->id->isMultiId()) {
164 1
                    $this->updateMultiBelongsToIds($object, $association);
165
                } else {
166 6
                    $this->updateBelogsToId($object, $association, $associationProperty);
167
                }
168
169 6
                continue;
170
            }
171
172 3
            if ($associationProperty->hasAnnotation(HasOne::class)) {
173 1
                $this->updateHasOneId($object, $association, $associationProperty->getAnnotation(HasOne::class));
174
175 1
                continue;
176
            }
177
178 3
            if ($associationProperty->hasAnnotation(HasMany::class)) {
179 3
                $this->updateHasManyIds($object, $association, $associationProperty->getAnnotation(HasMany::class));
180
181 3
                continue;
182
            }
183
        }
184 16
    }
185
186 1
    private function updateMultiBelongsToIds($object, $association)
187
    {
188 1
        $id             = $this->getIdValue($association);
189 1
        $objectMetadata = $this->getClassMetadata($object);
190
191 1
        if (empty($id)) {
192 1
            foreach ($this->getClassMetadata($association)->id->getIds() as $idProperty) {
193 1
                $idProperty->setValue(
194 1
                    $association,
195 1
                    $objectMetadata->propertyMetadata[$idProperty->name]->getValue($object)
196
                );
197
            }
198
        } else {
199 1
            foreach ($this->getClassMetadata($association)->id->getIds() as $idProperty) {
200 1
                $objectMetadata->propertyMetadata[$idProperty->name]
201 1
                    ->setValue($object, $idProperty->getValue($association));
202
            }
203
        }
204 1
    }
205
206 6
    private function updateBelogsToId($object, $association, PropertyMetadata $associationProperty)
207
    {
208
        /* @var $belongsTo BelongsTo */
209 6
        $belongsTo = $associationProperty->getAnnotation(BelongsTo::class);
210
211 6
        $objectMetadata = $this->getClassMetadata($object);
212
213 6
        $foreignPropertyMetadata = $objectMetadata->propertyMetadata[$belongsTo->foreignField];
214 6
        $idValue                 = $this->getIdValue($association);
215
        
216 6
        if ($foreignPropertyMetadata->type == 'string') {
217 1
            $resource = $this->getClassMetadata($association)->getAnnotation(Resource::class);
218 1
            $idValue = sprintf('%s/%s', $resource->route, $idValue);
219
        }
220
        
221 6
        $foreignPropertyMetadata->setValue($object, $idValue);
222 6
    }
223
224
    /**
225
     * @param $object
226
     * @param $association
227
     * @param HasOne|HasMany $annotation
228
     */
229 3
    private function updateHasOneId($object, $association, $annotation)
230
    {
231 3
        $id                  = $this->getIdValue($object);
232 3
        $associationMetadata = $this->getClassMetadata($association);
233
234 3
        $foreignPropertyMetadata = $associationMetadata->propertyMetadata[$annotation->foreignField];
235
        
236 3
        if ($foreignPropertyMetadata->type == 'string') {
237 1
            $resource = $this->getClassMetadata($association)->getAnnotation(Resource::class);
238 1
            $id = sprintf('%s/%s', $resource->route, $id);
239
        }
240
        
241 3
        $foreignPropertyMetadata->setValue($association, $id);
242 3
    }
243
244
    /**
245
     * @param $object
246
     * @param Traversable $associations
247
     * @param HasOne|HasMany $annotation
248
     */
249 3
    private function updateHasManyIds($object, Traversable $associations, $annotation)
250
    {
251 3
        if (!empty($annotation->foreignField)) {
252 3
            foreach ($associations as $association) {
253 3
                $this->updateHasOneId($object, $association, $annotation);
254
            }
255
        }
256
        
257 3
        if (!$annotation->iriCollectionField) {
0 ignored issues
show
Bug introduced by
The property iriCollectionField does not seem to exist on Vox\Webservice\Mapping\HasOne.
Loading history...
258 2
            return;
259
        }
260
        
261 1
        $iris = [];
262
        
263 1
        $objectMetadata = $this->getClassMetadata($object);
264
        
265 1
        foreach ($associations as $association) {
266 1
            $resource = $this->getClassMetadata($association)->getAnnotation(Resource::class);
267 1
            $iris[] = sprintf('%s/%s', $resource->route, $this->getIdValue($association));
268
        }
269
        
270 1
        $objectMetadata->propertyMetadata[$annotation->iriCollectionField]->setValue($object, $iris);
271 1
    }
272
273 16
    private function renewState($object)
274
    {
275 16
        $this->unitOfWork->detach($object);
276 16
        $this->unitOfWork->attach($object);
277 16
    }
278
}
279