DynamoDbModel   F
last analyzed

Complexity

Total Complexity 61

Size/Duplication

Total Lines 450
Duplicated Lines 0 %

Test Coverage

Coverage 78.91%

Importance

Changes 24
Bugs 0 Features 1
Metric Value
wmc 61
eloc 117
c 24
b 0
f 1
dl 0
loc 450
ccs 116
cts 147
cp 0.7891
rs 3.52

31 Methods

Rating   Name   Duplication   Size   Complexity  
B saveAsync() 0 33 9
A updateAsync() 0 3 1
A getKeyNames() 0 3 2
A deleteAsync() 0 20 4
A marshalValue() 0 3 1
A getMarshaler() 0 3 1
A __wakeup() 0 4 1
A refresh() 0 13 2
A newQuery() 0 9 2
A getClient() 0 3 1
A __sleep() 0 4 1
A unmarshalItem() 0 3 1
A getDynamoDbClientService() 0 3 1
A unsetDynamoDbClientService() 0 3 1
A setupDynamoDb() 0 4 1
A all() 0 5 1
A getKey() 0 3 1
A getDynamoDbIndexKeys() 0 3 1
A hasCompositeKey() 0 3 1
A getKeys() 0 15 3
A update() 0 3 1
A getKeyName() 0 3 1
A setDynamoDbIndexKeys() 0 3 1
A delete() 0 20 5
A __construct() 0 9 1
A create() 0 7 1
A newCollection() 0 3 1
A setId() 0 13 3
A marshalItem() 0 3 1
A setDynamoDbClientService() 0 3 1
B save() 0 33 9

How to fix   Complexity   

Complex Class

Complex classes like DynamoDbModel 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 DynamoDbModel, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace BaoPham\DynamoDb;
4
5
use Exception;
6
use DateTime;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Support\Arr;
9
10
/**
11
 * Class DynamoDbModel.
12
 */
13
abstract class DynamoDbModel extends Model
14
{
15
    /**
16
     * Always set this to false since DynamoDb does not support incremental Id.
17
     *
18
     * @var bool
19
     */
20
    public $incrementing = false;
21
22
    /**
23
     * @var \BaoPham\DynamoDb\DynamoDbClientInterface
24
     */
25
    protected static $dynamoDb;
26
27
    /**
28
     * @deprecated
29
     * @var \Aws\DynamoDb\Marshaler
30
     */
31
    protected $marshaler;
32
33
    /**
34
     * @deprecated
35
     * @var \BaoPham\DynamoDb\EmptyAttributeFilter
36
     */
37
    protected $attributeFilter;
38
39
    /**
40
     * Indexes.
41
     *   [
42
     *     '<simple_index_name>' => [
43
     *          'hash' => '<index_key>'
44
     *     ],
45
     *     '<composite_index_name>' => [
46
     *          'hash' => '<index_hash_key>',
47
     *          'range' => '<index_range_key>'
48
     *     ],
49
     *   ]
50
     *
51
     * @var array
52
     */
53
    protected $dynamoDbIndexKeys = [];
54
55
    /**
56
     * Array of your composite key.
57
     * ['<hash>', '<range>']
58
     *
59
     * @var array
60
     */
61
    protected $compositeKey = [];
62
63
    /**
64
     * Default Date format
65
     * ISO 8601 Compliant
66
     */
67
    protected $dateFormat = DateTime::ATOM;
68
69
70 131
    public function __construct(array $attributes = [])
71
    {
72 131
        $this->bootIfNotBooted();
73
74 131
        $this->syncOriginal();
75
76 131
        $this->fill($attributes);
77
78 131
        $this->setupDynamoDb();
79 131
    }
80
81
    /**
82
     * Get the DynamoDbClient service that is being used by the models.
83
     *
84
     * @return DynamoDbClientInterface
85
     */
86 3
    public static function getDynamoDbClientService()
87
    {
88 3
        return static::$dynamoDb;
89
    }
90
91
    /**
92
     * Set the DynamoDbClient used by models.
93
     *
94
     * @param DynamoDbClientInterface $dynamoDb
95
     *
96
     * @return void
97
     */
98 146
    public static function setDynamoDbClientService(DynamoDbClientInterface $dynamoDb)
99
    {
100 146
        static::$dynamoDb = $dynamoDb;
101 146
    }
102
103
    /**
104
     * Unset the DynamoDbClient service for models.
105
     *
106
     * @return void
107
     */
108 3
    public static function unsetDynamoDbClientService()
109
    {
110 3
        static::$dynamoDb = null;
111 3
    }
112
113 131
    protected function setupDynamoDb()
114
    {
115 131
        $this->marshaler = static::$dynamoDb->getMarshaler();
116 131
        $this->attributeFilter = static::$dynamoDb->getAttributeFilter();
117 131
    }
118
119 92
    public function newCollection(array $models = [], $index = null)
120
    {
121 92
        return new DynamoDbCollection($models, $index);
122
    }
123
124 8
    public function save(array $options = [])
125
    {
126 8
        $create = !$this->exists;
127
128 8
        if ($this->fireModelEvent('saving') === false) {
129
            return false;
130
        }
131
132 8
        if ($create && $this->fireModelEvent('creating')  === false) {
133
            return false;
134
        }
135
136 8
        if (!$create && $this->fireModelEvent('updating') === false) {
137
            return false;
138
        }
139
140 8
        if ($this->usesTimestamps()) {
141 8
            $this->updateTimestamps();
142
        }
143
144 8
        $saved = $this->newQuery()->save();
145
146 8
        if (!$saved) {
147
            return $saved;
148
        }
149
150 8
        $this->exists = true;
151 8
        $this->wasRecentlyCreated = $create;
152 8
        $this->fireModelEvent($create ? 'created' : 'updated', false);
153
154 8
        $this->finishSave($options);
155
156 8
        return $saved;
157
    }
158
159
    /**
160
     * Saves the model to DynamoDb asynchronously and returns a promise
161
     * @param array $options
162
     * @return bool|\GuzzleHttp\Promise\Promise
163
     */
164 6
    public function saveAsync(array $options = [])
165
    {
166 6
        $create = !$this->exists;
167
168 6
        if ($this->fireModelEvent('saving') === false) {
169
            return false;
170
        }
171
172 6
        if ($create && $this->fireModelEvent('creating')  === false) {
173
            return false;
174
        }
175
176 6
        if (!$create && $this->fireModelEvent('updating') === false) {
177
            return false;
178
        }
179
180 6
        if ($this->usesTimestamps()) {
181 6
            $this->updateTimestamps();
182
        }
183
184 6
        $savePromise = $this->newQuery()->saveAsync();
185
186
        $savePromise->then(function ($result) use ($create, $options) {
187 6
            if (Arr::get($result, '@metadata.statusCode') === 200) {
188 6
                $this->exists = true;
189 6
                $this->wasRecentlyCreated = $create;
190 6
                $this->fireModelEvent($create ? 'created' : 'updated', false);
191
192 6
                $this->finishSave($options);
193
            }
194 6
        });
195
196 6
        return $savePromise;
197
    }
198
199 3
    public function update(array $attributes = [], array $options = [])
200
    {
201 3
        return $this->fill($attributes)->save();
202
    }
203
204 2
    public function updateAsync(array $attributes = [], array $options = [])
205
    {
206 2
        return $this->fill($attributes)->saveAsync($options);
207
    }
208
209
    public static function create(array $attributes = [])
210
    {
211
        $model = new static;
212
213
        $model->fill($attributes)->save();
214
215
        return $model;
216
    }
217
218 2
    public function delete()
219
    {
220 2
        if (is_null($this->getKeyName())) {
0 ignored issues
show
introduced by
The condition is_null($this->getKeyName()) is always false.
Loading history...
221
            throw new Exception('No primary key defined on model.');
222
        }
223
224 2
        if ($this->exists) {
225 2
            if ($this->fireModelEvent('deleting') === false) {
226
                return false;
227
            }
228
229 2
            $this->exists = false;
230
231 2
            $success = $this->newQuery()->delete();
232
233 2
            if ($success) {
234 2
                $this->fireModelEvent('deleted', false);
235
            }
236
237 2
            return $success;
238
        }
239
    }
240
241 2
    public function deleteAsync()
242
    {
243 2
        if (is_null($this->getKeyName())) {
0 ignored issues
show
introduced by
The condition is_null($this->getKeyName()) is always false.
Loading history...
244
            throw new Exception('No primary key defined on model.');
245
        }
246
247 2
        if ($this->exists) {
248 2
            if ($this->fireModelEvent('deleting') === false) {
249
                return false;
250
            }
251
252 2
            $this->exists = false;
253
254 2
            $deletePromise = $this->newQuery()->deleteAsync();
255
256
            $deletePromise->then(function () {
257 2
                $this->fireModelEvent('deleted', false);
258 2
            });
259
260 2
            return $deletePromise;
261
        }
262
    }
263
264 7
    public static function all($columns = [])
265
    {
266 7
        $instance = new static;
267
268 7
        return $instance->newQuery()->get($columns);
269
    }
270
271 2
    public function refresh()
272
    {
273 2
        if (! $this->exists) {
274
            return $this;
275
        }
276
277 2
        $query = $this->newQuery();
278
279 2
        $refreshed = $query->find($this->getKeys());
280
281 2
        $this->setRawAttributes($refreshed->toArray());
282
283 2
        return $this;
284
    }
285
286
    /**
287
     * @return DynamoDbQueryBuilder
288
     */
289 131
    public function newQuery()
290
    {
291 131
        $builder = new DynamoDbQueryBuilder($this);
292
293 131
        foreach ($this->getGlobalScopes() as $identifier => $scope) {
294 7
            $builder->withGlobalScope($identifier, $scope);
295
        }
296
297 131
        return $builder;
298
    }
299
300 110
    public function hasCompositeKey()
301
    {
302 110
        return !empty($this->compositeKey);
303
    }
304
305
    /**
306
     * @deprecated
307
     * @param $item
308
     * @return array
309
     */
310
    public function marshalItem($item)
311
    {
312
        return $this->marshaler->marshalItem($item);
313
    }
314
315
    /**
316
     * @deprecated
317
     * @param $value
318
     * @return array
319
     */
320
    public function marshalValue($value)
321
    {
322
        return $this->marshaler->marshalValue($value);
323
    }
324
325
    /**
326
     * @deprecated
327
     * @param $item
328
     * @return array|\stdClass
329
     */
330
    public function unmarshalItem($item)
331
    {
332
        return $this->marshaler->unmarshalItem($item);
333
    }
334
335 36
    public function setId($id)
336
    {
337 36
        if (!is_array($id)) {
338 14
            $this->setAttribute($this->getKeyName(), $id);
339
340 14
            return $this;
341
        }
342
343 24
        foreach ($id as $keyName => $value) {
344 24
            $this->setAttribute($keyName, $value);
345
        }
346
347 24
        return $this;
348
    }
349
350
    /**
351
     * @return \Aws\DynamoDb\DynamoDbClient
352
     */
353 131
    public function getClient()
354
    {
355 131
        return static::$dynamoDb->getClient($this->getConnectionName());
356
    }
357
358
    /**
359
     * Get the value of the model's primary key.
360
     *
361
     * @return mixed
362
     */
363
    public function getKey()
364
    {
365
        return $this->getAttribute($this->getKeyName());
366
    }
367
368
    /**
369
     * Get the value of the model's primary / composite key.
370
     * Use this if you always want the key values in associative array form.
371
     *
372
     * @return array
373
     *
374
     * ['id' => 'foo']
375
     *
376
     * or
377
     *
378
     * ['id' => 'foo', 'id2' => 'bar']
379
     */
380 46
    public function getKeys()
381
    {
382 46
        if ($this->hasCompositeKey()) {
383 22
            $key = [];
384
385 22
            foreach ($this->compositeKey as $name) {
386 22
                $key[$name] = $this->getAttribute($name);
387
            }
388
389 22
            return $key;
390
        }
391
392 24
        $name = $this->getKeyName();
393
394 24
        return [$name => $this->getAttribute($name)];
395
    }
396
397
    /**
398
     * Get the primary key for the model.
399
     *
400
     * @return string
401
     */
402 28
    public function getKeyName()
403
    {
404 28
        return $this->primaryKey;
405
    }
406
407
    /**
408
     * Get the primary/composite key for the model.
409
     *
410
     * @return array
411
     */
412 104
    public function getKeyNames()
413
    {
414 104
        return $this->hasCompositeKey() ? $this->compositeKey : [$this->primaryKey];
415
    }
416
417
    /**
418
     * @return array
419
     */
420 70
    public function getDynamoDbIndexKeys()
421
    {
422 70
        return $this->dynamoDbIndexKeys;
423
    }
424
425
    /**
426
     * @param array $dynamoDbIndexKeys
427
     */
428
    public function setDynamoDbIndexKeys($dynamoDbIndexKeys)
429
    {
430
        $this->dynamoDbIndexKeys = $dynamoDbIndexKeys;
431
    }
432
433
    /**
434
     * @deprecated
435
     * @return \Aws\DynamoDb\Marshaler
436
     */
437
    public function getMarshaler()
438
    {
439
        return $this->marshaler;
440
    }
441
442
    /**
443
     * Remove non-serializable properties when serializing.
444
     *
445
     * @return array
446
     */
447 2
    public function __sleep()
448
    {
449 2
        return array_keys(
450 2
            Arr::except(get_object_vars($this), ['marshaler', 'attributeFilter'])
451
        );
452
    }
453
454
    /**
455
     * When a model is being unserialized, check if it needs to be booted and setup DynamoDB.
456
     *
457
     * @return void
458
     */
459 2
    public function __wakeup()
460
    {
461 2
        parent::__wakeup();
462 2
        $this->setupDynamoDb();
463 2
    }
464
}
465