Продолжаем "ковырять" openapi. `#[AsCommand( name: 'lint:doc', description: 'Проверяет, что все роуты api покрыты документацией', )] class LintDocCommand extends Command { public function construct( private RouterInterface $router, #[Autowire(param: 'kernel.project_dir')] private string $projectDir, ) { parent::construct(); }

protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output);

$yaml = Yaml::parseFile($this->projectDir . '/docs/main.yaml'); #yaml->array $documented = []; foreach ($yaml['paths'] as $path => $methods) { #собираем все пути и методы, указанные в документации foreach (array_keys($methods) as $method) { $documented[] = sprintf('%s %s', $method, $path); } }

// Получаем маршруты из приложения $routes = $this->router->getRouteCollection();

$notDocumented = []; foreach ($routes as $route) { $path = $route->getPath(); if (str_starts_with($path, '/api')) { #Всё что не касается /api - нас не интересует $path = substr($path, 4); foreach ($route->getMethods() as $method) { $key = sprintf('%s %s', strtolower($method), $path); if (!in_array($key, $documented)) { $notDocumented[] = $key; } } } }

if (!empty($notDocumented)) { $io->error('Не документированы следующие роуты:'); $io->table(['Method', 'Path'], array_map(fn(string $route) => explode(' ', $route), $notDocumented));

return Command::FAILURE; }

$io->success('OK!');

return Command::SUCCESS; } } Таким образом, команда нас уведомляет, если есть роуты, которые не упомянуты в документации.`