Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions docs-v2/content/en/api/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,22 @@ for example: `UserPostRepository` class has the model `UserPost`.

Having this in place you're basically ready for the CRUD actions over posts. You have available the follow endpoints:

| Verb | URI | Action |
| :------------- |:----------------------------- | :-------|
| **GET** | `/api/restify/posts` | index |
| **GET** | `/api/restify/posts/actions` | index actions |
| **GET** | `/api/restify/posts/{post}` | show |
| **GET** | `/api/restify/posts/{post}/actions` | individual actions |
| **POST** | `/api/restify/posts` | store |
| **POST** | `/api/restify/posts/actions?action=actionName` | perform index actions |
| **POST** | `/api/restify/posts/bulk` | store multiple |
| **POST** | `/api/restify/posts/bulk/update` | update multiple |
| **PATCH** | `/api/restify/posts/{post}` | partial update |
| **PUT** | `/api/restify/posts/{post}` | full update |
| **POST** | `/api/restify/posts/{post}` | partial of full update including attachments |
| **POST** | `/api/restify/posts/{post}/actions?action=actionName` | perform index actions |
| **DELETE** | `/api/restify/posts/{post}` | destroy |
| Verb | URI | Action |
|:-----------|:------------------------------------------------------|:---------------------------------------------|
| **GET** | `/api/restify/posts` | index |
| **GET** | `/api/restify/posts/actions` | index actions |
| **GET** | `/api/restify/posts/{post}` | show |
| **GET** | `/api/restify/posts/{post}/actions` | individual actions |
| **POST** | `/api/restify/posts` | store |
| **POST** | `/api/restify/posts/actions?action=actionName` | perform index actions |
| **POST** | `/api/restify/posts/bulk` | store multiple |
| **DELETE** | `/api/restify/posts/bulk/delete` | delete multiple |
| **POST** | `/api/restify/posts/bulk/update` | update multiple |
| **PATCH** | `/api/restify/posts/{post}` | partial update |
| **PUT** | `/api/restify/posts/{post}` | full update |
| **POST** | `/api/restify/posts/{post}` | partial of full update including attachments |
| **POST** | `/api/restify/posts/{post}/actions?action=actionName` | perform index actions |
| **DELETE** | `/api/restify/posts/{post}` | destroy |

<alert>

Expand Down Expand Up @@ -662,6 +663,18 @@ Payload:
]
```

### Bulk delete flow

The payload for a bulk delete should contain an array of primary keys for the models you want to delete:

```json
[
1, 10, 15
]
```

These models will be resolved from the database and check for the `deleteBulk` policy permission, in case any of the models isn't allowed to be deleted, no entry will be deleted.

## Force eager loading

However, Laravel Restify [provides eager](/search/) loading based on the query `related` property, you may want to force
Expand Down
1 change: 1 addition & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
Route::post('/{repository}', \Binaryk\LaravelRestify\Http\Controllers\RepositoryStoreController::class)->name('restify.store');
Route::post('/{repository}/bulk', \Binaryk\LaravelRestify\Http\Controllers\RepositoryStoreBulkController::class)->name('restify.store.bulk');
Route::post('/{repository}/bulk/update', \Binaryk\LaravelRestify\Http\Controllers\RepositoryUpdateBulkController::class)->name('restify.update.bulk');
Route::delete('/{repository}/bulk/delete', \Binaryk\LaravelRestify\Http\Controllers\RepositoryDestroyBulkController::class)->name('restify.destroy.bulk');
Route::get('/{repository}/{repositoryId}', \Binaryk\LaravelRestify\Http\Controllers\RepositoryShowController::class)->name('restify.show');
Route::patch('/{repository}/{repositoryId}', \Binaryk\LaravelRestify\Http\Controllers\RepositoryPatchController::class)->name('restify.patch');
Route::put('/{repository}/{repositoryId}', \Binaryk\LaravelRestify\Http\Controllers\RepositoryUpdateController::class)->name('restify.put');
Expand Down
5 changes: 5 additions & 0 deletions src/Commands/stubs/policy.stub
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class {{ class }}
//
}

public function deleteBulk(User $user, {{ model }} $model)
{
//
}

public function delete(User $user, {{ model }} $model)
{
//
Expand Down
35 changes: 35 additions & 0 deletions src/Http/Controllers/RepositoryDestroyBulkController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Binaryk\LaravelRestify\Http\Controllers;

use Binaryk\LaravelRestify\Http\Requests\RepositoryDestroyBulkRequest;
use Binaryk\LaravelRestify\Repositories\Repository;
use Illuminate\Support\Facades\DB;

class RepositoryDestroyBulkController
{
public function __invoke(RepositoryDestroyBulkRequest $request)
{
$collection = DB::transaction(function () use ($request) {
return $request->collect()
->each(function (int|string $key, int $row) use ($request) {
$model = $request->modelQuery($key)->lockForUpdate()->firstOrFail();

/**
* @var Repository $repository
*/
$repository = $request->repositoryWith($model);

return $repository
->allowToDestroyBulk($request)
->deleteBulk(
$request,
$key,
$row
);
});
});

return ok();
}
}
7 changes: 7 additions & 0 deletions src/Http/Requests/RepositoryDestroyBulkRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Binaryk\LaravelRestify\Http\Requests;

class RepositoryDestroyBulkRequest extends RestifyRequest
{
}
24 changes: 24 additions & 0 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,23 @@ public function updateBulk(RestifyRequest $request, $repositoryId, int $row)
return response()->json();
}

public function deleteBulk(RestifyRequest $request, $repositoryId, int $row)
{
$status = DB::transaction(function () use ($request) {
if (in_array(HasActionLogs::class, class_uses_recursive($this->resource))) {
Restify::actionLog()
->forRepositoryDestroy($this->resource, $request->user())
->save();
}

return $this->resource->delete();
});

static::deleted($status, $request);

return ok(code: 204);
}

public function attach(RestifyRequest $request, $repositoryId, Collection $pivots)
{
$eagerField = $this->authorizeBelongsToMany($request)->belongsToManyField($request);
Expand Down Expand Up @@ -919,6 +936,13 @@ public function allowToUpdateBulk(RestifyRequest $request, $payload = null): sel
return $this;
}

public function allowToDestroyBulk(RestifyRequest $request, $payload = null): self
{
$this->authorizeToDeleteBulk($request);

return $this;
}

public function allowToStore(RestifyRequest $request, $payload = null): self
{
static::authorizeToStore($request);
Expand Down
5 changes: 5 additions & 0 deletions src/Traits/AuthorizableModels.php
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ public function authorizeToUpdateBulk(Request $request)
$this->authorizeTo($request, 'updateBulk');
}

public function authorizeToDeleteBulk(Request $request)
{
$this->authorizeTo($request, 'deleteBulk');
}

/**
* Determine if the current user can update the given resource.
*
Expand Down
2 changes: 1 addition & 1 deletion src/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ function ok(string $message = null, int $code = 200)
], $code);
}

return response()->json([], 204);
return response()->json([], $code);
}
}

Expand Down
39 changes: 39 additions & 0 deletions tests/Controllers/RepositoryDestroyBulkControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Controllers;

use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostPolicy;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostRepository;
use Binaryk\LaravelRestify\Tests\IntegrationTest;
use Illuminate\Support\Facades\Gate;

class RepositoryDestroyBulkControllerTest extends IntegrationTest
{
protected function setUp(): void
{
parent::setUp();

$this->authenticate();
}

public function test_basic_bulk_delete_works(): void
{
Gate::policy(Post::class, PostPolicy::class);

$post1 = Post::factory()->create();
$post2 = Post::factory()->create();
$post3 = Post::factory()->create();

$this->withoutExceptionHandling();

$this->deleteJson(PostRepository::to('bulk/delete'), [
$post1->getKey(),
$post2->getKey(),
])->assertOk();

$this->assertModelMissing($post1);
$this->assertModelMissing($post2);
$this->assertModelExists($post3);
}
}
5 changes: 5 additions & 0 deletions tests/Fixtures/Post/PostPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public function update($user, $post)
return $_SERVER['restify.post.update'] ?? true;
}

public function deleteBulk($user, $post)
{
return $_SERVER['restify.post.deleteBulk'] ?? true;
}

public function delete($user, $post)
{
return $_SERVER['restify.post.delete'] ?? true;
Expand Down