Completed
Push — 5.1 ( af1a7b...f5b0b1 )
by Rémi
03:26
created

Store::updateDirtyRelated()   C

Complexity

Conditions 14
Paths 18

Size

Total Lines 44
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 2 Features 1
Metric Value
c 7
b 2
f 1
dl 0
loc 44
rs 5.0864
cc 14
eloc 25
nc 18
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Analogue\ORM\Commands;
4
5
use Analogue\ORM\Mappable;
6
use Analogue\ORM\EntityCollection;
7
use Analogue\ORM\System\Aggregate;
8
use Analogue\ORM\System\Proxies\EntityProxy;
9
use Analogue\ORM\System\Proxies\CollectionProxy;
10
11
/**
12
 * Persist entities & relationships to the
13
 * database.
14
 */
15
class Store extends Command
16
{
17
    /**
18
     * Persist the entity in the database
19
     *
20
     * @throws \InvalidArgumentException
21
     * @return false|mixed
22
     */
23
    public function execute()
24
    {
25
        $entity = $this->aggregate->getEntityObject();
26
27
        $mapper = $this->aggregate->getMapper();
28
29
        if ($mapper->fireEvent('storing', $entity) === false) {
30
            return false;
31
        }
32
33
        $this->preStoreProcess();
34
35
        /**
36
         * We will test the entity for existence
37
         * and run a creation if it doesn't exists
38
         */
39
        if (!$this->aggregate->exists()) {
40
            if ($mapper->fireEvent('creating', $entity) === false) {
41
                return false;
42
            }
43
44
            $this->insert();
45
46
            $mapper->fireEvent('created', $entity, false);
47
        }
48
        
49
        /**
50
         * We'll only run an update if the entity
51
         * is actually dirty
52
         */
53
        if ($this->aggregate->isDirty()) {
54
            if ($mapper->fireEvent('updating', $entity) === false) {
55
                return false;
56
            }
57
            $this->update();
58
59
            $mapper->fireEvent('updated', $entity, false);
60
        }
61
62
        $this->postStoreProcess();
63
64
        $mapper->fireEvent('stored', $entity, false);
65
66
        return $entity;
67
    }
68
69
    /**
70
     * Run all operations that have to occur before actually
71
     * storing the entity
72
     *
73
     * @throws \InvalidArgumentException
74
     * @return void
75
     */
76
    protected function preStoreProcess()
77
    {
78
        // Create any related object that doesn't exist in the database.
79
        $localRelationships = $this->aggregate->getEntityMap()->getLocalRelationships();
80
81
        $this->createRelatedEntities($localRelationships);
82
83
        // Now we can sync the related collections
84
        $this->aggregate->syncRelationships($localRelationships);
85
    }
86
87
    /**
88
     * Check for existence and create non-existing related entities
89
     *
90
     * @param  array
91
     * @throws \InvalidArgumentException
92
     * @return void
93
     */
94
    protected function createRelatedEntities($relations)
95
    {
96
        $entitiesToCreate = $this->aggregate->getNonExistingRelated($relations);
97
98
        foreach ($entitiesToCreate as $aggregate) {
99
            $this->createStoreCommand($aggregate)->execute();
100
        }
101
    }
102
103
    /**
104
     * Create a new store command
105
     *
106
     * @param  Aggregate $aggregate
107
     * @return Store
108
     */
109
    protected function createStoreCommand(Aggregate $aggregate)
110
    {
111
        // We gotta retrieve the corresponding query adapter to use.
112
        $mapper = $aggregate->getMapper();
113
114
        return new Store($aggregate, $mapper->newQueryBuilder());
115
    }
116
117
    /**
118
     * Run all operations that have to occur after the entity
119
     * is stored.
120
     *
121
     * @throws \InvalidArgumentException
122
     * @return void
123
     */
124
    protected function postStoreProcess()
125
    {
126
        $aggregate = $this->aggregate;
127
128
        // Create any related object that doesn't exist in the database.
129
        $foreignRelationships = $aggregate->getEntityMap()->getForeignRelationships();
130
        $this->createRelatedEntities($foreignRelationships);
131
132
        // Update any pivot tables that has been modified.
133
        $aggregate->updatePivotRecords();
134
135
        // Update any dirty relationship. This include relationships that already exists, have
136
        // dirty attributes / newly created related entities / dirty related entities.
137
        $dirtyRelatedAggregates = $aggregate->getDirtyRelationships();
138
139
        foreach ($dirtyRelatedAggregates as $related) {
140
            $this->createStoreCommand($related)->execute();
141
        }
142
143
        // Now we can sync the related collections
144
        if ($this->aggregate->exists()) {
145
            $this->aggregate->syncRelationships($foreignRelationships);
146
        }
147
        
148
        // TODO be move it to the wrapper class
149
        // so it's the same code for the entity builder
150
        $aggregate->setProxies();
151
152
        // Update Entity Cache
153
        $aggregate->getMapper()->getEntityCache()->refresh($aggregate);
154
    }
155
156
    /**
157
     * Update Related Entities which attributes have
158
     * been modified.
159
     *
160
     * @return void
161
     */
162
    protected function updateDirtyRelated()
163
    {
164
        $relations = $this->entityMap->getRelationships();
0 ignored issues
show
Bug introduced by
The property entityMap does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
165
        $attributes = $this->getAttributes();
0 ignored issues
show
Bug introduced by
The method getAttributes() does not seem to exist on object<Analogue\ORM\Commands\Store>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
166
167
        foreach ($relations as $relation) {
168
            if (!array_key_exists($relation, $attributes)) {
169
                continue;
170
            }
171
172
            $value = $attributes[$relation];
173
174
            if ($value == null) {
175
                continue;
176
            }
177
178
            if ($value instanceof EntityProxy) {
179
                continue;
180
            }
181
182
            if ($value instanceof CollectionProxy && $value->isLoaded()) {
183
                $value = $value->getUnderlyingCollection();
184
            }
185
            if ($value instanceof CollectionProxy && !$value->isLoaded()) {
186
                foreach ($value->getAddedItems() as $entity) {
187
                    $this->updateEntityIfDirty($entity);
0 ignored issues
show
Bug introduced by
The method updateEntityIfDirty() does not exist on Analogue\ORM\Commands\Store. Did you maybe mean update()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
188
                }
189
                continue;
190
            }
191
192
            if ($value instanceof EntityCollection) {
193
                foreach ($value as $entity) {
194
                    if (!$this->createEntityIfNotExists($entity)) {
0 ignored issues
show
Bug introduced by
The method createEntityIfNotExists() does not seem to exist on object<Analogue\ORM\Commands\Store>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
195
                        $this->updateEntityIfDirty($entity);
0 ignored issues
show
Bug introduced by
The method updateEntityIfDirty() does not exist on Analogue\ORM\Commands\Store. Did you maybe mean update()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
196
                    }
197
                }
198
                continue;
199
            }
200
            if ($value instanceof Mappable) {
201
                $this->updateEntityIfDirty($value);
0 ignored issues
show
Bug introduced by
The method updateEntityIfDirty() does not exist on Analogue\ORM\Commands\Store. Did you maybe mean update()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
202
                continue;
203
            }
204
        }
205
    }
206
207
    /**
208
     * Execute an insert statement on the database
209
     *
210
     * @return void
211
     */
212
    protected function insert()
213
    {
214
        $aggregate = $this->aggregate;
215
216
        $attributes = $aggregate->getRawAttributes();
217
        
218
        $keyName = $aggregate->getEntityMap()->getKeyName();
219
220
        // Check if the primary key is defined in the attributes
221
        if (array_key_exists($keyName, $attributes) && $attributes[$keyName] != null) {
222
            $this->query->insert($attributes);
223
        } else {
224
            $sequence = $aggregate->getEntityMap()->getSequence();
225
226
            $id = $this->query->insertGetId($attributes, $sequence);
227
228
            $aggregate->setEntityAttribute($keyName, $id);
229
        }
230
    }
231
232
    /**
233
     * Run an update statement on the entity
234
     *
235
     * @throws \InvalidArgumentException
236
     *
237
     * @return void
238
     */
239
    protected function update()
240
    {
241
        $query = $this->query;
242
243
        $keyName = $this->aggregate->getEntityKey();
244
245
        $query = $query->where($keyName, '=', $this->aggregate->getEntityId());
246
247
        $dirtyAttributes = $this->aggregate->getDirtyRawAttributes();
248
249
        if (count($dirtyAttributes) > 0) {
250
            $query->update($dirtyAttributes);
251
        }
252
    }
253
}
254