Атрибуты, новая фича PHP 8, также известные как аннотации, добавляют метаданные в классы, методы и переменные. Используя их, мы можем избежать создания дополнительных файлов маршрутизации и все необходимое сразу задать в одном контроллере, повысив читаемость кода. Нам не понадобятся никакие дополнительные пакеты, важна только версия языка.
Обычный код без атрибутов:
// on web.php Route::prefix('api')->delete('admin/auth', [MyController::class, 'delete'])->middleware('auth:admin'); // on Controller class MyController extends Controller { public function logout(Request $request): Response { /** @var $admin Admin */ $admin = Auth::guard('admin')->user(); $admin->logout(); return response(null, Response::HTTP_NO_CONTENT); } }
После применения атрибутов
// Just on the Controller only class MyController extends Controller { #[Route (method: 'delete', path: 'api/admin/auth', middlewares: ['auth:admin'])] public function logout(Request $request): Response { /** @var $admin Admin */ $admin = Auth::quard('admin')->user(); $admin->logout(); return response(null, Response::HTTP_NO_CONTENT); } }
Давайте разбираться как всё это работает. Прежде всего рекомендую прочесть официальную документацию по PHP атрибутам. Если вы уже разобрались, то идём дальше.
Зададим наш новый атрибут Route
. Создаём новую папку Attributes
в каталоге app
. Затем создаём файл атрибута с названием Route.php
.
namespace App\Attributes; use Attribute; #[Attribute] class Route { public array $args; public function __construct(string $method, string $path, array $middlewares = []) { $this->args = func_get_args(); } }
Теперь обновим логику загрузки маршрутов в RouteServiceProvider.php
как показно ниже:
namespace App\Providers; use App\Attributes\Route as RouteAttribute; class RouteServiceProvider extends ServiceProvider { public function boot() { $this->routes(function () { $rii = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(app_path('Http/Controllers'))); foreach ($rii as $file) { // pass if the directory if ($file->isDir()) continue; // make our controller namespace $class = 'App\\Http\\Controllers\\' . $file->getBasename('.php'); // create our virtual class $reflectionClass = new \ReflectionClass($class); // collect attributes of class if the invokable method is defined $classAttributes = collect($reflectionClass->getAttributes(RouteAttribute::class)); // check if the class have attribute itself if ($classAttributes->isNotEmpty()) { $arguments = collect($classAttributes->first()->getArguments()); // register our dynamic route Route::match($arguments->get('method'), $arguments->get('path'), $class) ->middleware($arguments->get('middleware', [])); } // check each method individually foreach ($reflectionClass->getMethods() as $method) { // collect all attributes of the method $methodAttributes = collect($method->getAttributes(RouteAttribute::class)); // pass if the method doesn't have any defined attributes if ($methodAttributes->isEmpty()) continue; // get arguments of our defined attribute $arguments = collect($methodAttributes->first()->getArguments()); // register our dynamic route Route::match($arguments->get('method'), $arguments->get('path'), $class . '@' . $method->getName()) ->middleware($arguments->get('middleware', [])); } } }); } }
Этот код в цикле прочитает ваши контроллеры и использует атрибуты из них для создания новых маршрутов. Больше нет необходимости отдельно прописывать их в файл маршрутов.
Важно: этот функционал предназначен только для одноуровневой организации контроллеров. Если вы используете вложенную структуру папок, то вам необходимо дописать функционал.
Если вы используете контроллеры одного действия, то вам необходимо добавить атрбут Route
не в метод, а в класс:
namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Models\User; #[Route(method: 'post', path: 'api/account/{id}/server/provision')] class ProvisionServer extends Controller { /** * Provision a new web server. * * @return \Illuminate\Http\Response */ public function __invoke() { // } }
Необязательно всерьёз воспринимать это как замену дефолтной маршрутизации. Важно, что вы теперь знаете как можно использовать атрибуты в вашем коде!
Автор: Samir Mammadhasanov
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.