Okej teraz będą wklejał kod zamiast screenów.
Analizując dokładnie kod natrafiłem na to, że został napisany własny loader dla tych adnotacji z Nelmio\ApiDocBundle\Annotation\Model
Kopiuj
<?php
declare(strict_types=1);
namespace App\UI\Api\Router;
use App\UI\Api\Router\Annotation\ApiActionRoute;
use App\UI\Api\Router\Annotation\ApiBulkActionRoute;
use App\UI\Api\Router\Annotation\ApiCreateRoute;
use App\UI\Api\Router\Annotation\ApiDeleteItemRoute;
use App\UI\Api\Router\Annotation\ApiDownloadItemRoute;
use App\UI\Api\Router\Annotation\ApiGetItemInfoRoute;
use App\UI\Api\Router\Annotation\ApiGetItemRoute;
use App\UI\Api\Router\Annotation\ApiListRoute;
use App\UI\Api\Router\Annotation\ApiRoute;
use App\UI\Api\Router\Annotation\ApiUpdateRoute;
use Doctrine\Common\Annotations\Reader;
use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Symfony\Component\Routing\Loader\AttributeClassLoader;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\Route;
class ApiRouteAnnotationLoader extends AttributeClassLoader
{
private Inflector $inflector;
public function __construct(Reader $reader = null)
{
$this->inflector = InflectorFactory::create()->build();
parent::__construct($reader);
}
protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $annot): void
{
if ($annot instanceof ApiRoute) {
$this->handle($route, $annot);
$this->addDefaults($route, ['api_route' => get_class($annot)]);
}
}
private function handle(Route $route, ApiRoute $annot): void
{
$name = $this->prepareName($annot->getResourceClassShortName());
switch (true) {
case $annot instanceof ApiListRoute:
$this->handleAnnot($route, $annot, "/{$name}", "api_{$name}", ['GET']);
break;
case $annot instanceof ApiGetItemRoute:
$this->handleAnnot($route, $annot, "/{$name}/{id}", "api_{$name}_item", ['GET']);
break;
case $annot instanceof ApiGetItemInfoRoute:
$infoName = $annot->getInfoName() ?? 'info';
$this->handleAnnot($route, $annot, "/{$name}/{id}/{$infoName}", "api_{$name}_item_{$infoName}", ['GET']);
break;
case $annot instanceof ApiCreateRoute:
$this->handleAnnot($route, $annot, "/{$name}", "api_{$name}_create", ['POST']);
break;
case $annot instanceof ApiDeleteItemRoute:
$this->handleAnnot($route, $annot, "/{$name}/{id}", "api_{$name}_delete", ['DELETE']);
break;
case $annot instanceof ApiUpdateRoute:
$this->handleAnnot($route, $annot, "/{$name}/{id}", "api_{$name}_update", ['PUT']);
break;
case $annot instanceof ApiActionRoute:
$action = $this->prepareName($annot->getActionName(), false);
$this->handleAnnot($route, $annot, "/{$name}/{id}/actions/{$action}", "api_{$name}_item_action_{$action}", ['POST']);
break;
case $annot instanceof ApiBulkActionRoute:
$action = $this->prepareName($annot->getActionName(), false);
$this->handleAnnot($route, $annot, "/{$name}/actions/{$action}", "api_{$name}_action_{$action}", ['POST']);
break;
case $annot instanceof ApiDownloadItemRoute:
$this->handleAnnot($route, $annot, "/{$name}/{id}/download", "api_{$name}_download", ['GET']);
break;
}
}
private function handleAnnot(Route $route, ApiRoute $annot, string $path, string $name, array $methods): void
{
if (!$route->getPath()) {
$route->setPath($path);
}
if (!$route->getName()) {
$route->setDefault('_route', $name);
}
if (preg_match('#{id}#', $route->getPath())) {
$route->setRequirement('id', '^[\w\-]+$');
}
if (empty($route->getMethods())) {
$route->setMethods($methods);
}
}
private function addDefaults(Route $route, array $defaults = []): void
{
$route->setDefaults(array_merge($route->getDefaults(), $defaults));
}
private function prepareName(string $name, bool $pluralize = true): string
{
$name = preg_replace_callback('#([A-Z])([A-Z]+)([A-Z]|$)#', fn($word) => $word[1] . strtolower($word[2]) . $word[3], $name);
if ($pluralize) {
$words = preg_split('/(?=[A-Z])/', $name);
$lastWord = array_pop($words);
$words[] = $this->inflector->pluralize($lastWord);
$name = implode('', $words);
}
return $this->inflector->tableize($name);
}
}
W services.yaml jest zarejestrowany ten loader, ale symfony 6.4 tak jak by go nie czytało, dałem dd('test') w configureRoute, i nie wypluwa w konsoli, na poprzedniej wersji projektu wypluwa
Tutaj rejestracja loadera w services.yaml
Kopiuj
# ApiRoute route generation
routing.loader.annotation:
class: App\UI\Api\Router\ApiRouteAnnotationLoader
Object of class Nelmio\ApiDocBundle\Annotation\Model could not be converted !! to string, jest spowodowane tym, że AttributeClassLoader otrzymuje w $path obiekt Nelmio, a nie ścieżke do wygenerowania
Tutaj kawałek kodu gdzie to pobiera, tego raczej nie ruszamy bo jest to paczka z vendora symfonowska, i trzeba jakoś uruchomić tem niestandardowy ApiRouteAnnotationLoader
Kopiuj
$path = $annot->getLocalizedPaths() ?: $annot->getPath();
$prefix = $globals['localized_paths'] ?: $globals['path'];
$paths = [];
Przeglądałem dokumentację rejestracji niestandardowego loadera, ale na marne od razu leci domyślny AttributeClassLoader.
Wiem, że w nowszych wersjach symfony zalecane jest używanie atrybutów, ale stary projekt jest oparty na adnotacjach, rector zamienił mi tylko adnotacje na atrybuty w encjach, tam gdzie jest wykorzystywana paczka Nelmio jest już problem