Skip to content

Commit d3f0274

Browse files
authored
Merge pull request #10 from weMail/feature/increment-decrement
Support Atomic Counters
2 parents b5323a3 + 8c753b4 commit d3f0274

File tree

7 files changed

+181
-6
lines changed

7 files changed

+181
-6
lines changed

src/Kitar/Dynamodb/Model/Model.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,31 @@ public function save(array $options = [])
171171
return $saved;
172172
}
173173

174+
/**
175+
* @inheritdoc
176+
*/
177+
protected function incrementOrDecrement($column, $amount, $extra, $method)
178+
{
179+
if (! $this->exists) {
180+
return $this->newQuery()
181+
->key($this->getKey())
182+
->{$method}($column, $amount, $extra);
183+
}
184+
185+
if ($this->fireModelEvent('updating') === false) {
186+
return false;
187+
}
188+
189+
return tap($this->newQuery()
190+
->key($this->getKey())
191+
->{$method}($column, $amount, $extra), function ($response) use($column, $amount, $method) {
192+
$this->forceFill($response['Attributes']);
193+
$this->syncChanges();
194+
195+
$this->fireModelEvent('updated', false);
196+
});
197+
}
198+
174199
/**
175200
* Perform a model update operation.
176201
*
@@ -328,6 +353,10 @@ public function __call($method, $parameters)
328353
"keyConditionBetween",
329354
];
330355

356+
if (in_array($method, ['increment', 'decrement'])) {
357+
return $this->$method(...$parameters);
358+
}
359+
331360
if (! in_array($method, $allowedBuilderMethods)) {
332361
static::throwBadMethodCallException($method);
333362
}

src/Kitar/Dynamodb/Query/Builder.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ public function deleteItem($key)
274274
* Update item.
275275
*
276276
* @param array $item
277-
* @return \Aws\Result
277+
* @return array|null
278278
*/
279279
public function updateItem($item)
280280
{
@@ -292,7 +292,41 @@ public function updateItem($item)
292292
}
293293
}
294294

295-
return $this->process('updateItem', null);
295+
return $this->process('updateItem', 'processSingleItem');
296+
}
297+
298+
/**
299+
* @inheritdoc
300+
*/
301+
public function increment($column, $amount = 1, array $extra = [])
302+
{
303+
return $this->incrementOrDecrement($column, '+', $amount, $extra);
304+
}
305+
306+
/**
307+
* @inheritdoc
308+
*/
309+
public function decrement($column, $amount = 1, array $extra = [])
310+
{
311+
return $this->incrementOrDecrement($column, '-', $amount, $extra);
312+
}
313+
314+
/**
315+
* Increment or decrement column's value by a given amount.
316+
*
317+
* @param $column
318+
* @param $symbol
319+
* @param int $amount
320+
* @param array $extra
321+
* @return array|\Aws\Result|Aws\Result|Illuminate\Support\Collection
322+
*/
323+
protected function incrementOrDecrement($column, $symbol, $amount = 1, array $extra = [])
324+
{
325+
$name = $this->expression_attributes->addName($column);
326+
$value = $this->expression_attributes->addValue($amount);
327+
$this->updates['set'][] = "{$name} = {$name} {$symbol} {$value}";
328+
329+
return $this->updateItem($extra);
296330
}
297331

298332
/**

src/Kitar/Dynamodb/Query/Grammar.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ public function compileUpdates($updates)
130130
}
131131

132132
return [
133-
'UpdateExpression' => implode(' ', $expressions)
133+
'UpdateExpression' => implode(' ', $expressions),
134+
'ReturnValues' => 'UPDATED_NEW',
134135
];
135136
}
136137

src/Kitar/Dynamodb/Query/Processor.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ protected function unmarshal(Result $res)
2929
}
3030
}
3131

32+
if (! empty($responseArray['Attributes'])) {
33+
$responseArray['Attributes'] = $this->marshaler->unmarshalItem($responseArray['Attributes']);
34+
}
35+
3236
return $responseArray;
3337
}
3438

@@ -46,6 +50,10 @@ public function processSingleItem(Result $awsResponse, $modelClass = null)
4650
$item->setMeta($response ?? null);
4751
return $item;
4852
}
53+
54+
if (! empty($response['Attributes'])) {
55+
return $response;
56+
}
4957
}
5058

5159
public function processMultipleItems(Result $awsResponse, $modelClass = null)

tests/Model/AuthUserProviderTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ public function it_can_update_remember_token()
255255
]
256256
],
257257
'UpdateExpression' => 'set #1 = :1',
258+
'ReturnValues' => 'UPDATED_NEW',
258259
'ExpressionAttributeNames' => [
259260
'#1' => 'remember_token'
260261
],
@@ -263,7 +264,7 @@ public function it_can_update_remember_token()
263264
'S' => 'new_token'
264265
]
265266
]
266-
]);
267+
])->andReturn($this->sampleAwsResultEmpty());
267268
$this->setConnectionResolver($connection);
268269

269270
$provider = new AuthUserProvider($this->hasher, UserA::class);

tests/Model/ModelTest.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ protected function newConnectionMock()
3636
return $connection;
3737
}
3838

39+
protected function sampleAwsResultEmpty()
40+
{
41+
return new Result([
42+
'@metadata' => [
43+
'statuscode' => 200
44+
]
45+
]);
46+
}
47+
3948
/** @test */
4049
public function it_can_create_new_instance()
4150
{
@@ -446,6 +455,7 @@ public function it_can_save_existing_instance()
446455
]
447456
],
448457
'UpdateExpression' => 'set #1 = :1',
458+
'ReturnValues' => 'UPDATED_NEW',
449459
'ExpressionAttributeNames' => [
450460
'#1' => 'name'
451461
],
@@ -457,7 +467,7 @@ public function it_can_save_existing_instance()
457467
];
458468

459469
$connection = $this->newConnectionMock();
460-
$connection->shouldReceive('updateItem')->with($params);
470+
$connection->shouldReceive('updateItem')->with($params)->andReturn($this->sampleAwsResultEmpty());
461471
$this->setConnectionResolver($connection);
462472

463473
$user = (new UserA)->newFromBuilder(['partition' => 'p']);

tests/Query/BuilderTest.php

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ public function it_can_process_update_item()
668668
]
669669
],
670670
'UpdateExpression' => 'set #1 = :1, #2 = :2 remove #3, #4',
671+
'ReturnValues' => 'UPDATED_NEW',
671672
'ExpressionAttributeNames' => [
672673
'#1' => 'LastPostedBy',
673674
'#2' => 'Replies',
@@ -683,6 +684,7 @@ public function it_can_process_update_item()
683684
]
684685
]
685686
];
687+
$processor = 'processSingleItem';
686688

687689
$query = $this->newQuery('Thread')
688690
->key([
@@ -697,7 +699,97 @@ public function it_can_process_update_item()
697699

698700
$this->assertEquals($method, $query['method']);
699701
$this->assertEquals($params, $query['params']);
700-
$this->assertNull($query['processor']);
702+
$this->assertEquals($processor, $query['processor']);
703+
}
704+
705+
/** @test */
706+
public function it_can_increment_value_of_attribute()
707+
{
708+
$method = 'updateItem';
709+
$params = [
710+
'TableName' => 'Thread',
711+
'Key' => [
712+
'ForumName' => [
713+
'S' => 'Laravel'
714+
],
715+
'Subject' => [
716+
'S' => 'Laravel Thread 1'
717+
]
718+
],
719+
'UpdateExpression' => 'set #1 = #1 + :1, #2 = :2',
720+
'ExpressionAttributeNames' => [
721+
'#1' => 'Replies',
722+
'#2' => 'LastPostedBy'
723+
],
724+
'ExpressionAttributeValues' => [
725+
':1' => [
726+
'N' => '2'
727+
],
728+
':2' => [
729+
'S' => 'User A'
730+
]
731+
],
732+
'ReturnValues' => 'UPDATED_NEW'
733+
];
734+
735+
$query = $this->newQuery('Thread')
736+
->key([
737+
'ForumName' => 'Laravel',
738+
'Subject' => 'Laravel Thread 1'
739+
])->increment('Replies', 2, [
740+
'LastPostedBy' => 'User A'
741+
]);
742+
743+
$processor = 'processSingleItem';
744+
745+
$this->assertEquals($method, $query['method']);
746+
$this->assertEquals($params, $query['params']);
747+
$this->assertEquals($processor, $query['processor']);
748+
}
749+
750+
/** @test */
751+
public function it_can_decrement_value_of_attribute()
752+
{
753+
$method = 'updateItem';
754+
$params = [
755+
'TableName' => 'Thread',
756+
'Key' => [
757+
'ForumName' => [
758+
'S' => 'Laravel'
759+
],
760+
'Subject' => [
761+
'S' => 'Laravel Thread 1'
762+
]
763+
],
764+
'UpdateExpression' => 'set #1 = #1 - :1, #2 = :2',
765+
'ExpressionAttributeNames' => [
766+
'#1' => 'Replies',
767+
'#2' => 'LastPostedBy'
768+
],
769+
'ExpressionAttributeValues' => [
770+
':1' => [
771+
'N' => '2'
772+
],
773+
':2' => [
774+
'S' => 'User A'
775+
]
776+
],
777+
'ReturnValues' => 'UPDATED_NEW'
778+
];
779+
780+
$processor = 'processSingleItem';
781+
782+
$query = $this->newQuery('Thread')
783+
->key([
784+
'ForumName' => 'Laravel',
785+
'Subject' => 'Laravel Thread 1'
786+
])->decrement('Replies', 2, [
787+
'LastPostedBy' => 'User A'
788+
]);
789+
790+
$this->assertEquals($method, $query['method']);
791+
$this->assertEquals($params, $query['params']);
792+
$this->assertEquals($processor, $query['processor']);
701793
}
702794

703795
/** @test */

0 commit comments

Comments
 (0)