Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Router
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
2 / 2
6
100.00% covered (success)
100.00%
1 / 1
 crud
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 shouldRegisterAction
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace DevToolbelt\LaravelFastCrud;
6
7use 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 */
27final 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}