Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
chore: make restoration of soft-deleted record works with policy
Browse files Browse the repository at this point in the history
Signed-off-by: Fery Wardiyanto <[email protected]>
  • Loading branch information
feryardiant committed Mar 31, 2024
1 parent 1febf19 commit 62c2fdb
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 128 deletions.
18 changes: 9 additions & 9 deletions routes/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@

Route::middleware(['api', 'auth:sanctum'])->group(function () {
Route::apiResource('companies', Controllers\CompanyController::class);
// Route::prefix('companies')->controller(Controllers\CompanyController::class)->group(function () {
// Route::put('{company}/restore', 'restore')->name('companies.restore')->withTrashed();
// });
Route::prefix('companies')->controller(Controllers\CompanyController::class)->group(function () {
Route::put('{company}/restore', 'restore')->name('companies.restore')->withTrashed();
});

Route::apiResource('personnels', Controllers\PersonnelController::class);
// Route::prefix('personnels')->controller(Controllers\EmployeeController::class)->group(function () {
// Route::put('{employee}/restore', 'restore')->name('personnels.restore')->withTrashed();
// });
Route::prefix('personnels')->controller(Controllers\PersonnelController::class)->group(function () {
Route::put('{personnel}/restore', 'restore')->name('personnels.restore')->withTrashed();
});

Route::apiResource('addresses', Controllers\AddressController::class);
Route::prefix('addresses')->controller(Controllers\AddressController::class)->group(function () {
Expand Down Expand Up @@ -63,8 +63,8 @@
Route::apiResource($route, Controllers\StakeholderController::class)
->parameter((string) $route, 'stakeholder');

// Route::prefix($route)->controller(Controllers\StakeholderController::class)->group(function () use ($route) {
// Route::put('{stakeholder}/restore', 'restore')->name($route.'.restore')->withTrashed();
// });
Route::prefix($route)->controller(Controllers\StakeholderController::class)->group(function () use ($route) {
Route::put('{stakeholder}/restore', 'restore')->name($route.'.restore')->withTrashed();
});
}
});
30 changes: 15 additions & 15 deletions src/Http/Controllers/CompanyController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,58 +22,58 @@ public function __construct()
/**
* Index endpoint for companies.
*/
public function index(Company $org, IndexRequest $request): OrganizationCollection
public function index(Company $company, IndexRequest $request): OrganizationCollection
{
return new OrganizationCollection(
$request->fulfill($org)->paginate()
$request->fulfill($company)->paginate()
);
}

/**
* Create endpoint for company.
*/
public function store(Company $org, StoreRequest $request): JsonResponse
public function store(Company $company, StoreRequest $request): JsonResponse
{
$org = $request->fulfill($org);
$company = $request->fulfill($company);

return $this->show($org)->toResponse($request)->setStatusCode(201);
return $this->show($company)->toResponse($request)->setStatusCode(201);
}

/**
* Show endpoint for a single company.
*/
public function show(Company $org): OrganizationResource
public function show(Company $company): OrganizationResource
{
return new OrganizationResource($org);
return new OrganizationResource($company);
}

/**
* Update endpoint for a single company.
*/
public function update(Company $org, UpdateRequest $request): OrganizationResource
public function update(Company $company, UpdateRequest $request): OrganizationResource
{
$request->fulfill($org);
$request->fulfill($company);

return $this->show($org);
return $this->show($company);
}

/**
* Delete endpoint for a single company.
*/
public function destroy(Company $org, DeleteRequest $request): Response
public function destroy(Company $company, DeleteRequest $request): Response
{
$request->fulfill($org);
$request->fulfill($company);

return response()->noContent();
}

/**
* Restore endpoint for a single company.
*/
public function restore(Company $org): OrganizationResource
public function restore(Company $company): OrganizationResource
{
$org->restore();
$company->restore();

return $this->show($org);
return $this->show($company->refresh());
}
}
24 changes: 12 additions & 12 deletions src/Http/Controllers/PersonnelController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,46 +36,46 @@ public function index(Company $company, IndexRequest $request): PersonCollection
public function store(Company $company, StoreRequest $request): JsonResponse
{
/** @var Personnel */
$person = $request->fulfill($company);
$personnel = $request->fulfill($company);

return $this->show($person)->toResponse($request)->setStatusCode(201);
return $this->show($personnel)->toResponse($request)->setStatusCode(201);
}

/**
* Show endpoint for a single employee.
*/
public function show(Personnel $person): PersonResource
public function show(Personnel $personnel): PersonResource
{
return new PersonResource($person);
return new PersonResource($personnel);
}

/**
* Update endpoint for a single employee.
*/
public function update(Personnel $person, UpdateRequest $request): PersonResource
public function update(Personnel $personnel, UpdateRequest $request): PersonResource
{
$request->fulfill($person);
$request->fulfill($personnel);

return $this->show($person);
return $this->show($personnel);
}

/**
* Delete endpoint for a single employee.
*/
public function destroy(Personnel $person, DeleteRequest $request): Response
public function destroy(Personnel $personnel, DeleteRequest $request): Response
{
$request->fulfill($person);
$request->fulfill($personnel);

return response()->noContent();
}

/**
* Restore endpoint for a single employee.
*/
public function restore(Personnel $person): PersonResource
public function restore(Personnel $personnel): PersonResource
{
$person->restore();
$personnel->restore();

return $this->show($person);
return $this->show($personnel);
}
}
4 changes: 0 additions & 4 deletions src/Http/Resources/AsAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ final protected function addressMeta(): array

final protected function forAddress(Address $address): array
{
if (! $address->exists) {
return [];
}

return [
$address->getKeyName() => $address->getKey(),
'type' => $address->type ? [
Expand Down
41 changes: 27 additions & 14 deletions src/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,46 +28,59 @@ public function __construct(protected Router $router)
/**
* @param Authenticatable|\Illuminate\Foundation\Auth\User|HasProfile $user
*/
public function resolvePerson(Authenticatable $user): Personnel
public function resolvePerson(Authenticatable $user, $default = null): Entity|Personnel
{
$user->loadMissing('profile');

$key = $this->router->input('personnel');
if (! ($key = $this->router->input('personnel', $default))) {
return $user->profile;
}

/** @var \Creasi\Base\Database\Models\Person */
$model = $user->profile()->newModelInstance();

return $key ? $user->profile->resolveRouteBinding($key) : $user->profile;
return $this->router->current()->allowsTrashedBindings()
? $model->resolveSoftDeletableRouteBinding($key)
: $model->resolveRouteBinding($key);
}

/**
* @param Authenticatable|\Illuminate\Foundation\Auth\User|HasProfile $user
*/
public function resolveOrganization(Authenticatable $user): Company
public function resolveOrganization(Authenticatable $user, $default = null): Entity|Company
{
$user->loadMissing('profile');

$key = $this->router->input('company');
if (! ($key = $this->router->input('company', $default))) {
return $user->profile->employer;
}

/** @var \Creasi\Base\Database\Models\Organization */
$model = $user->profile->employers()->newModelInstance();

return $key ? $user->profile->employer->resolveRouteBinding($key) : $user->profile->employer;
return $this->router->current()->allowsTrashedBindings()
? $model->resolveSoftDeletableRouteBinding($key)
: $model->resolveRouteBinding($key);
}

public function resolveEntity(Company $org, Personnel $person): Entity
public function resolveEntity(Authenticatable $user): Entity
{
$entity = $this->currentRoutePrefix('companies') ? $org : $person;
$key = $this->router->input('entity');

// For some reason we do need to resolve the binding ourselves
// see: https://stackoverflow.com/a/76717314/881743
return $key ? $entity->resolveRouteBinding($key) : $entity;
return $this->currentRoutePrefix('companies')
? $this->resolveOrganization($user, $key)
: $this->resolvePerson($user, $key);
}

public function resolveStakeholder(Company $org, StakeholderType $type): Stakeholder
public function resolveStakeholder(Company $company, StakeholderType $type): Stakeholder
{
/** @var \Creasi\Base\Database\Models\OrganizationRelative */
$relative = $org->stakeholders()->newQuery()->with('stakeholder')->where([
$relative = $company->stakeholders()->newQuery()->with('stakeholder')->where([
'type' => $type,
'stakeholder_id' => (int) $this->router->input('stakeholder'),
])->first();

return $relative?->stakeholder ?: $org->newInstance();
return $relative?->stakeholder ?: $company->newInstance();
}

public function resolveOrganizationRelativeType(): StakeholderType
Expand Down
16 changes: 4 additions & 12 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ServiceProvider extends IlluminateServiceProvider
protected $policies = [
Contracts\Company::class => Policies\OrganizationPolicy::class,
Contracts\Personnel::class => Policies\PersonPolicy::class,
Contracts\Stakeholder::class => Policies\StakeholderPolicy::class,
// Contracts\Stakeholder::class => Policies\StakeholderPolicy::class,
Models\Address::class => Policies\AddressPolicy::class,
Models\File::class => Policies\FilePolicy::class,
];
Expand Down Expand Up @@ -144,17 +144,9 @@ protected function defineRoutes(): void
return $request->isMethodCacheable() ? $request->query('api_token') : null;
});

Route::bind('company', function (string $value) {
return app(Contracts\Company::class)->resolveRouteBinding($value);
});

Route::bind('personnel', function (string $value) {
return app(Contracts\Personnel::class)->resolveRouteBinding($value);
});

Route::bind('stakeholder', function (string $value) {
return app(Contracts\Stakeholder::class)->resolveRouteBinding($value);
});
Route::bind('company', fn () => app(Contracts\Company::class));
Route::bind('personnel', fn () => app(Contracts\Personnel::class));
Route::bind('stakeholder', fn () => app(Contracts\Stakeholder::class));

if (app()->routesAreCached() || config('creasi.base.routes_enable') === false) {
return;
Expand Down
23 changes: 9 additions & 14 deletions tests/Feature/Http/AddressTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function should_able_to_update_existing_data(): void
}

#[Test]
public function should_able_to_delete_existing_data(): void
public function should_able_to_delete_and_restore_data(): void
{
Sanctum::actingAs($user = $this->user());

Expand All @@ -112,24 +112,19 @@ public function should_able_to_delete_existing_data(): void
$response = $this->deleteJson($this->getRoutePath($model));

$response->assertNoContent();
}

#[Test]
public function should_able_to_restore_deleted_data(): void
{
Sanctum::actingAs($user = $this->user());
$this->assertSoftDeleted($model);

$model = Address::factory()->createOne();
$user->profile->addresses()->save($model);
$response = $this->putJson($this->getRoutePath($model, 'restore'));

$deleted = clone $model;
$model->delete();
$response->assertOk();

$response = $this->putJson($this->getRoutePath($deleted, 'restore'));
$response = $this->deleteJson($this->getRoutePath($model), ['force' => true]);

$response->assertOk()->assertJsonStructure([
'data' => $this->dataStructure,
'meta' => ['types'],
$response->assertNoContent();

$this->assertDatabaseMissing($model, [
$model->getRouteKeyName() => $model->getRouteKey(),
]);
}
}
22 changes: 10 additions & 12 deletions tests/Feature/Http/CompanyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ public function should_able_to_update_existing_data(): void
}

#[Test]
public function should_able_to_delete_existing_data(): void
public function should_able_to_delete_and_restore_data(): void
{
Sanctum::actingAs($this->user());

Expand All @@ -190,24 +190,22 @@ public function should_able_to_delete_existing_data(): void
$response = $this->deleteJson($this->getRoutePath($model));

$response->assertNoContent();
}

#[Test]
public function should_able_to_restore_deleted_data(): void
{
$this->markTestIncomplete();

Sanctum::actingAs($user = $this->user());

$model = Organization::factory()->createOne();

$model->delete();
$this->assertSoftDeleted($model);

$response = $this->putJson($this->getRoutePath($model, 'restore'));

$response->assertOk()->assertJsonStructure([
'data' => $this->dataStructure,
'meta' => [],
]);

$response = $this->deleteJson($this->getRoutePath($model), ['force' => true]);

$response->assertNoContent();

$this->assertDatabaseMissing($model, [
$model->getRouteKeyName() => $model->getRouteKey(),
]);
}
}
Loading

0 comments on commit 62c2fdb

Please sign in to comment.