Skip to content

Commit cbd5e26

Browse files
committed
✨ batchable actions
1 parent f83b72d commit cbd5e26

File tree

10 files changed

+223
-14
lines changed

10 files changed

+223
-14
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,6 @@ TODO
9393

9494
## Roadmap
9595

96-
- Metrics
97-
- Refactor the response class
96+
- Metrics support
97+
- Refactor the response class
98+
- Plain text search using Laravel Scout

src/Actions/Action.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

33
namespace Lomkit\Rest\Actions;
44

5+
use Illuminate\Bus\Batchable;
6+
use Illuminate\Bus\PendingBatch;
57
use Illuminate\Bus\Queueable;
8+
use Illuminate\Foundation\Bus\Dispatchable;
9+
use Illuminate\Queue\InteractsWithQueue;
10+
use Illuminate\Queue\SerializesModels;
611
use Illuminate\Support\Collection;
712
use Illuminate\Support\Str;
813
use Lomkit\Rest\Concerns\Makeable;
@@ -12,7 +17,7 @@
1217

1318
class Action implements \JsonSerializable
1419
{
15-
use Makeable, Metable;
20+
use Makeable, Metable, Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
1621

1722
/**
1823
* The name of the connection the job should be sent to.
@@ -115,6 +120,18 @@ public function fields(\Lomkit\Rest\Http\Requests\RestRequest $request)
115120
return [];
116121
}
117122

123+
/**
124+
* Register callbacks on the pending batch.
125+
*
126+
* @param array $fields
127+
* @param \Illuminate\Bus\PendingBatch $batch
128+
* @return void
129+
*/
130+
public function withBatch(array $fields, PendingBatch $batch)
131+
{
132+
//
133+
}
134+
118135
/**
119136
* Execute the action for the given request.
120137
*

src/Actions/CallRestApiAction.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Lomkit\Rest\Actions;
44

5+
use Illuminate\Bus\Batchable;
56
use Illuminate\Bus\Queueable;
67
use Illuminate\Foundation\Bus\Dispatchable;
78
use Illuminate\Queue\InteractsWithQueue;
@@ -10,7 +11,7 @@
1011

1112
class CallRestApiAction
1213
{
13-
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
14+
use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
1415

1516
/**
1617
* Models collection.
@@ -55,7 +56,7 @@ public function __construct(Action $action, array $fields, Collection $models)
5556
*/
5657
public function handle()
5758
{
58-
$this->action->handle($this->fields, $this->models);
59+
$this->action->setJob($this->job)->handle($this->fields, $this->models);
5960
}
6061

6162
/**

src/Actions/DispatchAction.php

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Illuminate\Support\Facades\Bus;
99
use Illuminate\Support\Facades\DB;
1010
use Illuminate\Support\Facades\Queue;
11+
use Lomkit\Rest\Contracts\BatchableAction;
1112
use Lomkit\Rest\Contracts\QueryBuilder;
1213
use Lomkit\Rest\Http\Requests\OperateRequest;
1314
use Lomkit\Rest\Http\Requests\RestRequest;
@@ -35,6 +36,12 @@ class DispatchAction
3536
*/
3637
protected $fields;
3738

39+
/**
40+
* The pending batch instance.
41+
*
42+
* @var \Illuminate\Bus\PendingBatch|null
43+
*/
44+
protected $batchJob;
3845

3946
/**
4047
* Create a new action dispatcher instance.
@@ -44,11 +51,38 @@ class DispatchAction
4451
* @param array $fields
4552
* @return void
4653
*/
47-
public function __construct(OperateRequest $request, Action $action, array $fields)
54+
public function __construct(OperateRequest $request, \Lomkit\Rest\Actions\Action $action, array $fields)
4855
{
4956
$this->request = $request;
5057
$this->action = $action;
5158
$this->fields = $fields;
59+
60+
if ($action instanceof BatchableAction) {
61+
$this->configureBatchJob($action, $fields);
62+
}
63+
}
64+
65+
/**
66+
* Configure the batch job for the action.
67+
*
68+
* @param \Lomkit\Rest\Actions\Action $action
69+
* @param array $fields
70+
* @return void
71+
*/
72+
protected function configureBatchJob(\Lomkit\Rest\Actions\Action $action, array $fields)
73+
{
74+
$batch = Bus::batch([]);
75+
$batch->name($action->uriKey());
76+
77+
if (! is_null($connection = $this->connection())) {
78+
$batch->onConnection($connection);
79+
}
80+
if (! is_null($queue = $this->queue())) {
81+
$batch->onQueue($queue);
82+
}
83+
84+
$action->withBatch($fields, $batch);
85+
$this->batchJob = $batch;
5286
}
5387

5488
/**
@@ -77,6 +111,10 @@ function ($chunk) {
77111
}
78112
);
79113

114+
if (! is_null($this->batchJob)) {
115+
$this->batchJob->dispatch();
116+
}
117+
80118
return $searchQuery->count();
81119
}
82120

@@ -129,13 +167,15 @@ protected function dispatchSynchronouslyForCollection(Collection $models)
129167
*/
130168
protected function addQueuedActionJob( Collection $models)
131169
{
132-
$job = new CallRestApiAction(
133-
$this->action, $this->fields, $models
134-
);
170+
$job = new CallRestApiAction($this->action, $this->fields, $models);
135171

136-
Queue::connection($this->connection())->pushOn(
137-
$this->queue(), $job
138-
);
172+
if ($this->action instanceof BatchableAction) {
173+
$this->batchJob->add([$job]);
174+
} else {
175+
Queue::connection($this->connection())->pushOn(
176+
$this->queue(), $job
177+
);
178+
}
139179

140180
return $this;
141181
}

src/Contracts/BatchableAction.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Lomkit\Rest\Contracts;
4+
5+
use Illuminate\Bus\PendingBatch;
6+
7+
interface BatchableAction
8+
{
9+
/**
10+
* Register callbacks on the pending batch.
11+
*
12+
* @param array $fields
13+
* @param \Illuminate\Bus\PendingBatch $batch
14+
* @return void
15+
*/
16+
public function withBatch(array $fields, PendingBatch $batch);
17+
}

tests/Feature/Controllers/ActionsOperationsTest.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Lomkit\Rest\Tests\Feature\Controllers;
44

5+
use Illuminate\Bus\PendingBatch;
56
use Illuminate\Queue\Queue;
7+
use Illuminate\Support\Facades\Bus;
68
use Illuminate\Support\Facades\Gate;
79
use Lomkit\Rest\Actions\CallRestApiAction;
810
use Lomkit\Rest\Http\Requests\RestRequest;
@@ -33,7 +35,7 @@ public function test_getting_actions(): void
3335
'/api/models/actions',
3436
['Accept' => 'application/json']
3537
);
36-
38+
3739
$response->assertJson(
3840
['data' => collect(new ModelResource)->each->jsonSerialize()->toArray()]
3941
);
@@ -249,4 +251,25 @@ public function test_operate_action_with_search(): void
249251
Model::where('number', 100000000)->count()
250252
);
251253
}
254+
255+
public function test_operate_batchable_action(): void
256+
{
257+
ModelFactory::new()->count(150)->create();
258+
259+
Gate::policy(Model::class, GreenPolicy::class);
260+
261+
Bus::fake();
262+
263+
$response = $this->post(
264+
'/api/models/actions/batchable-modify-number',
265+
[],
266+
['Accept' => 'application/json']
267+
);
268+
269+
270+
Bus::assertBatched(function (PendingBatch $batch) {
271+
return $batch->name == 'batchable-modify-number' &&
272+
$batch->jobs->count() === 2;
273+
});
274+
}
252275
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*
12+
* @return void
13+
*/
14+
public function up()
15+
{
16+
Schema::create('job_batches', function (Blueprint $table) {
17+
$table->string('id')->primary();
18+
$table->string('name');
19+
$table->integer('total_jobs');
20+
$table->integer('pending_jobs');
21+
$table->integer('failed_jobs');
22+
$table->longText('failed_job_ids');
23+
$table->mediumText('options')->nullable();
24+
$table->integer('cancelled_at')->nullable();
25+
$table->integer('created_at');
26+
$table->integer('finished_at')->nullable();
27+
});
28+
}
29+
};
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace Lomkit\Rest\Tests\Support\Rest\Actions;
4+
5+
use Illuminate\Bus\Batch;
6+
use Illuminate\Bus\PendingBatch;
7+
use Illuminate\Contracts\Queue\ShouldQueue;
8+
use Illuminate\Database\Eloquent\Model;
9+
use Illuminate\Support\Collection;
10+
use Lomkit\Rest\Actions\Action;
11+
use Lomkit\Rest\Contracts\BatchableAction;
12+
use Lomkit\Rest\Http\Requests\RestRequest;
13+
use Throwable;
14+
15+
class BatchableModifyNumberAction extends Action implements ShouldQueue, BatchableAction
16+
{
17+
public function __construct()
18+
{
19+
$this->withMeta([
20+
'color' => '#FFFFFF'
21+
]);
22+
}
23+
24+
/**
25+
* Perform the action on the given models.
26+
*
27+
* @param array $fields
28+
* @param \Illuminate\Support\Collection $models
29+
* @return mixed
30+
*/
31+
public function handle(array $fields, Collection $models)
32+
{
33+
foreach ($models as $model) {
34+
/** @var Model $model */
35+
$model->forceFill(
36+
['number' => $fields['number'] ?? 100000000]
37+
)
38+
->save();
39+
}
40+
}
41+
42+
/**
43+
* Called in an action failed.
44+
*
45+
* @param RestRequest $request
46+
* @return array
47+
*/
48+
public function fields(RestRequest $request)
49+
{
50+
return [
51+
'number' => [
52+
'min: 100'
53+
]
54+
];
55+
}
56+
57+
/**
58+
* Register callbacks on the pending batch.
59+
*
60+
* @param array $fields
61+
* @param \Illuminate\Bus\PendingBatch $batch
62+
* @return void
63+
*/
64+
public function withBatch(array $fields, PendingBatch $batch)
65+
{
66+
$batch->then(function (Batch $batch) {
67+
// ...
68+
})->catch(function (Batch $batch, Throwable $e) {
69+
// ...
70+
})->finally(function (Batch $batch) {
71+
// ...
72+
});
73+
}
74+
}

tests/Support/Rest/Actions/ModifyNumberAction.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,9 @@ public function fields(RestRequest $request)
4343
]
4444
];
4545
}
46+
47+
public function failed(array $fields, Collection $models, $exception)
48+
{
49+
dump('FAILED');
50+
}
4651
}

tests/Support/Rest/Resources/ModelResource.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Lomkit\Rest\Tests\Support\Models\Model;
2222
use Lomkit\Rest\Tests\Support\Models\MorphedByManyRelation;
2323
use Lomkit\Rest\Tests\Support\Models\MorphOneRelation;
24+
use Lomkit\Rest\Tests\Support\Rest\Actions\BatchableModifyNumberAction;
2425
use Lomkit\Rest\Tests\Support\Rest\Actions\ModifyNumberAction;
2526
use Lomkit\Rest\Tests\Support\Rest\Actions\QueueableModifyNumberAction;
2627
use Lomkit\Rest\Tests\Support\Rest\Actions\WithMetaModifyNumberAction;
@@ -126,7 +127,8 @@ public function actions(RestRequest $request): array {
126127
return [
127128
ModifyNumberAction::make(),
128129
QueueableModifyNumberAction::make(),
129-
WithMetaModifyNumberAction::make()
130+
WithMetaModifyNumberAction::make(),
131+
BatchableModifyNumberAction::make()
130132
];
131133
}
132134
}

0 commit comments

Comments
 (0)