├── package-banner.png ├── CHANGELOG.md ├── resources ├── replacements │ ├── broadcast-service-provider.stub │ └── console-kernel.stub └── stubs │ ├── route-registrar.stub │ ├── web-default-route-registrar.stub │ ├── api-default-route-registrar.stub │ ├── route-registrar.children.stub │ ├── web-route-registrar.stub │ ├── api-route-registrar.stub │ └── route-service-provider.stub ├── src ├── Contracts │ └── RouteRegistrar.php ├── RouteRegistrarServiceProvider.php ├── Commands │ ├── RouteRegistrarMakeCommand.php │ └── InitRoutingCommand.php └── Concerns │ └── MapsRouteRegistrars.php ├── composer.json ├── LICENSE.md └── README.md /package-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/olliecodes/laravel-route-registrars/HEAD/package-banner.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | 4 | All notable changes to this project will be documented in this file. 5 | 6 | 7 | ## [1.0.0](https://github.com/olliecodes/laravel-route-registrars/compare/de39e1b20e6aae11e6cbf492b07b405c2f13c59f...v1.0.0) (2022-08-08) 8 | 9 | --- -------------------------------------------------------------------------------- /resources/replacements/broadcast-service-provider.stub: -------------------------------------------------------------------------------- 1 | get('/', function () { 20 | return view('welcome'); 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Contracts/RouteRegistrar.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 21 | return $request->user(); 22 | })->middleware('auth:sanctum'); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/RouteRegistrarServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 18 | $this->commands([ 19 | InitRoutingCommand::class, 20 | RouteRegistrarMakeCommand::class, 21 | ]); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /resources/stubs/route-registrar.children.stub: -------------------------------------------------------------------------------- 1 | [] 15 | */ 16 | protected array $registrars = [ 17 | ]; 18 | 19 | /** 20 | * Map the routes this class represents. 21 | * 22 | * @param \Illuminate\Contracts\Routing\Registrar $registrar 23 | * 24 | * @return void 25 | */ 26 | public function map(Registrar $registrar): void 27 | { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/replacements/console-kernel.stub: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 19 | } 20 | 21 | /** 22 | * Register the commands for the application. 23 | * 24 | * @return void 25 | */ 26 | protected function commands() 27 | { 28 | $this->load(__DIR__.'/Commands'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/stubs/web-route-registrar.stub: -------------------------------------------------------------------------------- 1 | [] 16 | */ 17 | protected array $registrars = [ 18 | Web\DefaultRoutes::class 19 | ]; 20 | 21 | /** 22 | * Map the routes this class represents. 23 | * 24 | * @param \Illuminate\Contracts\Routing\Registrar $registrar 25 | * 26 | * @return void 27 | */ 28 | public function map(Registrar $registrar): void 29 | { 30 | $registrar->group([ 31 | 'middleware' => 'web', 32 | ], function (Registrar $registrar) { 33 | $this->mapRouteRegistrars($registrar, $this->registrars); 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "olliecodes/laravel-route-registrars", 3 | "description": "Adds route registrars as an alternative object based approach to registering routes.", 4 | "type": "library", 5 | "license": "MIT", 6 | "minimum-stability": "stable", 7 | "authors": [ 8 | { 9 | "name": "Ollie Read", 10 | "email": "code@ollie.codes" 11 | } 12 | ], 13 | "require": { 14 | "php": "^8.0", 15 | "laravel/framework": "^9.0" 16 | }, 17 | "require-dev": { 18 | "marcocesarato/php-conventional-changelog": "^1.10" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "OllieCodes\\Registrars\\": "src/" 23 | } 24 | }, 25 | "scripts": { 26 | "changelog": "conventional-changelog --config=.changelog" 27 | }, 28 | "extra": { 29 | "laravel": { 30 | "providers": [ 31 | "OllieCodes\\Registrars\\RouteRegistrarServiceProvider" 32 | ], 33 | "aliases": [] 34 | } 35 | }, 36 | "version": "1.0.0" 37 | } -------------------------------------------------------------------------------- /resources/stubs/api-route-registrar.stub: -------------------------------------------------------------------------------- 1 | [] 16 | */ 17 | protected array $registrars = [ 18 | Api\DefaultRoutes::class 19 | ]; 20 | 21 | /** 22 | * Map the routes this class represents. 23 | * 24 | * @param \Illuminate\Contracts\Routing\Registrar $registrar 25 | * 26 | * @return void 27 | */ 28 | public function map(Registrar $registrar): void 29 | { 30 | $registrar->group([ 31 | 'middleware' => 'api', 32 | 'prefix' => 'api' 33 | ], function (Registrar $registrar) { 34 | $this->mapRouteRegistrars($registrar, $this->registrars); 35 | }); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © `2022` `ollieread aka Ollie Read, aka Ollie Codes` 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 7 | documentation files (the “Software”), to deal in the Software without restriction, including without limitation the 8 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 9 | persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 12 | Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 15 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 17 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /resources/stubs/route-service-provider.stub: -------------------------------------------------------------------------------- 1 | [] 19 | */ 20 | protected array $registrars = [ 21 | Routes\WebRoutes::class, 22 | Routes\ApiRoutes::class 23 | ]; 24 | 25 | /** 26 | * Define your route model bindings, pattern filters, and other route configuration. 27 | * 28 | * @return void 29 | */ 30 | public function boot(): void 31 | { 32 | $this->configureRateLimiting(); 33 | 34 | $this->routes(function (Registrar $router) { 35 | $this->mapRouteRegistrars($router, $this->registrars); 36 | }); 37 | } 38 | 39 | /** 40 | * Configure the rate limiters for the application. 41 | * 42 | * @return void 43 | */ 44 | protected function configureRateLimiting() 45 | { 46 | RateLimiter::for('api', function (Request $request) { 47 | return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Commands/RouteRegistrarMakeCommand.php: -------------------------------------------------------------------------------- 1 | option('hasChildren')) { 31 | return __DIR__ . '/../../resources/stubs/route-registrar.children.stub'; 32 | } 33 | 34 | return __DIR__ . '/../../resources/stubs/route-registrar.stub'; 35 | } 36 | 37 | /** 38 | * Get the default namespace for the class. 39 | * 40 | * @param string $rootNamespace 41 | * 42 | * @return string 43 | */ 44 | protected function getDefaultNamespace($rootNamespace): string 45 | { 46 | $namespace = $rootNamespace . '\\Http\\Routes'; 47 | 48 | if ($this->option('web')) { 49 | $namespace .= '\\Web'; 50 | } else if ($this->option('api')) { 51 | $namespace .= '\\Api'; 52 | } 53 | 54 | return $namespace; 55 | } 56 | } -------------------------------------------------------------------------------- /src/Commands/InitRoutingCommand.php: -------------------------------------------------------------------------------- 1 | confirmToProceed()) { 21 | return self::FAILURE; 22 | } 23 | 24 | $this->warn('This command will overwrite the following file:'); 25 | $this->line('app/Providers/RouteServiceProvider.php'); 26 | 27 | if (! $this->confirm('Are you sure you wish to proceed?')) { 28 | return self::FAILURE; 29 | } 30 | 31 | $this->overwriteRouteServiceProvider(); 32 | $this->createDirectories(); 33 | $this->addDefaultRoutes(); 34 | 35 | return self::SUCCESS; 36 | } 37 | 38 | protected function getStub(): string 39 | { 40 | return ''; 41 | } 42 | 43 | private function getReplacement(string $stubName, string $name): string 44 | { 45 | $stub = $this->files->get($stubName); 46 | $this->replaceNamespace($stub, $name); 47 | return $this->sortImports($stub); 48 | } 49 | 50 | private function addDefaultRoutes(): void 51 | { 52 | $defaultRoutes = [ 53 | 'WebRoutes' => 'web-route-registrar.stub', 54 | 'Web\\DefaultRoutes' => 'web-default-route-registrar.stub', 55 | 'ApiRoutes' => 'api-route-registrar.stub', 56 | 'Api\\DefaultRoutes' => 'api-default-route-registrar.stub', 57 | ]; 58 | 59 | foreach ($defaultRoutes as $class => $stub) { 60 | if ($this->createClass('Http\\Routes\\' . $class, $stub)) { 61 | $this->info('Route registrar \'' . $class . '\' written'); 62 | } else { 63 | $this->error('Unable to write \'' . $class . '\' class'); 64 | } 65 | } 66 | } 67 | 68 | private function createDirectories(): void 69 | { 70 | $directories = [ 71 | app_path('Http/Routes'), 72 | app_path('Http/Routes/Web'), 73 | app_path('Http/Routes/Api') 74 | 75 | ]; 76 | 77 | foreach ($directories as $directory) { 78 | if (! $this->files->isDirectory($directory)) { 79 | $this->files->makeDirectory($directory); 80 | $this->info('Created directory: ' . $directory); 81 | } 82 | } 83 | } 84 | 85 | private function overwriteRouteServiceProvider(): void 86 | { 87 | if ($this->createClass('Providers\\RouteServiceProvider', 'route-service-provider.stub')) { 88 | $this->info('Route service provider replaced'); 89 | } 90 | } 91 | 92 | private function createClass(string $class, string $stub): bool 93 | { 94 | return $this->files->put( 95 | app_path(str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php'), 96 | $this->getReplacement( 97 | __DIR__ . '/../../resources/stubs/' . $stub, 98 | $class 99 | ) 100 | ) !== false; 101 | } 102 | } -------------------------------------------------------------------------------- /src/Concerns/MapsRouteRegistrars.php: -------------------------------------------------------------------------------- 1 | make($routeRegistrar); 34 | } 35 | 36 | /** 37 | * Map the routes. 38 | * 39 | * @param \Illuminate\Contracts\Routing\Registrar $router 40 | * @param class-string<\OllieCodes\Registrars\Contracts\RouteRegistrar>[] $registrars 41 | * 42 | * @return void 43 | * 44 | * @throws \Illuminate\Contracts\Container\BindingResolutionException 45 | */ 46 | protected function mapRouteRegistrars(Registrar $router, array $registrars): void 47 | { 48 | foreach ($registrars as $registrar) { 49 | $this->mapRouteRegistrar($router, $registrar); 50 | } 51 | } 52 | 53 | /** 54 | * Map a route registrar. 55 | * 56 | * @param \Illuminate\Contracts\Routing\Registrar $router 57 | * @param string $routeRegistrar 58 | * 59 | * @return void 60 | * 61 | * @throws \Illuminate\Contracts\Container\BindingResolutionException 62 | */ 63 | protected function mapRouteRegistrar(Registrar $router, string $routeRegistrar): void 64 | { 65 | if (! is_subclass_of($routeRegistrar, RouteRegistrar::class)) { 66 | throw new RuntimeException(sprintf( 67 | 'Cannot map routes \'%s\', it is not a valid routes class', 68 | $routeRegistrar 69 | )); 70 | } 71 | 72 | $this->makeRouteRegistrar($routeRegistrar) 73 | ->map($router); 74 | } 75 | 76 | /** 77 | * Load route registrars from the provided paths. 78 | * 79 | * @param \Illuminate\Contracts\Routing\Registrar $router 80 | * @param array $paths 81 | * 82 | * @return void 83 | * 84 | * @throws \Illuminate\Contracts\Container\BindingResolutionException 85 | * @throws \ReflectionException 86 | */ 87 | protected function loadRouteRegistrars(Registrar $router, array $paths): void 88 | { 89 | $paths = array_unique($paths); 90 | $paths = array_filter($paths, 'is_dir'); 91 | 92 | if (empty($paths)) { 93 | return; 94 | } 95 | 96 | $namespace = app()->getNamespace(); 97 | 98 | foreach ((new Finder)->in($paths)->files() as $registrar) { 99 | $registrar = $namespace . str_replace( 100 | ['/', '.php'], 101 | ['\\', ''], 102 | Str::after($registrar->getRealPath(), realpath(app_path()) . DIRECTORY_SEPARATOR) 103 | ); 104 | 105 | if ( 106 | is_subclass_of($registrar, RouteRegistrar::class) 107 | && ! (new ReflectionClass($registrar))->isAbstract() 108 | ) { 109 | $this->mapRouteRegistrar($router, $registrar); 110 | } 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A Laravel package that introduces a clean object based alternative to Laravel route files. 2 | 3 | ![Packagist Version](https://img.shields.io/packagist/v/olliecodes/laravel-route-registrars) 4 | ![Packagist PHP Version Support](https://img.shields.io/packagist/php-v/olliecodes/laravel-route-registrars) 5 | ![GitHub](https://img.shields.io/github/license/olliecodes/laravel-route-registrars) 6 | [![codecov](https://codecov.io/gh/olliecodes/laravel-route-registrars/branch/main/graph/badge.svg?token=FHJ41NQMTA)](https://codecov.io/gh/olliecodes/laravel-route-registrars) 7 | 8 | ## Laravel Route Registrars 9 | This package introduces a clean object based way to define your routes in a Laravel application. A tutorial on the basic premise exists on [Laravel news](https://laravel-news.com/route-registrars), written by [@juststeveking](https://twitter.com/JustSteveKing). 10 | 11 | ## Install 12 | 13 | Install via composer. 14 | 15 | ```bash 16 | $ composer require olliecodes/laravel-route-registrars 17 | ``` 18 | 19 | ### Fresh Laravel Installation 20 | 21 | If you're installing this package on a fresh laravel installation, you'll want to run the following command before you do anything with your routes. 22 | 23 | ```bash 24 | php artisan init:routing 25 | ``` 26 | 27 | This will overwrite the `app/Providers/RouteServiceProvider.php` file with one compatible with the route registrars, and create default route registrars to replace both `routes/web.php` and `routes/api.php`. 28 | 29 | ### Existing Laravel Installation 30 | 31 | If you have an existing application, it is recommended that you read through the Laravel News article that covers this approach, as you're going to have to manually refactor your route service provider and route files. 32 | 33 | ### Requirements 34 | 35 | This package requires the following; 36 | 37 | - PHP >= 8.0 (Including 8). 38 | - `laravel/framework` >= 9.0 39 | 40 | ## Usage 41 | 42 | To register new routes, you can either add their definitions to the `map` method of a `RouteRegistrar`, or create a new one. 43 | 44 | ### Creating a new registrar 45 | 46 | The following command will create a new registrar inside `app/Http/Routes`. 47 | 48 | ```bash 49 | php artisan make:registrar {name} 50 | ``` 51 | 52 | The `{name}` can be any valid class name, or sub-namespace. For example, using `Auth\\GuestRoutes` will create `app/Http/Routes/Auth/GuestRoutes.php`. 53 | 54 | There are several options available when creating a registrar. 55 | 56 | #### `--C|hasChildren` 57 | 58 | Create a route registrar that has the `MapRouteRegistrars` trait so that it can map child registrars. 59 | 60 | #### `--W|web` 61 | 62 | Create the route registrar in `app/Http/Routes/Web`, an option left in for those of you that are splitting the Web and API routes. 63 | 64 | #### `--A|API` 65 | 66 | Exactly the same as the above option, except it creates it in `app/Http/Routes/Api`, 67 | 68 | ### Registering Registrars 69 | 70 | Any class that wants to register registrars can use the following trait. 71 | 72 | ``` 73 | OllieCodes\Registrars\Concerns\MapsRouteRegistrars 74 | ``` 75 | 76 | Once doing so, there are three ways to register. All of them require an instance of the following class. 77 | 78 | ``` 79 | Illuminate\Contracts\Routing\Registrar 80 | ``` 81 | 82 | This interface is implemented by the Laravel `Router` class, so you can use that. 83 | 84 | #### Register multiple 85 | Call the following method with an instance of the laravel router, and an array of fully qualified class names for the registrars. 86 | 87 | ``` 88 | mapRouteRegistrars(Registrar $router, array $registrars) 89 | ``` 90 | 91 | #### Register one 92 | Call the following method with an instance of the laravel router, and the fully qualified class name of the registrars. 93 | 94 | ``` 95 | mapRouteRegistrar(Registrar $router, string $registrar) 96 | ``` 97 | 98 | #### Register from paths 99 | Call the following method with an instance of the laravel router, and an array of paths where the registrars are held. 100 | 101 | This method will use the order that the files are returned, so please keep route order precedence in mind. 102 | 103 | ``` 104 | loadRouteRegistrars(Registrar $router, array $paths) 105 | ``` --------------------------------------------------------------------------------