Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
100.00% |
14 / 14 |
|
100.00% |
2 / 2 |
CRAP | |
100.00% |
1 / 1 |
| Router | |
100.00% |
14 / 14 |
|
100.00% |
2 / 2 |
6 | |
100.00% |
1 / 1 |
| crud | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
4 | |||
| shouldRegisterAction | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace DevToolbelt\LaravelFastCrud; |
| 6 | |
| 7 | use Illuminate\Support\Facades\Route; |
| 8 | |
| 9 | /** |
| 10 | * Route registration helper for CRUD controllers. |
| 11 | * |
| 12 | * Provides a convenient method to register all RESTful routes for a CRUD controller |
| 13 | * with automatic permission middleware assignment. |
| 14 | * |
| 15 | * @example |
| 16 | * ```php |
| 17 | * // Register all CRUD routes for products |
| 18 | * Router::crud('products', ProductController::class, 'products'); |
| 19 | * |
| 20 | * // Exclude specific actions |
| 21 | * Router::crud('categories', CategoryController::class, 'categories', except: ['delete', 'exportCsv']); |
| 22 | * |
| 23 | * // Include only specific actions |
| 24 | * Router::crud('tags', TagController::class, 'tags', only: ['search', 'read']); |
| 25 | * ``` |
| 26 | */ |
| 27 | final class Router extends Route |
| 28 | { |
| 29 | /** |
| 30 | * Default CRUD action definitions. |
| 31 | * |
| 32 | * @var array<int, array{verb: string, path: string, method: string, permission: string}> |
| 33 | */ |
| 34 | private const CRUD_ACTIONS = [ |
| 35 | ['verb' => 'get', 'path' => '', 'method' => 'search', 'permission' => 'search'], |
| 36 | ['verb' => 'get', 'path' => '/options', 'method' => 'options', 'permission' => 'search'], |
| 37 | ['verb' => 'post', 'path' => '', 'method' => 'create', 'permission' => 'create'], |
| 38 | ['verb' => 'get', 'path' => '/export-csv', 'method' => 'exportCsv', 'permission' => 'exportCsv'], |
| 39 | ['verb' => 'get', 'path' => '/{id:uuid}', 'method' => 'read', 'permission' => 'view'], |
| 40 | ['verb' => 'put', 'path' => '/{id:uuid}', 'method' => 'update', 'permission' => 'update'], |
| 41 | ['verb' => 'patch', 'path' => '/{id:uuid}', 'method' => 'update', 'permission' => 'update'], |
| 42 | ['verb' => 'post', 'path' => '/{id:uuid}', 'method' => 'update', 'permission' => 'update'], |
| 43 | ['verb' => 'delete', 'path' => '/{id:uuid}', 'method' => 'delete', 'permission' => 'delete'], |
| 44 | ['verb' => 'delete', 'path' => '/{id:uuid}/soft', 'method' => 'softDelete', 'permission' => 'delete'], |
| 45 | ['verb' => 'patch', 'path' => '/{id:uuid}/restore', 'method' => 'restore', 'permission' => 'restore'], |
| 46 | ['verb' => 'put', 'path' => '/{id:uuid}/restore', 'method' => 'restore', 'permission' => 'restore'], |
| 47 | ]; |
| 48 | |
| 49 | /** |
| 50 | * Register CRUD routes for a controller with permission middleware. |
| 51 | * |
| 52 | * Generates the following routes: |
| 53 | * - GET /{uri} -> search() with {module}.access.search permission |
| 54 | * - GET /{uri}/options -> options() with {module}.access.search permission |
| 55 | * - POST /{uri} -> create() with {module}.access.create permission |
| 56 | * - GET /{uri}/export-csv -> exportCsv() with {module}.access.exportCsv permission |
| 57 | * - GET /{uri}/{id:uuid} -> read() with {module}.access.view permission |
| 58 | * - PUT|PATCH|POST /{uri}/{id:uuid} -> update() with {module}.access.update permission |
| 59 | * - DELETE /{uri}/{id:uuid} -> delete() with {module}.access.delete permission |
| 60 | * - DELETE /{uri}/{id:uuid}/soft -> softDelete() with {module}.access.delete permission |
| 61 | * - PUT|PATCH /{uri}/{id:uuid}/restore -> restore() with {module}.access.restore permission |
| 62 | * |
| 63 | * @param string $uri Base URI for the resource (e.g., 'products', 'api/v1/users') |
| 64 | * @param class-string $controllerName Fully qualified controller class name |
| 65 | * @param string $moduleName Module name used for permission middleware (e.g., 'products') |
| 66 | * @param array<int, string> $except Action methods to exclude (e.g., ['delete', 'exportCsv']) |
| 67 | * @param array<int, string> $only If provided, only these action methods will be registered |
| 68 | */ |
| 69 | public static function crud( |
| 70 | string $uri, |
| 71 | string $controllerName, |
| 72 | string $moduleName, |
| 73 | array $except = [], |
| 74 | array $only = [] |
| 75 | ): void { |
| 76 | foreach (self::CRUD_ACTIONS as $action) { |
| 77 | $verb = $action['verb']; |
| 78 | $path = $uri . $action['path']; |
| 79 | $method = $action['method']; |
| 80 | $permission = $action['permission']; |
| 81 | |
| 82 | if (!method_exists($controllerName, $method)) { |
| 83 | continue; |
| 84 | } |
| 85 | |
| 86 | if (!self::shouldRegisterAction($method, $only, $except)) { |
| 87 | continue; |
| 88 | } |
| 89 | |
| 90 | static::$verb($path, "{$controllerName}@{$method}") |
| 91 | ->middleware("can:{$moduleName}.access.{$permission}"); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * Determines if an action should be registered based on only/except filters. |
| 97 | * |
| 98 | * @param string $method The action method name |
| 99 | * @param array<int, string> $only Actions to include (if not empty, only these are registered) |
| 100 | * @param array<int, string> $except Actions to exclude |
| 101 | */ |
| 102 | private static function shouldRegisterAction(string $method, array $only, array $except): bool |
| 103 | { |
| 104 | if (!empty($only)) { |
| 105 | return in_array($method, $only, true); |
| 106 | } |
| 107 | |
| 108 | return !in_array($method, $except, true); |
| 109 | } |
| 110 | } |