├── .github ├── FUNDING.yml └── workflows │ └── run-tests.yml ├── .gitignore ├── README.md ├── composer.json ├── config └── auto-route.php ├── phpunit.xml.dist ├── src ├── AutoRoute.php ├── AutoRouteServiceProvider.php ├── Facades │ └── Route.php └── Middleware │ └── AjaxRequestMiddleware.php └── tests └── AutoRouteTest.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ['izniburak'] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['https://buymeacoff.ee/izniburak'] 14 | -------------------------------------------------------------------------------- /.github/workflows/run-tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: true 10 | matrix: 11 | os: [ubuntu-latest] 12 | php: [8.1, 8.2] 13 | stability: [prefer-stable] 14 | 15 | name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v2 20 | 21 | - name: Setup PHP 22 | uses: shivammathur/setup-php@v2 23 | with: 24 | php-version: ${{ matrix.php }} 25 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 26 | coverage: none 27 | 28 | - name: Setup problem matchers 29 | run: | 30 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 31 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 32 | 33 | - name: Install dependencies 34 | run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction 35 | 36 | - name: Execute tests 37 | run: composer test 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | /node_modules 4 | /vendor 5 | .phpunit.result.cache 6 | .phpunit.cache/ 7 | composer.lock 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Laravel Auto Routes 2 | ``` 3 | _ _____ _ 4 | /\ | | | __ \ | | 5 | / \ _ _| |_ ___ ______| |__) |___ _ _| |_ ___ ___ 6 | / /\ \| | | | __/ _ \______| _ // _ \| | | | __/ _ \/ __| 7 | / ____ \ |_| | || (_) | | | \ \ (_) | |_| | || __/\__ \ 8 | /_/ \_\__,_|\__\___/ |_| \_\___/ \__,_|\__\___||___/ 9 | ``` 10 | [![Total Downloads](https://poser.pugx.org/izniburak/laravel-auto-routes/d/total.svg)](https://packagist.org/packages/izniburak/laravel-auto-routes) 11 | [![Latest Stable Version](https://poser.pugx.org/izniburak/laravel-auto-routes/v/stable.svg)](https://packagist.org/packages/izniburak/laravel-auto-routes) 12 | [![Latest Unstable Version](https://poser.pugx.org/izniburak/laravel-auto-routes/v/unstable.svg)](https://packagist.org/packages/izniburak/laravel-auto-routes) 13 | [![License](https://poser.pugx.org/izniburak/laravel-auto-routes/license.svg)](https://packagist.org/packages/izniburak/laravel-auto-routes) 14 | 15 | Automatically Route Generator & Discovery Package for Laravel. 16 | 17 | ## Features 18 | - All HTTP Methods which supported by Laravel 19 | - AJAX supported HTTP Methods (XMLHttpRequest) 20 | - Custom patterns for parameters with Regex 21 | - kebab-case and snake_case supported URLs 22 | - Livewire routes support [included Volt] _(in v2.x)_ 23 | 24 | ## Install 25 | Supported Laravel Versions: 26 | - v2.x: Laravel 10 and later 27 | - v1.x: Laravel 6 and later ([see the source](https://github.com/izniburak/laravel-auto-routes/tree/1.x)) 28 | 29 | Run the following command directly in your Project path: 30 | ```sh 31 | composer require izniburak/laravel-auto-routes 32 | ``` 33 | **OR** open your `composer.json` file and add the package like this: 34 | ```json 35 | { 36 | "require": { 37 | "izniburak/laravel-auto-routes": "^2.0" 38 | } 39 | } 40 | ``` 41 | after run the install command. 42 | ```sh 43 | composer install 44 | ``` 45 | 46 | The service provider of the Package will be **automatically discovered** by Laravel. 47 | 48 | After that, you should publish the config file via following command: 49 | 50 | ```sh 51 | php artisan vendor:publish --provider="Buki\AutoRoute\AutoRouteServiceProvider" 52 | ``` 53 | Greate! You can start to use **Auto Route** Package. 54 | 55 | ## Usage 56 | Open `web.php` or `api.php` files in `routes` directory, and add a new route that will be generated automatically: 57 | ```php 58 | Route::auto('/test', 'TestController'); 59 | ``` 60 | All methods will be automatically generated by the AutoRoute Package. 61 | 62 | ## Details 63 | 64 | - You can use `auto-route.php` file in `config` directory in order to change configuration of the Package. You can; 65 | * add new patterns for the parameters of the methods. 66 | * change default HTTP methods. 67 | * change main method. 68 | 69 | 70 | - You can use `Buki\AutoRoute\Facades\Route` in `web.php` and `api.php` in order to simple code completion for the method while using an IDE or Editor. 71 | You can add/replace the following line to top of the file: 72 | ```php 73 | // use Illuminate\Support\Facades\Route; 74 | use Buki\AutoRoute\Facades\Route; 75 | ``` 76 | - All methods which will be auto generated must have `public` accessor to discovered by the **AutoRoute** Package. 77 | 78 | ### Methods 79 | - If you use `camelCase` style for your method names in the Controllers, these methods endpoints will automatically convert to `kebab-case` to make pretty URLs. For example: 80 | ```php 81 | Route::auto('/test', 'TestController'); 82 | # OR 83 | Route::auto('/test', TestController::class); 84 | ``` 85 | ```php 86 | namespace App\Http\Controllers; 87 | 88 | use Illuminate\Http\Request; 89 | 90 | class TestController extends Controller 91 | { 92 | /** 93 | * URL will be converted to "/test/foo-bar" 94 | */ 95 | public function fooBar(Request $request) 96 | { 97 | // your codes 98 | } 99 | } 100 | ``` 101 | - You can specify HTTP Method for the method of the Controllers. If you want that a method works with `GET` method and other method works with `POST` method, you can do it. Just add a prefix for the method. That's all. For example; 102 | ```php 103 | namespace App\Http\Controllers; 104 | 105 | use Illuminate\Http\Request; 106 | 107 | class TestController extends Controller 108 | { 109 | /** 110 | * URL: "/test/foo-bar" 111 | * This method will only work with 'GET' method. 112 | */ 113 | public function getFooBar(Request $request) 114 | { 115 | // your codes 116 | } 117 | 118 | /** 119 | * URL: "/test/bar-baz" 120 | * This method will only work with 'POST' method. 121 | */ 122 | public function postBarBaz(Request $request) 123 | { 124 | // your codes 125 | } 126 | } 127 | ``` 128 | - If you don't add any prefix to your methods to use HTTP method definition, all URL will work with all HTTP methods. This options can be changed from `auto-route.php` configuration file. 129 | 130 | 131 | - If you want to use `snake_case` format for your methods, you can do it like that: 132 | ```php 133 | namespace App\Http\Controllers; 134 | 135 | use Illuminate\Http\Request; 136 | 137 | class TestController extends Controller 138 | { 139 | /** 140 | * URL: "/test/foo_bar" 141 | * This method will only work with 'GET' method. 142 | */ 143 | public function get_foo_bar(Request $request) 144 | { 145 | // your codes 146 | } 147 | 148 | /** 149 | * URL: "/test/bar_baz" 150 | * This method will only work with 'POST' method. 151 | */ 152 | public function post_bar_baz(Request $request) 153 | { 154 | // your codes 155 | } 156 | } 157 | ``` 158 | 159 | ### Ajax Supported Methods 160 | 161 | Also, you can add **AJAX supported** routes. For example; If you want to have a route which only access with GET method and XMLHttpRequest, you can define it simply. 162 | This package has some AJAX supported methods. These are; 163 | ``` 164 | XGET, XPOST, XPUT, XDELETE, XPATCH, XOPTIONS, XANY. 165 | ``` 166 | ```php 167 | namespace App\Http\Controllers; 168 | 169 | use Illuminate\Http\Request; 170 | 171 | class TestController extends Controller 172 | { 173 | /** 174 | * URL: "/test/foo" 175 | * This method will only work with 'GET' method and XMLHttpRequest. 176 | */ 177 | public function xgetFoo(Request $request) 178 | { 179 | // your codes 180 | } 181 | 182 | /** 183 | * URL: "/test/bar" 184 | * This method will only work with 'POST' method and XMLHttpRequest. 185 | */ 186 | public function xpostBar(Request $request) 187 | { 188 | // your codes 189 | } 190 | 191 | /** 192 | * URL: "/test/baz" 193 | * This method will work with any method and XMLHttpRequest. 194 | */ 195 | public function xanyBaz(Request $request) 196 | { 197 | // your codes 198 | } 199 | } 200 | ``` 201 | As you see, you need to add only `x` char as prefix to define the AJAX supported routes. 202 | If you want to support XMLHttpRequest and all HTTP methods which supported by Laravel, you can use `xany` prefix. 203 | 204 | For AJAX supported methods, the package will automatically add a middleware in order to check XMLHttpRequest for the routes. 205 | This middleware throws a `MethodNotAllowedException` exception. But, you can change this middleware from `auto-routes.php` file in `config` directory, if you want. 206 | 207 | ### Options 208 | 209 | - You can add route options via third parameter of the `auto` method. 210 | ```php 211 | Route::auto('/test', 'TestController', [ 212 | // your options... 213 | ]); 214 | ``` 215 | Options array may contain all Laravel route attributes like `name`, `middleware`, `namespace`, etc.. 216 | 217 | In addition, you can add `patterns` into the Options array in order to define new patterns for the parameters of the methods in the Controllers. For example: 218 | ```php 219 | Route::auto('/test', 'TestController', [ 220 | 'name' => 'test', 221 | 'middleware' => [YourMiddleware::class], 222 | 'patterns' => [ 223 | 'id' => '\d+', 224 | 'value' => '\w+', 225 | ], 226 | ]); 227 | ``` 228 | According to example above, you can use `$id` and `$value` parameters in all methods in the Controller. And for these parameters, the rules you defined will be applied. 229 | 230 | Also, to define default patterns for the parameters, you can modify `patterns` in `auto-route.php` file. 231 | 232 | - You can specify the Routes which will be generated automatically by using `only` or `except` with `options` parameters. You should use method names in the Controllers. For example; 233 | ```php 234 | # First Example 235 | Route::auto('/foo', 'FooController', [ 236 | 'only' => ['fooBar', 'postUpdatePost'], 237 | ]); 238 | 239 | # Second Example 240 | Route::auto('/bar', 'BarController', [ 241 | 'except' => ['test', 'putExample'], 242 | ]); 243 | ``` 244 | 245 | According to first example above, only two methods will be generated. And according to other example, all methods will be generated except two methods which specified. 246 | 247 | - If you don't change the `main_method` in configurations, your main method will be `index` for the Controllers. That's mean, you should be add `index` method into your controller to define base endpoint of the Controller. For example; 248 | ```php 249 | Route::auto('/test', 'TestController'); 250 | ``` 251 | ```php 252 | namespace App\Http\Controllers; 253 | 254 | use Illuminate\Http\Request; 255 | 256 | class TestController extends Controller 257 | { 258 | /** 259 | * URL: "/test" 260 | */ 261 | public function index(Request $request) 262 | { 263 | // your codes 264 | } 265 | 266 | /** 267 | * URL: "/test/foo-bar" 268 | */ 269 | public function fooBar(Request $request) 270 | { 271 | // your codes 272 | } 273 | } 274 | ``` 275 | ### Parameters 276 | - You can use parameters as `required` and `optional` for the methods in your Controllers. For example; 277 | ```php 278 | namespace App\Http\Controllers; 279 | 280 | use Illuminate\Http\Request; 281 | 282 | class TestController extends Controller 283 | { 284 | /** 285 | * URL: "/test/{id}" 286 | */ 287 | public function index(Request $request, $id) 288 | { 289 | // your codes 290 | } 291 | 292 | /** 293 | * URL: "/test/foo-bar/{name}/{surname?}" 294 | */ 295 | public function fooBar(Request $request, $name, $surname = null) 296 | { 297 | // your codes 298 | } 299 | } 300 | ``` 301 | Also, you can use parameter type to use compatible pattern for the parameter. Parameter types can be `int`, `string`, `float` and `bool`. For example: 302 | 303 | ```php 304 | namespace App\Http\Controllers; 305 | 306 | use Illuminate\Http\Request; 307 | 308 | class TestController extends Controller 309 | { 310 | /** 311 | * URL: "/test/{id}" 312 | * id parameter must be numeric. 313 | */ 314 | public function index(Request $request, int $id) 315 | { 316 | // your codes 317 | } 318 | 319 | /** 320 | * URL: "/test/foo-bar/{name}/{surname?}" 321 | * name and surname parameters must be string. 322 | */ 323 | public function fooBar(Request $request, string $name, string $surname = null) 324 | { 325 | // your codes 326 | } 327 | } 328 | ``` 329 | If you define patterns for these variable names in the `auto-route.php` configuration file, your definition will be used for the value checking. 330 | 331 | To use `int`, `float`, `string` and `bool` patterns quickly for your parameters, you can use parameter type directly. 332 | 333 | 334 | - You can use subfolder definition for the Controllers. For example; 335 | ```php 336 | Route::auto('/test', 'Backend.TestController'); 337 | # OR 338 | Route::auto('/test', 'Backend\\TestController'); 339 | ``` 340 | 341 | ## Livewire & Volt support 342 | You can define Livewire or Volt component routes directly in your controller by using Auto Routes package! 343 | For this, you should add new methods which have prefix `volt` or `wire`. That's it. Auto Routes package will automatically discover your Livewire routes and add them into the application routes. 344 | 345 | ```php 346 | namespace App\Http\Controllers; 347 | 348 | use Illuminate\Http\Request; 349 | 350 | class TestController extends Controller 351 | { 352 | /** 353 | * URL: "/test/foo" 354 | */ 355 | public function voltFoo(): string 356 | { 357 | // resources/views/livewire/pages/foo.blade.php 358 | return 'pages.foo'; 359 | } 360 | 361 | /** 362 | * URL: "/test/bar" 363 | */ 364 | public function wireBar(): string 365 | { 366 | return \App\Livewire\TestComponent::class; 367 | } 368 | } 369 | ``` 370 | As you see; for both methods, you must return a string value that Volt component path string or Livewire component class string. Now, you can access your Livewire components. 371 | 372 | 373 | ## Support 374 | You can use Issues 375 | 376 | [izniburak's homepage][author-url] 377 | 378 | [izniburak's twitter][twitter-url] 379 | 380 | ## Licence 381 | [MIT Licence][mit-url] 382 | 383 | ## Contributing 384 | 385 | 1. Fork it ( https://github.com/izniburak/laravel-auto-routes/fork ) 386 | 2. Create your feature branch (git checkout -b my-new-feature) 387 | 3. Commit your changes (git commit -am 'Add some feature') 388 | 4. Push to the branch (git push origin my-new-feature) 389 | 5. Create a new Pull Request 390 | 391 | ## Contributors 392 | 393 | - [izniburak](https://github.com/izniburak) İzni Burak Demirtaş - creator, maintainer 394 | 395 | [mit-url]: http://opensource.org/licenses/MIT 396 | [author-url]: https://buki.dev 397 | [twitter-url]: https://twitter.com/izniburak 398 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "izniburak/laravel-auto-routes", 3 | "description": "Auto Route Generating (Auto-Discovery) Package for Laravel", 4 | "keywords": [ 5 | "auto-route", 6 | "laravel", 7 | "route", 8 | "router", 9 | "route-generator", 10 | "route-discovery" 11 | ], 12 | "type": "library", 13 | "license": "MIT", 14 | "homepage": "https://github.com/izniburak/laravel-auto-routes", 15 | "authors": [ 16 | { 17 | "name": "İzni Burak Demirtaş", 18 | "email": "info@burakdemirtas.org", 19 | "homepage": "https://buki.dev" 20 | } 21 | ], 22 | "require": { 23 | "php": "^8.1" 24 | }, 25 | "require-dev": { 26 | "illuminate/routing": "^10.0|^11.0", 27 | "phpunit/phpunit": "^10.1" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Buki\\AutoRoute\\": "src/" 32 | } 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Buki\\Tests\\": "tests/" 37 | } 38 | }, 39 | "scripts": { 40 | "test": "vendor/bin/phpunit", 41 | "coverage": "vendor/bin/phpunit --coverage-html coverage" 42 | }, 43 | "extra": { 44 | "laravel": { 45 | "providers": [ 46 | "Buki\\AutoRoute\\AutoRouteServiceProvider" 47 | ], 48 | "aliases": { 49 | "AutoRoute": "Buki\\AutoRoute\\AutoRouteFacade" 50 | } 51 | } 52 | }, 53 | "minimum-stability": "dev", 54 | "prefer-stable": true 55 | } 56 | -------------------------------------------------------------------------------- /config/auto-route.php: -------------------------------------------------------------------------------- 1 | 'App\\Http\\Controllers', 12 | 13 | /* 14 | |-------------------------------------------------------------------------- 15 | | Main Method 16 | |-------------------------------------------------------------------------- 17 | | Main method for the Controllers. This method name will be used to 18 | | define main endpoint for the Controllers. 19 | */ 20 | 'main_method' => 'index', 21 | 22 | /* 23 | |-------------------------------------------------------------------------- 24 | | Default HTTP Method(s) 25 | |-------------------------------------------------------------------------- 26 | | Default HTTP methods for the routes which will be generated from 27 | | method name which not specified the HTTP method. 28 | | You can define multiple methods by using an array. 29 | | 30 | | type: array 31 | | null or '*': all methods that supported by Laravel 32 | */ 33 | 'http_methods' => '*', 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Parameter Patterns 38 | |-------------------------------------------------------------------------- 39 | | You can define more new patterns for the all parameters that 40 | | you'll use at methods of the Controllers. Parameters that do not match 41 | | any pattern will accept all values. 42 | | 43 | | Format: $variable => pattern 44 | | Example: 'id' => '(\d+)' 45 | */ 46 | 'patterns' => [ 47 | 'slug' => '([\w\-_]+)', 48 | 'uuid' => '([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', 49 | 'date' => '([0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]))', 50 | ], 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | AJAX Middleware Class 55 | |-------------------------------------------------------------------------- 56 | | The middleware class that check AJAX request for your methods 57 | | which starts with 'x' char in your Controller file. 58 | | For example: xgetFoo, xpostBar, xanyBaz. 59 | | If you have any method in your controller like above, this middleware 60 | | will be triggered while trying to access your route. 61 | | 62 | | Default: \Buki\AutoRoute\Middleware\AjaxRequestMiddleware::class 63 | */ 64 | // 'ajax_middleware' => App\\Http\\Middleware\\YourMiddleware::class, 65 | 66 | ]; 67 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | tests/ 7 | 8 | 9 | 10 | 11 | src/ 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/AutoRoute.php: -------------------------------------------------------------------------------- 1 | 21 | * @web https://buki.dev 22 | */ 23 | class AutoRoute 24 | { 25 | protected Router $router; 26 | 27 | protected string $namespace; 28 | 29 | protected array $availableMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']; 30 | 31 | protected array $customMethods = ['XGET', 'XPOST', 'XPUT', 'XPATCH', 'XDELETE', 'XOPTIONS', 'XANY', 'VOLT', 'WIRE']; 32 | 33 | protected string $mainMethod; 34 | 35 | protected string|array $defaultHttpMethods; 36 | 37 | protected string $ajaxMiddleware; 38 | 39 | protected array $defaultPatterns = [ 40 | ':any' => '([^/]+)', 41 | ':int' => '(\d+)', 42 | ':float' => '[+-]?([0-9]*[.])?[0-9]+', 43 | ':bool' => '(true|false|1|0)', 44 | ]; 45 | 46 | /** 47 | * AutoRoute constructor. 48 | * @throws 49 | */ 50 | public function __construct(protected Container $app) 51 | { 52 | $this->router = $app->get('router'); 53 | } 54 | 55 | /** 56 | * Initialize for the AutoRoute 57 | */ 58 | public function setConfigurations(array $config): void 59 | { 60 | $this->mainMethod = $config['main_method'] ?? 'index'; 61 | $this->namespace = $config['namespace'] ?? 'App\\Http\\Controllers'; 62 | $this->ajaxMiddleware = $config['ajax_middleware'] ?? AjaxRequestMiddleware::class; 63 | $this->defaultPatterns = array_merge($this->defaultPatterns, $config['patterns'] ?? []); 64 | $this->defaultHttpMethods = $config['http_methods'] ?? $this->availableMethods; 65 | 66 | if (empty($this->defaultHttpMethods) || $this->defaultHttpMethods === '*') { 67 | $this->defaultHttpMethods = $this->availableMethods; 68 | } 69 | } 70 | 71 | /** 72 | * @throws 73 | */ 74 | public function auto(string $prefix, string $controller, array $options = []): void 75 | { 76 | $only = $options['only'] ?? []; 77 | $except = $options['except'] ?? []; 78 | $patterns = $options['patterns'] ?? []; 79 | 80 | $routeName = trim($options['as'] ?? ($options['name'] ?? trim($prefix, '/')), '.') . '.'; 81 | if ($routeName === '.') { 82 | $routeName = ''; 83 | } 84 | 85 | $this->router->group( 86 | array_merge($options, ['prefix' => $prefix, 'as' => $routeName]), 87 | function () use ($controller, $only, $except, $patterns) { 88 | [$class, $className] = $this->resolveControllerName($controller); 89 | $classRef = new ReflectionClass($class); 90 | foreach ($classRef->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { 91 | // Check the method should be added into Routes or not. 92 | if (in_array($method->class, [BaseController::class, "{$this->namespace}\\Controller"]) 93 | || ($method->getDeclaringClass()->getParentClass() && $method->getDeclaringClass()->getParentClass()->getName() === BaseController::class) 94 | || !$method->isPublic() 95 | || str_starts_with($method->name, '__')) { 96 | continue; 97 | } 98 | 99 | // Needed definitions 100 | $methodName = $method->name; 101 | 102 | if ((!empty($only) && !in_array($methodName, $only)) 103 | || (!empty($except) && in_array($methodName, $except))) { 104 | continue; 105 | } 106 | 107 | // Find the HTTP method which will be used and method name. 108 | [$httpMethods, $path, $middleware] = $this->getHttpMethodAndName($methodName); 109 | 110 | // Get endpoints and parameter patterns for Route 111 | [$endpoints, $routePatterns] = $this->getRouteValues($method, $patterns); 112 | 113 | $endpoint = implode('/', $endpoints); 114 | 115 | $handler = [$classRef->getName(), $method->name]; 116 | $routePath = ($path !== $this->mainMethod ? $path : '') . "/{$endpoint}"; 117 | 118 | // for volt 119 | if (str_starts_with($method->name, 'volt')) { 120 | if (class_exists(Volt::class) && $method->getReturnType()?->getName() === 'string') { 121 | Volt::route($routePath, $method->invoke(new ($classRef->getName()), ...$endpoints)) 122 | ->where($routePatterns)->name("{$method->name}")->middleware($middleware); 123 | } 124 | 125 | continue; 126 | } 127 | 128 | // for livewire 129 | if (str_starts_with($method->name, 'wire')) { 130 | if (!(class_exists(Component::class) && $method->getReturnType()?->getName() === 'string')) { 131 | continue; 132 | } 133 | 134 | $handler = $method->invoke(new ($classRef->getName()), ...$endpoints); 135 | if (!is_subclass_of($handler, Component::class)) { 136 | continue; 137 | } 138 | } 139 | 140 | $this->router 141 | ->addRoute(array_map(fn ($method) => strtoupper($method), $httpMethods), $routePath, $handler) 142 | ->where($routePatterns)->name("{$method->name}")->middleware($middleware); 143 | } 144 | } 145 | ); 146 | } 147 | 148 | private function getHttpMethodAndName(string $controllerMethod): array 149 | { 150 | $httpMethods = $this->defaultHttpMethods; 151 | $middleware = null; 152 | foreach (array_merge($this->availableMethods, $this->customMethods) as $method) { 153 | $method = strtolower($method); 154 | if (stripos($controllerMethod, $method, 0) === 0) { 155 | if (in_array($method, ['volt', 'wire'])) { 156 | $httpMethods = ['GET', 'HEAD']; 157 | } elseif ($method !== 'xany') { 158 | $httpMethods = [ltrim($method, 'x')]; 159 | } 160 | 161 | $middleware = str_starts_with($method, 'x') ? $this->ajaxMiddleware : null; 162 | $controllerMethod = lcfirst( 163 | preg_replace('/' . $method . '_?/i', '', $controllerMethod, 1) 164 | ); 165 | break; 166 | } 167 | } 168 | 169 | // Convert URL from camelCase to snake-case. 170 | $controllerMethod = strtolower(preg_replace('%([a-z]|[0-9])([A-Z])%', '\1-\2', $controllerMethod)); 171 | 172 | return [$httpMethods, $controllerMethod, $middleware]; 173 | } 174 | 175 | private function getRouteValues(ReflectionMethod $method, array $patterns = []): array 176 | { 177 | $routePatterns = $endpoints = []; 178 | $patterns = array_merge($this->defaultPatterns, $patterns); 179 | foreach ($method->getParameters() as $param) { 180 | $paramName = $param->getName(); 181 | $typeHint = $param->hasType() ? $param->getType()->getName() : null; 182 | 183 | if (!$this->isValidRouteParam($typeHint)) { 184 | continue; 185 | } 186 | 187 | $routePatterns[$paramName] = $patterns[$paramName] ?? 188 | ($this->defaultPatterns[":{$typeHint}"] ?? $this->defaultPatterns[':any']); 189 | $endpoints[] = $param->isOptional() ? "{{$paramName}?}" : "{{$paramName}}"; 190 | } 191 | 192 | return [$endpoints, $routePatterns]; 193 | } 194 | 195 | private function resolveControllerName(string $controller): array 196 | { 197 | $controller = str_replace(['.', $this->namespace], ['\\', ''], $controller); 198 | return [ 199 | $this->namespace . "\\" . trim($controller, "\\"), 200 | $controller, 201 | ]; 202 | } 203 | 204 | private function isValidRouteParam(?string $type): bool 205 | { 206 | if (is_null($type) || in_array($type, ['int', 'float', 'string', 'bool', 'mixed'])) { 207 | return true; 208 | } 209 | 210 | if (class_exists($type) && is_subclass_of($type, Model::class)) { 211 | return true; 212 | } 213 | 214 | if (function_exists('enum_exists') && enum_exists($type)) { 215 | return true; 216 | } 217 | 218 | return false; 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/AutoRouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class AutoRouteServiceProvider extends ServiceProvider 15 | { 16 | /** 17 | * Bootstrap the services. 18 | * 19 | * @return void 20 | */ 21 | public function boot(): void 22 | { 23 | $this->publishes([ 24 | $this->configPath() => config_path('auto-route.php'), 25 | ], 'auto-route'); 26 | } 27 | 28 | /** 29 | * Register the services. 30 | * 31 | * @return void 32 | */ 33 | public function register(): void 34 | { 35 | $this->mergeConfigFrom($this->configPath(), 'auto-route'); 36 | $this->app->singleton(AutoRoute::class, function ($app) { 37 | $autoRouter = new AutoRoute($app); 38 | $autoRouter->setConfigurations($app['config']->get('auto-route')); 39 | return $autoRouter; 40 | }); 41 | 42 | /** @var Router $router */ 43 | $router = $this->app['router']; 44 | $autoRoute = $this->app[AutoRoute::class]; 45 | $router->macro('auto', function (string $prefix, string $controller, array $options = []) use ($autoRoute) { 46 | return $autoRoute->auto($prefix, $controller, $options); 47 | }); 48 | } 49 | 50 | /** 51 | * @return string 52 | */ 53 | protected function configPath(): string 54 | { 55 | return __DIR__ . '/../config/auto-route.php'; 56 | } 57 | 58 | /** 59 | * Get the services provided by the provider. 60 | * 61 | * @return array 62 | */ 63 | public function provides(): array 64 | { 65 | return [AutoRoute::class]; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Facades/Route.php: -------------------------------------------------------------------------------- 1 | 10 | * 11 | * @method static void auto(string $prefix, string $controller, array $options = []) 12 | */ 13 | class Route extends \Illuminate\Support\Facades\Route 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /src/Middleware/AjaxRequestMiddleware.php: -------------------------------------------------------------------------------- 1 | ajax() || $request->pjax())) { 21 | throw new MethodNotAllowedException( 22 | ['XMLHttpRequest'], 23 | "You cannot use this route without XMLHttpRequest." 24 | ); 25 | } 26 | 27 | return $next($request); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/AutoRouteTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 22 | } 23 | } 24 | --------------------------------------------------------------------------------