Skip to content

Commit

Permalink
refactor: create queuable services (#5834)
Browse files Browse the repository at this point in the history
  • Loading branch information
asbiin authored Jan 3, 2022
1 parent dc7ec5f commit d76af04
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 12 deletions.
4 changes: 2 additions & 2 deletions app/Http/Controllers/Api/ApiContactController.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,10 @@ public function update(Request $request, $contactId)
public function destroy(Request $request, $contactId)
{
$data = [
'contact_id' => $contactId,
'account_id' => auth()->user()->account_id,
'contact_id' => $contactId,
];
app(DestroyContact::class)->execute($data);
DestroyContact::dispatch($data);

return $this->respondObjectDeleted($contactId);
}
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/ContactsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ public function destroy(Request $request, Contact $contact)
'contact_id' => $contact->id,
];

app(DestroyContact::class)->execute($data);
DestroyContact::dispatch($data);

return redirect()->route('people.index')
->with('success', trans('people.people_delete_success'));
Expand Down
74 changes: 74 additions & 0 deletions app/Jobs/ServiceQueue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace App\Jobs;

use Throwable;
use App\Services\BaseService;
use Illuminate\Bus\Queueable;
use App\Services\QueuableService;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;

class ServiceQueue implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

/**
* The service to queue.
*
* @var QueuableService
*/
public $service;

/**
* The data to run service.
*
* @var array
*/
public $data;

/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 1;

/**
* Create a new job instance.
*
* @param BaseService $service
* @param array|null $data
*/
public function __construct(BaseService $service, array $data = null)
{
if (! $service instanceof QueuableService) {
throw new \Exception('Service is not queuable');
}
$this->service = $service;
$this->data = $data;
}

/**
* Execute the job.
*
* @return void
*/
public function handle(): void
{
$this->service->handle($this->data);
}

/**
* Handle a job failure.
*
* @param \Throwable $exception
* @return void
*/
public function failed(Throwable $exception): void
{
$this->service->failed($exception);
}
}
12 changes: 7 additions & 5 deletions app/Services/Contact/Contact/DestroyContact.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

use App\Services\BaseService;
use App\Models\Contact\Contact;
use App\Services\QueuableService;
use App\Services\DispatchableService;
use App\Models\Relationship\Relationship;
use App\Services\Contact\Relationship\DestroyRelationship;

class DestroyContact extends BaseService
class DestroyContact extends BaseService implements QueuableService
{
use DispatchableService;

/**
* Get the validation rules that apply to the service.
*
Expand All @@ -26,9 +30,9 @@ public function rules()
* Destroy a contact.
*
* @param array $data
* @return bool
* @return void
*/
public function execute(array $data): bool
public function handle(array $data): void
{
$this->validate($data);

Expand All @@ -41,8 +45,6 @@ public function execute(array $data): bool

$contact->deleteAvatars();
$contact->delete();

return true;
}

/**
Expand Down
50 changes: 50 additions & 0 deletions app/Services/DispatchableService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace App\Services;

use Throwable;
use App\Jobs\ServiceQueue;
use Illuminate\Foundation\Bus\PendingDispatch;

/**
* This trait helps dispatch a QueuableService.
*/
trait DispatchableService
{
/**
* Dispatch the service with the given arguments.
*
* @param mixed ...$arguments
* @return \Illuminate\Foundation\Bus\PendingDispatch
*/
public static function dispatch(...$arguments): PendingDispatch
{
/** @var QueuableService $service */
$service = new self();

return ServiceQueue::dispatch($service, ...$arguments);
}

/**
* Dispatch the service with the given arguments on the sync queue.
*
* @param mixed ...$arguments
* @return mixed
*/
public static function dispatchSync(...$arguments)
{
/** @var QueuableService $service */
$service = new self();

return ServiceQueue::dispatchSync($service, ...$arguments);
}

/**
* Handle a job failure.
*
* @param \Throwable $exception
*/
public function failed(Throwable $exception): void
{
}
}
27 changes: 27 additions & 0 deletions app/Services/QueuableService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace App\Services;

use Throwable;

/**
* Makes a BaseService queuable using the generic ServiceQueue job.
*/
interface QueuableService
{
/**
* Execute the service.
*
* @param array $data
* @return void
*/
public function handle(array $data): void;

/**
* Handle a job failure.
*
* @param \Throwable $exception
* @return void
*/
public function failed(Throwable $exception): void;
}
54 changes: 54 additions & 0 deletions tests/Unit/Jobs/ServiceQueueTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

namespace Tests\Unit\Jobs;

use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class ServiceQueueTest extends TestCase
{
use DatabaseTransactions;

/** @test */
public function it_run_a_service_ok(): void
{
config(['queue.default' => 'sync']);

ServiceQueueTester::dispatch();

$this->assertTrue(ServiceQueueTester::$executed);
$this->assertFalse(ServiceQueueTester::$failed);
}

/** @test */
public function it_run_a_service_sync(): void
{
ServiceQueueTester::dispatchSync();

$this->assertTrue(ServiceQueueTester::$executed);
$this->assertFalse(ServiceQueueTester::$failed);
}

/** @test */
public function it_run_a_service_which_failed(): void
{
$this->expectException(\Exception::class);
try {
ServiceQueueTester::dispatchSync(['throw' => true]);
} finally {
$this->assertTrue(ServiceQueueTester::$executed);
$this->assertTrue(ServiceQueueTester::$failed);
}
}

/** @test */
public function service_is_not_run_if_queue_set(): void
{
config(['queue.default' => 'database']);

ServiceQueueTester::dispatch(['throw' => true]);

$this->assertFalse(ServiceQueueTester::$executed);
$this->assertFalse(ServiceQueueTester::$failed);
}
}
55 changes: 55 additions & 0 deletions tests/Unit/Jobs/ServiceQueueTester.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Tests\Unit\Jobs;

use Throwable;
use App\Services\BaseService;
use App\Services\QueuableService;
use App\Services\DispatchableService;

class ServiceQueueTester extends BaseService implements QueuableService
{
use DispatchableService;

public static bool $executed = false;
public static bool $failed = false;

public bool $object;

/**
* Initialize the service.
*
* @param array $data
*/
public function __construct()
{
self::$executed = false;
self::$failed = false;
}

/**
* Execute the service.
*/
public function handle($data): void
{
self::$executed = true;

if ($data && $data['throw'] === true) {
throw new \Exception();
}
}

/**
* Handle a job failure.
*
* @param \Throwable $exception
*/
public function failed(Throwable $exception): void
{
self::$failed = true;

if (isset($this->obj)) {
// variable can be touch
}
}
}
8 changes: 4 additions & 4 deletions tests/Unit/Services/Contact/Contact/DestroyContactTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function it_destroys_a_contact()
'contact_id' => $contact->id,
];

app(DestroyContact::class)->execute($request);
app(DestroyContact::class)->handle($request);

$this->assertDatabaseMissing('contacts', [
'id' => $contact->id,
Expand All @@ -42,7 +42,7 @@ public function it_fails_if_contact_is_archived()
];

$this->expectException(ValidationException::class);
app(DestroyContact::class)->execute($request);
app(DestroyContact::class)->handle($request);
}

/** @test */
Expand All @@ -55,7 +55,7 @@ public function it_fails_if_wrong_parameters_are_given()
];

$this->expectException(ValidationException::class);
app(DestroyContact::class)->execute($request);
app(DestroyContact::class)->handle($request);
}

/** @test */
Expand All @@ -70,6 +70,6 @@ public function it_throws_an_exception_if_contact_doesnt_exist()
];

$this->expectException(ModelNotFoundException::class);
app(DestroyContact::class)->execute($request);
app(DestroyContact::class)->handle($request);
}
}

0 comments on commit d76af04

Please sign in to comment.