Created
July 2, 2022 06:32
-
-
Save finagin/6cd80497bea2eb3563124a21bacd9027 to your computer and use it in GitHub Desktop.
Attribute Routing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Services; | |
use App\Attributes\Route as RouteAttribute; | |
use Illuminate\Routing\Router; | |
use Illuminate\Support\Facades\Route as RouteFacade; | |
use Illuminate\Support\Str; | |
use ReflectionClass; | |
use ReflectionMethod; | |
class AttributeRouts | |
{ | |
public const BASE_CONTROLLER = \App\Http\Controllers\Controller::class; | |
public static function scan(Router $router) | |
{ | |
foreach (static::getIterator() as $file) { | |
/** @var \SplFileInfo $file */ | |
if ($file->isDir()) { | |
continue; | |
} | |
$class = static::getClass($file); | |
$controller = new ReflectionClass($class); | |
$router = static::deep($controller); | |
$router(function (Router $router) use ($class, $controller) { | |
$classAttributes = static::getAttributes($controller); | |
$router->group($classAttributes, function ($router) use ($class, $controller, $classAttributes) { | |
if (! $controller->isAbstract() && array_key_exists('method', $classAttributes)) { | |
$router->match($classAttributes['method'], '/', $class); | |
} | |
foreach ($controller->getMethods() as $method) { | |
$methodAttributes = static::getAttributes($method); | |
if (array_key_exists('method', $methodAttributes)) { | |
$router->group($methodAttributes, | |
fn(Router $router) => $router->match( | |
$methodAttributes['method'], | |
'/', | |
$class.'@'.$method->getName() | |
)); | |
} | |
} | |
}); | |
}); | |
} | |
} | |
private static function getIterator(): iterable | |
{ | |
return new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(app_path('Http/Controllers'))); | |
} | |
public static function getClass(\SplFileInfo $file): string | |
{ | |
return Str::replace([app_path(), '/', '.php'], ['App', '\\', ''], $file->getPathname()); | |
} | |
public static function deep(ReflectionClass $controller) | |
{ | |
if ($controller->isAbstract() || $controller->getName() === static::BASE_CONTROLLER) { | |
return fn($routes) => RouteFacade::group(static::getAttributes($controller, false), $routes); | |
} | |
return static::deep($controller->getParentClass()); | |
} | |
public static function getAttributes(ReflectionClass|ReflectionMethod $controller, bool $normalize = true) | |
{ | |
$attributes = collect($controller->getAttributes(RouteAttribute::class)); | |
$arguments = collect(optional($attributes->first())->getArguments()); | |
return $normalize | |
? static::normalizeAttributes($arguments->toArray(), $controller) | |
: $arguments->toArray(); | |
} | |
public static function normalizeAttributes(array $attributes, ReflectionClass|ReflectionMethod $attributable) | |
{ | |
$slug = Str::slug(class_basename($attributable->getName())); | |
$attributes['path'] ??= $slug; | |
$attributes['as'] ??= $slug; | |
$attributes['prefix'] ??= $attributes['path']; | |
$attributes['excluded_middleware'] ??= $attributes['withoutMiddleware'] ?? []; | |
return $attributes; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Attributes; | |
use Attribute; | |
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] | |
class Route | |
{ | |
public array $args; | |
public function __construct( | |
string $method = '', | |
string $path = '', | |
string $as = '', | |
string $prefix = '', | |
string $domain = '', | |
array $where = [], | |
array $middleware = [], | |
array $withoutMiddleware = [], | |
) { | |
$this->args = func_get_args(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Providers; | |
use App\Attributes\Route as RouteAttribute; | |
use Illuminate\Cache\RateLimiting\Limit; | |
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; | |
use Illuminate\Http\Request; | |
use Illuminate\Routing\Router; | |
use Illuminate\Support\Facades\RateLimiter; | |
use Illuminate\Support\Facades\Route; | |
use Illuminate\Support\Str; | |
use RecursiveDirectoryIterator; | |
use RecursiveIteratorIterator; | |
use ReflectionClass; | |
class RouteServiceProvider extends ServiceProvider | |
{ | |
/** | |
* The path to the "home" route for your application. | |
* | |
* Typically, users are redirected here after authentication. | |
* | |
* @var string | |
*/ | |
public const HOME = '/home'; | |
/** | |
* Define your route model bindings, pattern filters, and other route configuration. | |
* | |
* @return void | |
*/ | |
public function boot() | |
{ | |
$this->configureRateLimiting(); | |
Route::middleware(null) | |
->group(fn(Router $router) => \App\Services\AttributeRouts::scan($router)); | |
Route::middleware('api') | |
->prefix('api') | |
->group(base_path('routes/api.php')); | |
Route::middleware('web') | |
->group(base_path('routes/web.php')); | |
} | |
/** | |
* Configure the rate limiters for the application. | |
* | |
* @return void | |
*/ | |
protected function configureRateLimiting() | |
{ | |
RateLimiter::for('api', function (Request $request) { | |
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment