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