'get', PostRoute::class => 'post', PutRoute::class => 'put', DeleteRoute::class => 'delete', ]; /** @var array> */ private array $controllerRoutes = []; public function handle(): int { $paths = [ app_path('Http/Controllers'), base_path('modules'), ]; $totalRoutes = 0; foreach ($paths as $path) { if (!is_dir($path)) { continue; } $finder = new Finder(); $finder->files()->in($path)->name('*Controller.php'); foreach ($finder as $file) { $className = $this->getClassNameFromFile( $file->getRealPath(), $file->getPath() ); if ($className && class_exists($className)) { $routes = $this->scanController($className); if (!empty($routes)) { $this->controllerRoutes[$className] = $routes; $totalRoutes += count($routes); } } } } if (empty($this->controllerRoutes)) { $this->warn('No annotation routes found.'); return self::SUCCESS; } $this->writeHelperFile(); $this->info("Routes generated to: " . base_path('routes/api.php')); $this->info("Found {$totalRoutes} routes."); return self::SUCCESS; } private function scanController(string $className): array { $routes = []; try { $classRef = new ReflectionClass($className); } catch (ReflectionException) { return $routes; } $classAttrs = collect($classRef->getAttributes()); if ($classAttrs->isEmpty()) { return $routes; } $hasRequestAttr = $classAttrs->some( fn($attr) => $attr->getName() === RequestAttribute::class ); if (!$hasRequestAttr) { return $routes; } $requestAttr = $classAttrs->first( fn($attr) => $attr->getName() === RequestAttribute::class ); $requestInstance = $requestAttr->newInstance(); $routePrefix = $requestInstance->routePrefix ?? ''; $authGuard = $requestInstance->authGuard ?? null; $abilitiesPrefix = $requestInstance->abilitiesPrefix ?? ''; $classMiddleware = $this->normalizeMiddleware($requestInstance->middleware); foreach ($classRef->getMethods() as $method) { $attrs = $method->getAttributes(); if (empty($attrs)) { continue; } $methodName = $method->getName(); foreach ($attrs as $attr) { if (in_array($attr->getName(), self::$mapping)) { $instance = $attr->newInstance(); $authMiddleware = $this->buildAuthMiddleware( $instance->authorize, $authGuard, $abilitiesPrefix, ); $allMiddleware = array_values(array_unique(array_merge( $authMiddleware, $this->normalizeMiddleware($instance->middleware), $classMiddleware, ))); $routes[] = [ 'method' => self::$httpMethodMap[$attr->getName()], 'uri' => $instance->route, 'prefix' => trim($routePrefix, '/'), 'controller' => $className, 'action' => $methodName, 'middleware' => $allMiddleware, ]; } } } return $routes; } private function buildAuthMiddleware(string|bool $authorize, ?string $authGuard, string $abilitiesPrefix): array { if (empty($authorize) || $authorize === false) { return []; } $authMiddleware = ['auth:sanctum']; if (!empty($authGuard)) { $authMiddleware[] = 'authGuard:' . $authGuard; } else { $authMiddleware[] = 'authGuard'; } if (is_string($authorize) && !empty($abilitiesPrefix)) { $authMiddleware[] = 'abilities:' . $abilitiesPrefix . '.' . $authorize; } else if ($authorize !== true) { $authMiddleware[] = 'abilities:' . $authorize ; } return $authMiddleware; } private function normalizeMiddleware(string|array $middleware): array { if (empty($middleware)) { return []; } if (is_array($middleware)) { return $middleware; } return [$middleware]; } private function getClassNameFromFile(string $filePath, string $fileDir): ?string { $content = file_get_contents($filePath); if (!preg_match('/namespace\s+([^;]+);/', $content, $namespaceMatch)) { return $this->guessClassNameFromPath($filePath, $fileDir); } $namespace = trim($namespaceMatch[1]); $className = basename($filePath, '.php'); return $namespace . '\\' . $className; } private function guessClassNameFromPath(string $filePath, string $fileDir): ?string { $basePath = base_path(); $relativePath = str_replace($basePath, '', $fileDir); $namespace = str_replace('/', '\\', ltrim($relativePath, '/')); $namespace = trim($namespace, '\\'); $className = basename($filePath, '.php'); return empty($namespace) ? $className : $namespace . '\\' . $className; } private function writeHelperFile(): void { $outputPath = base_path('routes/api.php'); $lines = [ 'controllerRoutes as $className => $routes) { $prefix = $routes[0]['prefix']; $shortName = substr(strrchr($className, '\\'), 1); $lines[] = "// {$shortName}"; $chain = "Route::controller({$className}::class)"; if ($prefix !== '') { $chain .= "->prefix('{$prefix}')"; } $chain .= '->group(function () {'; $lines[] = $chain; foreach ($routes as $route) { $uri = $route['uri']; if ($uri === '') { $uri = '/'; } $middlewareStr = ''; if (!empty($route['middleware'])) { $middlewareStr = "->middleware(['" . implode("', '", $route['middleware']) . "'])"; } $lines[] = " Route::{$route['method']}('{$uri}', '{$route['action']}'){$middlewareStr};"; } $lines[] = '});'; $lines[] = ''; } File::put($outputPath, implode("\n", $lines)); } }