Skip to content

Commit 360ee2d

Browse files
authored
Merge pull request #150 from sahilsharma011/EloquentAsync
Eloquent save, update and delete method async execution using promises
2 parents d10e423 + c30fdf4 commit 360ee2d

File tree

5 files changed

+316
-3
lines changed

5 files changed

+316
-3
lines changed

README.md

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ Supports all key types - primary hash key and composite keys.
2020
* [Conditions](#conditions)
2121
* [all() and first()](#all-and-first)
2222
* [Pagination](#pagination)
23-
* [Update](#update)
24-
* [Save](#save)
23+
* [Update](#update) / [updateAsync()](#updateasync)
24+
* [Save](#save) / [saveAsync()](#saveasync)
25+
* [Delete](#delete) / [deleteAsync()](#deleteasync)
2526
* [Chunk](#chunk)
2627
* [limit() and take()](#limit-and-take)
2728
* [firstOrFail()](#firstorfail)
@@ -93,6 +94,7 @@ Usage
9394
* Extends your model with `BaoPham\DynamoDb\DynamoDbModel`, then you can use Eloquent methods that are supported. The idea here is that you can switch back to Eloquent without changing your queries.
9495
* Or if you want to sync your DB table with a DynamoDb table, use trait `BaoPham\DynamoDb\ModelTrait`, it will call a `PutItem` after the model is saved.
9596
* Alternatively, you can use the [query builder](#query-builder) facade to build more complex queries.
97+
* AWS SDK v3 for PHP uses guzzlehttp promises to allow for asynchronous workflows. Using this package you can run eloquent queries like [delete](#find-and-delete), [update](#updateasync), [save](#saveasync) asynchronously on DynamoDb.
9698

9799
### Supported features:
98100

@@ -101,6 +103,7 @@ Usage
101103
```php
102104
$model->find(<id>);
103105
$model->delete();
106+
$model->deleteAsync()->wait();
104107
```
105108

106109
#### Conditions
@@ -198,6 +201,13 @@ $nextPage = $query->afterKey($last->getKeys())->limit(2)->all();
198201
$model->update($attributes);
199202
```
200203
204+
#### updateasync()
205+
206+
```php
207+
// update asynchronously and wait on the promise for completion.
208+
$model->updateAsync($attributes)->wait();
209+
```
210+
201211
#### save()
202212
203213
```php
@@ -206,10 +216,50 @@ $model = new Model();
206216
$model->fillableAttr1 = 'foo';
207217
$model->fillableAttr2 = 'foo';
208218
// DynamoDb doesn't support incremented Id, so you need to use UUID for the primary key.
209-
$model->id = 'de305d54-75b4-431b-adb2-eb6b9e546014'
219+
$model->id = 'de305d54-75b4-431b-adb2-eb6b9e546014';
210220
$model->save();
211221
```
212222
223+
#### saveasync()
224+
**Examples**
225+
Saving single model asynchronously and waiting on the promise for completion.
226+
```php
227+
$model = new Model();
228+
// Define fillable attributes in your Model class.
229+
$model->fillableAttr1 = 'foo';
230+
$model->fillableAttr2 = 'bar';
231+
// DynamoDb doesn't support incremented Id, so you need to use UUID for the primary key.
232+
$model->id = 'de305d54-75b4-431b-adb2-eb6b9e546014';
233+
$model->saveAsync()->wait();
234+
```
235+
Saving multiple models asynchronously and waiting on all of them simultaneously.
236+
```php
237+
for($i = 0; $i < 10; $i++){
238+
$model = new Model();
239+
// Define fillable attributes in your Model class.
240+
$model->fillableAttr1 = 'foo';
241+
$model->fillableAttr2 = 'bar';
242+
// DynamoDb doesn't support incremented Id, so you need to use UUID for the primary key.
243+
$model->id = uniqid();
244+
// Returns a promise which you can wait on later.
245+
$promises[] = $model->saveAsync();
246+
}
247+
248+
\GuzzleHttp\Promise\all($promises)->wait();
249+
```
250+
251+
#### delete()
252+
253+
```php
254+
$model->delete();
255+
```
256+
257+
#### deleteasync()
258+
259+
```php
260+
$model->deleteAsync()->wait();
261+
```
262+
213263
#### chunk()
214264
215265
```php

src/DynamoDbModel.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,56 @@ public function save(array $options = [])
155155
return $saved;
156156
}
157157

158+
/**
159+
* Saves the model to DynamoDb asynchronously and returns a promise
160+
* @param array $options
161+
* @return bool|\GuzzleHttp\Promise\Promise
162+
*/
163+
public function saveAsync(array $options = [])
164+
{
165+
$create = !$this->exists;
166+
167+
if ($this->fireModelEvent('saving') === false) {
168+
return false;
169+
}
170+
171+
if ($create && $this->fireModelEvent('creating') === false) {
172+
return false;
173+
}
174+
175+
if (!$create && $this->fireModelEvent('updating') === false) {
176+
return false;
177+
}
178+
179+
if ($this->usesTimestamps()) {
180+
$this->updateTimestamps();
181+
}
182+
183+
$savePromise = $this->newQuery()->saveAsync();
184+
185+
$savePromise->then(function ($result) use ($create, $options) {
186+
if (array_get($result, '@metadata.statusCode') === 200) {
187+
$this->exists = true;
188+
$this->wasRecentlyCreated = $create;
189+
$this->fireModelEvent($create ? 'created' : 'updated', false);
190+
191+
$this->finishSave($options);
192+
}
193+
});
194+
195+
return $savePromise;
196+
}
197+
158198
public function update(array $attributes = [], array $options = [])
159199
{
160200
return $this->fill($attributes)->save();
161201
}
162202

203+
public function updateAsync(array $attributes = [], array $options = [])
204+
{
205+
return $this->fill($attributes)->saveAsync();
206+
}
207+
163208
public static function create(array $attributes = [])
164209
{
165210
$model = new static;
@@ -192,6 +237,29 @@ public function delete()
192237
}
193238
}
194239

240+
public function deleteAsync()
241+
{
242+
if (is_null($this->getKeyName())) {
243+
throw new Exception('No primary key defined on model.');
244+
}
245+
246+
if ($this->exists) {
247+
if ($this->fireModelEvent('deleting') === false) {
248+
return false;
249+
}
250+
251+
$this->exists = false;
252+
253+
$deletePromise = $this->newQuery()->deleteAsync();
254+
255+
$deletePromise->then(function () {
256+
$this->fireModelEvent('deleted', false);
257+
});
258+
259+
return $deletePromise;
260+
}
261+
}
262+
195263
public static function all($columns = [])
196264
{
197265
$instance = new static;

src/DynamoDbQueryBuilder.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,16 @@ public function delete()
606606
return array_get($result->toArray(), '@metadata.statusCode') === 200;
607607
}
608608

609+
public function deleteAsync()
610+
{
611+
$promise = DynamoDb::table($this->model->getTable())
612+
->setKey(DynamoDb::marshalItem($this->model->getKeys()))
613+
->prepare($this->client)
614+
->deleteItemAsync();
615+
616+
return $promise;
617+
}
618+
609619
public function save()
610620
{
611621
$result = DynamoDb::table($this->model->getTable())
@@ -616,6 +626,16 @@ public function save()
616626
return array_get($result, '@metadata.statusCode') === 200;
617627
}
618628

629+
public function saveAsync()
630+
{
631+
$promise = DynamoDb::table($this->model->getTable())
632+
->setItem(DynamoDb::marshalItem($this->model->getAttributes()))
633+
->prepare($this->client)
634+
->putItemAsync();
635+
636+
return $promise;
637+
}
638+
619639
public function get($columns = [])
620640
{
621641
return $this->all($columns);

tests/DynamoDbCompositeModelTest.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,29 @@ public function testCreateRecord()
4242
$this->assertEquals($this->testModel->id2, $record['Item']['id2']['S']);
4343
}
4444

45+
public function testCreateAsyncRecord()
46+
{
47+
$this->testModel->id = 'id1';
48+
$this->testModel->id2 = str_random(36);
49+
$this->testModel->name = 'Test Create Async';
50+
$this->testModel->count = 1;
51+
$this->testModel->saveAsync()->wait();
52+
53+
$query = [
54+
'TableName' => $this->testModel->getTable(),
55+
'Key' => [
56+
'id' => ['S' => $this->testModel->id],
57+
'id2' => ['S' => $this->testModel->id2],
58+
],
59+
];
60+
61+
$record = $this->getClient()->getItem($query)->toArray();
62+
63+
$this->assertArrayHasKey('Item', $record);
64+
$this->assertEquals($this->testModel->id, $record['Item']['id']['S']);
65+
$this->assertEquals($this->testModel->id2, $record['Item']['id2']['S']);
66+
}
67+
4568
public function testFindRecord()
4669
{
4770
$seed = $this->seed();
@@ -179,6 +202,29 @@ public function testUpdateRecord()
179202
$this->assertEquals($newName, array_get($record, 'Item.name.S'));
180203
}
181204

205+
public function testUpdateAsyncRecord()
206+
{
207+
$seed = $this->seed();
208+
$seedId = array_get($seed, 'id.S');
209+
$seedId2 = array_get($seed, 'id2.S');
210+
211+
$newName = 'New Name';
212+
$this->testModel = $this->testModel->find(['id' => $seedId, 'id2' => $seedId2]);
213+
$this->testModel->updateAsync(['name' => $newName])->wait();
214+
215+
$query = [
216+
'TableName' => $this->testModel->getTable(),
217+
'Key' => [
218+
'id' => ['S' => $seedId],
219+
'id2' => ['S' => $seedId2],
220+
],
221+
];
222+
223+
$record = $this->getClient()->getItem($query)->toArray();
224+
225+
$this->assertEquals($newName, array_get($record, 'Item.name.S'));
226+
}
227+
182228
public function testSaveRecord()
183229
{
184230
$seed = $this->seed();
@@ -204,6 +250,31 @@ public function testSaveRecord()
204250
$this->assertEquals($newName, array_get($record, 'Item.name.S'));
205251
}
206252

253+
public function testSaveAsyncRecord()
254+
{
255+
$seed = $this->seed();
256+
$seedId = array_get($seed, 'id.S');
257+
$seedId2 = array_get($seed, 'id2.S');
258+
259+
$newName = 'New Name to be saved asynchronously';
260+
$this->testModel = $this->testModel->find(['id' => $seedId, 'id2' => $seedId2]);
261+
$this->testModel->name = $newName;
262+
263+
$this->testModel->saveAsync()->wait();
264+
265+
$query = [
266+
'TableName' => $this->testModel->getTable(),
267+
'Key' => [
268+
'id' => ['S' => $seedId],
269+
'id2' => ['S' => $seedId2]
270+
],
271+
];
272+
273+
$record = $this->getClient()->getItem($query)->toArray();
274+
275+
$this->assertEquals($newName, array_get($record, 'Item.name.S'));
276+
}
277+
207278
public function testDeleteRecord()
208279
{
209280
$seed = $this->seed();
@@ -225,6 +296,27 @@ public function testDeleteRecord()
225296
$this->assertArrayNotHasKey('Item', $record);
226297
}
227298

299+
public function testDeleteAsyncRecord()
300+
{
301+
$seed = $this->seed();
302+
$seedId = array_get($seed, 'id.S');
303+
$seedId2 = array_get($seed, 'id2.S');
304+
305+
$this->testModel->find(['id' => $seedId, 'id2' => $seedId2])->deleteAsync()->wait();
306+
307+
$query = [
308+
'TableName' => $this->testModel->getTable(),
309+
'Key' => [
310+
'id' => ['S' => $seedId],
311+
'id2' => ['S' => $seedId2]
312+
],
313+
];
314+
315+
$record = $this->getClient()->getItem($query)->toArray();
316+
317+
$this->assertArrayNotHasKey('Item', $record);
318+
}
319+
228320
public function testLookUpByKey()
229321
{
230322
$this->seed();

0 commit comments

Comments
 (0)