Как работать с slug в Symfony?

Ссылка скопирована
26 марта 2026 1 ответ

Здравствуйте!

Никак не получается настроить роутер через {slug}

Всё делал по книге

Пока вместо слагов стоял Id всё работало как надо.

Поставил {slug} Symfony предъявил мне в show:

Cannot autowire argument $page of "AppControllerPageController::show()": it needs an instance of "AppEntityPage" but this type has been excluded in "config/services.yaml".

В services.yaml вставил

AppEntityPage:
autowire: true

Теперь show стал показывать пустоту или точнее светлоголубую пустоту.

Вставил дамп и

AppEntityPage {#762 ▼   -id: null   -title: null   -slug: null   -description: null   -cover: null   -body: null   -createdAt: null }

AppEntityPage {#762 ▼ -id: null -title: null -slug: null -description: null -cover: null -body: null -createdAt: null }

Как я понимаю не находит он страницу по слагам этим по id находит, а по слагам нет.
Как заставить ума не приложу.

Контроллер:

namespace AppController;  use AppEntityPage; use AppRepositoryPageRepository; use SymfonyBundleFrameworkBundleControllerAbstractController; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentRoutingAttributeRoute;  class PageController extends AbstractController {     #[Route('/', name: 'homepage')]     public function index(PageRepository $pageRepository): Response     {         return $this->render('page/index.html.twig', [             'pages' => $pageRepository->findAll(),         ]);     }      #[Route('/{slug}', name: 'show-page')]     public function show(Request $request, Page $page): Response     {         return $this->render('/page/show.html.twig', [             'slug' => $page->getSlug(),             'page' => $page,         ]);     } }

namespace AppController; use AppEntityPage; use AppRepositoryPageRepository; use SymfonyBundleFrameworkBundleControllerAbstractController; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentRoutingAttributeRoute; class PageController extends AbstractController { #[Route('/', name: 'homepage')] public function index(PageRepository $pageRepository): Response { return $this->render('page/index.html.twig', [ 'pages' => $pageRepository->findAll(), ]); } #[Route('/{slug}', name: 'show-page')] public function show(Request $request, Page $page): Response { return $this->render('/page/show.html.twig', [ 'slug' => $page->getSlug(), 'page' => $page, ]); } }

Index.twig:

{% extends 'base.html.twig' %}  {% block title %}Hello PageController!{% endblock %}  {% block body %} <style>     .example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }     .example-wrapper code { background: #F5F5F5; padding: 2px 6px; } </style>  <div class="example-wrapper">      This friendly message is coming from:     <ul>         <li>Your controller at <code>/home/pv/www/lantico.site/src/Controller/PageController.php</code></li>         <li>Your template at <code>/home/pv/www/lantico.site/templates/page/index.html.twig</code></li>     </ul>      {% for page in pages %}         <h4>{{ page.title }}</h4>         <p>             <a href="{{ path('show-page', {slug: page.slug}) }}">{{ page }}</a>         </p>     {% endfor %} </div> {% endblock %}

{% extends 'base.html.twig' %} {% block title %}Hello PageController!{% endblock %} {% block body %} <style> .example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; } .example-wrapper code { background: #F5F5F5; padding: 2px 6px; } </style> <div class="example-wrapper"> This friendly message is coming from: <ul> <li>Your controller at <code>/home/pv/www/lantico.site/src/Controller/PageController.php</code></li> <li>Your template at <code>/home/pv/www/lantico.site/templates/page/index.html.twig</code></li> </ul> {% for page in pages %} <h4>{{ page.title }}</h4> <p> <a href="{{ path('show-page', {slug: page.slug}) }}">{{ page }}</a> </p> {% endfor %} </div> {% endblock %}

show.twig.html

{% extends 'base.html.twig' %}  {% block title %}   {{ page.title }} {% endblock %}  {% block body %}   <h2>{{ page.body }} </h2>   {{ dump(page) }} {% endblock %}

{% extends 'base.html.twig' %} {% block title %} {{ page.title }} {% endblock %} {% block body %} <h2>{{ page.body }} </h2> {{ dump(page) }} {% endblock %}

Дополнительно:

В services.yaml вставил

AppEntityPage:
autowire: true

Вот это точно не нужно. Это сущность, а не сервис.

Покажите сам класс Page.

  • Вот он
    namespace AppEntity;  use AppRepositoryPageRepository; use DateTimeImmutable; use DoctrineDBALTypesTypes; use DoctrineORMMapping as ORM; use SymfonyBridgeDoctrineValidatorConstraintsUniqueEntity; use SymfonyComponentStringSluggerSluggerInterface;   #[ORMEntity(repositoryClass: PageRepository::class)] #[ORMHasLifecycleCallbacks] #[UniqueEntity(fields: ['title'], message: 'Страница с таким title уже существует')] #[UniqueEntity(fields: ['slug'], message: 'Страница с таким slug уже существует')] class Page {     #[ORMId]     #[ORMGeneratedValue]     #[ORMColumn]     private ?int $id = null;      #[ORMColumn(length: 255, unique: true)]     private ?string $title = null;      #[ORMColumn(length: 255, unique: true)]     private ?string $slug = null;      #[ORMColumn(type: Types::TEXT)]     private ?string $description = null;      #[ORMColumn(length: 255, nullable: true)]     private ?string $cover = null;      #[ORMColumn(type: Types::TEXT)]     private ?string $body = null;      #[ORMColumn]     private ?DateTimeImmutable $createdAt = null;      public function __toString(): string     {         return $this->title;     }      public function getId(): ?int     {         return $this->id;     }           public function getTitle(): ?string     {         return $this->title;     }          public function setTitle(string $title): static     {         $this->title = $title;          return $this;     }          public function getSlug(): ?string     {         return $this->slug;     }          public function setSlug(string $slug): static     {         $this->slug = $slug;          return $this;     }          public function computeSlug(SluggerInterface $slugger): void     {         if (!$this->slug || '-' === $this->slug) {             $this->slug = (string) $slugger->slug((string) $this)->lower();         }     }     public function getDescription(): ?string     {         return $this->description;     }      public function setDescription(string $description): static     {         $this->description = $description;          return $this;     }       public function getCover(): ?string     {         return $this->cover;     }          public function setCover(?string $cover): static     {         $this->cover = $cover;          return $this;     }      public function getBody(): ?string     {         return $this->body;     }      public function setBody(string $body): static     {         $this->body = $body;          return $this;     }        public function getCreatedAt(): ?DateTimeImmutable     {         return $this->createdAt;     }          public function setCreatedAt(DateTimeImmutable $createdAt): static     {         $this->createdAt = $createdAt;          return $this;     }     #[ORMPrePersist]     public function setCreatedAtValue(): void     {         $this->createdAt = new DateTimeImmutable();     } }

    namespace AppEntity; use AppRepositoryPageRepository; use DateTimeImmutable; use DoctrineDBALTypesTypes; use DoctrineORMMapping as ORM; use SymfonyBridgeDoctrineValidatorConstraintsUniqueEntity; use SymfonyComponentStringSluggerSluggerInterface; #[ORMEntity(repositoryClass: PageRepository::class)] #[ORMHasLifecycleCallbacks] #[UniqueEntity(fields: ['title'], message: 'Страница с таким title уже существует')] #[UniqueEntity(fields: ['slug'], message: 'Страница с таким slug уже существует')] class Page { #[ORMId] #[ORMGeneratedValue] #[ORMColumn] private ?int $id = null; #[ORMColumn(length: 255, unique: true)] private ?string $title = null; #[ORMColumn(length: 255, unique: true)] private ?string $slug = null; #[ORMColumn(type: Types::TEXT)] private ?string $description = null; #[ORMColumn(length: 255, nullable: true)] private ?string $cover = null; #[ORMColumn(type: Types::TEXT)] private ?string $body = null; #[ORMColumn] private ?DateTimeImmutable $createdAt = null; public function __toString(): string { return $this->title; } public function getId(): ?int { return $this->id; } public function getTitle(): ?string { return $this->title; } public function setTitle(string $title): static { $this->title = $title; return $this; } public function getSlug(): ?string { return $this->slug; } public function setSlug(string $slug): static { $this->slug = $slug; return $this; } public function computeSlug(SluggerInterface $slugger): void { if (!$this->slug || '-' === $this->slug) { $this->slug = (string) $slugger->slug((string) $this)->lower(); } } public function getDescription(): ?string { return $this->description; } public function setDescription(string $description): static { $this->description = $description; return $this; } public function getCover(): ?string { return $this->cover; } public function setCover(?string $cover): static { $this->cover = $cover; return $this; } public function getBody(): ?string { return $this->body; } public function setBody(string $body): static { $this->body = $body; return $this; } public function getCreatedAt(): ?DateTimeImmutable { return $this->createdAt; } public function setCreatedAt(DateTimeImmutable $createdAt): static { $this->createdAt = $createdAt; return $this; } #[ORMPrePersist] public function setCreatedAtValue(): void { $this->createdAt = new DateTimeImmutable(); } }

  • Какая версия symfony?
  • И версия DoctrineBundle?

    Покажите вывод composer show

  • Павел,
    Не вижу проблем в коде, но вот рекомендую поправить так
    use SymfonyComponentRoutingRequirementRequirement; ....  #[Route('/{slug}', name: 'show-page', requirements: ['slug' => Requirement::ASCII_SLUG])]

    use SymfonyComponentRoutingRequirementRequirement; .... #[Route('/{slug}', name: 'show-page', requirements: ['slug' => Requirement::ASCII_SLUG])]

    Лучше приучиться сразу использовать requirements чтобы исключить всякий мусор который не должен туда попадать можете глянуть в Requirement там на все случае жизни есть заготовки

    Покажите ваш конфиг файл config/services.yaml весь

  • Symfony 7.04

    "php": ">=8.3",

    "doctrine/dbal": "^3",
    "doctrine/doctrine-bundle": "^2.12",
    "doctrine/doctrine-migrations-bundle": "^3.3",
    "doctrine/orm": "^3.1",
    "easycorp/easyadmin-bundle": "^4",

  • Покажите что выведет команда
    bin/console debug:container --tag controller.argument_value_resolver

    bin/console debug:container --tag controller.argument_value_resolver

  • BoShurik,
    Вот такое:
    Symfony Container Services Tagged with "controller.argument_value_resolver" Tag ===============================================================================   ------------------------------------------------------------------------- ---------- ---------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------   Service ID                                                                priority   name                                                                                     Class name   ------------------------------------------------------------------------- ---------- ---------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------   security.user_value_resolver                                              120        SymfonyComponentSecurityHttpControllerUserValueResolver                             SymfonyComponentSecurityHttpControllerUserValueResolver   security.security_token_value_resolver                                    120        SymfonyComponentSecurityHttpControllerSecurityTokenValueResolver                    SymfonyComponentSecurityHttpControllerSecurityTokenValueResolver   doctrine.orm.entity_value_resolver                                        110        SymfonyBridgeDoctrineArgumentResolverEntityValueResolver                             SymfonyBridgeDoctrineArgumentResolverEntityValueResolver   argument_resolver.backed_enum_resolver                                    100        SymfonyComponentHttpKernelControllerArgumentResolverBackedEnumValueResolver         SymfonyComponentHttpKernelControllerArgumentResolverBackedEnumValueResolver   argument_resolver.uid                                                     100        SymfonyComponentHttpKernelControllerArgumentResolverUidValueResolver                SymfonyComponentHttpKernelControllerArgumentResolverUidValueResolver   argument_resolver.datetime                                                100        SymfonyComponentHttpKernelControllerArgumentResolverDateTimeValueResolver           SymfonyComponentHttpKernelControllerArgumentResolverDateTimeValueResolver   argument_resolver.request_attribute                                       100        SymfonyComponentHttpKernelControllerArgumentResolverRequestAttributeValueResolver   SymfonyComponentHttpKernelControllerArgumentResolverRequestAttributeValueResolver   argument_resolver.request                                                 50         SymfonyComponentHttpKernelControllerArgumentResolverRequestValueResolver            SymfonyComponentHttpKernelControllerArgumentResolverRequestValueResolver   argument_resolver.session                                                 50         SymfonyComponentHttpKernelControllerArgumentResolverSessionValueResolver            SymfonyComponentHttpKernelControllerArgumentResolverSessionValueResolver   EasyCorpBundleEasyAdminBundleArgumentResolverAdminContextResolver                                                                     EasyCorpBundleEasyAdminBundleArgumentResolverAdminContextResolver   EasyCorpBundleEasyAdminBundleArgumentResolverBatchActionDtoResolver                                                                     EasyCorpBundleEasyAdminBundleArgumentResolverBatchActionDtoResolver   argument_resolver.service                                                 -50        SymfonyComponentHttpKernelControllerArgumentResolverServiceValueResolver            SymfonyComponentHttpKernelControllerArgumentResolverServiceValueResolver   argument_resolver.default                                                 -100       SymfonyComponentHttpKernelControllerArgumentResolverDefaultValueResolver            SymfonyComponentHttpKernelControllerArgumentResolverDefaultValueResolver   argument_resolver.variadic                                                -150       SymfonyComponentHttpKernelControllerArgumentResolverVariadicValueResolver           SymfonyComponentHttpKernelControllerArgumentResolverVariadicValueResolver   argument_resolver.not_tagged_controller                                   -200                                                                     SymfonyComponentHttpKernelControllerArgumentResolverNotTaggedControllerValueResolver  ------------------------------------------------------------------------- ---------- ---------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------

    Symfony Container Services Tagged with "controller.argument_value_resolver" Tag =============================================================================== ------------------------------------------------------------------------- ---------- ---------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- Service ID priority name Class name ------------------------------------------------------------------------- ---------- ---------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------- security.user_value_resolver 120 SymfonyComponentSecurityHttpControllerUserValueResolver SymfonyComponentSecurityHttpControllerUserValueResolver security.security_token_value_resolver 120 SymfonyComponentSecurityHttpControllerSecurityTokenValueResolver SymfonyComponentSecurityHttpControllerSecurityTokenValueResolver doctrine.orm.entity_value_resolver 110 SymfonyBridgeDoctrineArgumentResolverEntityValueResolver SymfonyBridgeDoctrineArgumentResolverEntityValueResolver argument_resolver.backed_enum_resolver 100 SymfonyComponentHttpKernelControllerArgumentResolverBackedEnumValueResolver SymfonyComponentHttpKernelControllerArgumentResolverBackedEnumValueResolver argument_resolver.uid 100 SymfonyComponentHttpKernelControllerArgumentResolverUidValueResolver SymfonyComponentHttpKernelControllerArgumentResolverUidValueResolver argument_resolver.datetime 100 SymfonyComponentHttpKernelControllerArgumentResolverDateTimeValueResolver SymfonyComponentHttpKernelControllerArgumentResolverDateTimeValueResolver argument_resolver.request_attribute 100 SymfonyComponentHttpKernelControllerArgumentResolverRequestAttributeValueResolver SymfonyComponentHttpKernelControllerArgumentResolverRequestAttributeValueResolver argument_resolver.request 50 SymfonyComponentHttpKernelControllerArgumentResolverRequestValueResolver SymfonyComponentHttpKernelControllerArgumentResolverRequestValueResolver argument_resolver.session 50 SymfonyComponentHttpKernelControllerArgumentResolverSessionValueResolver SymfonyComponentHttpKernelControllerArgumentResolverSessionValueResolver EasyCorpBundleEasyAdminBundleArgumentResolverAdminContextResolver EasyCorpBundleEasyAdminBundleArgumentResolverAdminContextResolver EasyCorpBundleEasyAdminBundleArgumentResolverBatchActionDtoResolver EasyCorpBundleEasyAdminBundleArgumentResolverBatchActionDtoResolver argument_resolver.service -50 SymfonyComponentHttpKernelControllerArgumentResolverServiceValueResolver SymfonyComponentHttpKernelControllerArgumentResolverServiceValueResolver argument_resolver.default -100 SymfonyComponentHttpKernelControllerArgumentResolverDefaultValueResolver SymfonyComponentHttpKernelControllerArgumentResolverDefaultValueResolver argument_resolver.variadic -150 SymfonyComponentHttpKernelControllerArgumentResolverVariadicValueResolver SymfonyComponentHttpKernelControllerArgumentResolverVariadicValueResolver argument_resolver.not_tagged_controller -200 SymfonyComponentHttpKernelControllerArgumentResolverNotTaggedControllerValueResolver ------------------------------------------------------------------------- ---------- ---------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------

  • Странно. Все должно работать. Кеш чистили?
  • BoShurik,

    Всё решилось.

    Помог рецепт Виктора

  • В services.yaml вставил

    AppEntityPage:
    autowire: true

    1. Уберите это.

    2. Используйте атрибут MapEntity, чтобы явно указать, на какое поле мапить параметр 'slug'

    <?php  namespace AppController;  use AppEntityPage; use AppRepositoryPageRepository; use SymfonyBridgeDoctrineAttributeMapEntity; use SymfonyBundleFrameworkBundleControllerAbstractController; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentRoutingAttributeRoute;  class PageController extends AbstractController {     // ...      #[Route('/{slug}', name: 'show-page')]     public function show(         #[MapEntity(mapping: ['slug' => 'slug'])] Page $page     ): Response     {         return $this->render('/page/show.html.twig', [             'slug' => $page->getSlug(),             'page' => $page,         ]);     } }

    <?php namespace AppController; use AppEntityPage; use AppRepositoryPageRepository; use SymfonyBridgeDoctrineAttributeMapEntity; use SymfonyBundleFrameworkBundleControllerAbstractController; use SymfonyComponentHttpFoundationResponse; use SymfonyComponentRoutingAttributeRoute; class PageController extends AbstractController { // ... #[Route('/{slug}', name: 'show-page')] public function show( #[MapEntity(mapping: ['slug' => 'slug'])] Page $page ): Response { return $this->render('/page/show.html.twig', [ 'slug' => $page->getSlug(), 'page' => $page, ]); } }

    • Благодарю!
      Заработало.
      В книге про это нет ничего. Пошёл дальше учиться
    • Павел, "Книга" может касаться старых версий. В 7-й версии много старого задепрекейтили
    • Павел, Вот тут описано https://symfony.com/doc/current/doctrine.html#fetc...
    • Очень странно.

      Вот это надо было убрать с самого начала, как в комментариях писали

      AppEntityPage:     autowire: true

      AppEntityPage: autowire: true

      А дальше, скорее всего дело было в кеше, потому что по коду все должно работать без аннотаций.
      Да и на своем проекте проверил - тоже работает

    • BoShurik,
      Просто по умолчанию при свежей установке auto_mapping стоит false
      doctrine:     # ...     orm:         # ...         controller_resolver:             auto_mapping: false

      doctrine: # ... orm: # ... controller_resolver: auto_mapping: false

    • Виктор Кожухарь, точно, спасибо. Неделю назад смерджили
    • BoShurik, Считаю, что правильно сделали. Не люблю магию.
    • FYI: https://github.com/symfony/recipes/pull/1299#issue...
    • BoShurik, Повторюсь, что сама идея убрать этот автомат прекрасна. Но вот исполнение подкачало. Текст ошибки мог бы быть и получше, а то ведь и Павел, и Вы, и я, грешный, не сразу допёрли, в чём дело). Я привык, что у Symfony просто великолепные тексты ошибок, а тут такое...
    • BoShurik, уже вернули взад :D
      https://github.com/symfony/recipes/pull/1302

      Но я видимо останусь на флаге false.

    • Да, уже есть https://github.com/symfony/symfony/pull/54455 который плюс-минус решает проблему
    Нужно решить такую задачу?

    Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

    Заказать помощь
    Лучший ответ
    1
    Игорь Волков Ответ

    Для работы с slug в Symfony можно воспользоваться библиотекой "Slugify", которая поможет генерировать URL-дружелюбные строки (человеко-понятные URL) на основе заданного текста.

    Вот пример того, как можно использовать Slugify в Symfony:

    1. Установите библиотеку через Composer, добавив зависимость в ваш файл composer.json:

    "cocur/slugify": "^4.0"

    "cocur/slugify": "^4.0"

    2. Обновите зависимости Composer:

    composer update

    composer update

    3. Теперь вы можете использовать Slugify в вашем контроллере или сервисе:

    use Cocur\Slugify\Slugify;
     
    $slugify = new Slugify();
    $slug = $slugify->slugify('Пример текста для генерации slug');
    echo $slug; // результат: "primer-teksta-dlya-generacii-slug"

    use Cocur\Slugify\Slugify; $slugify = new Slugify(); $slug = $slugify->slugify('Пример текста для генерации slug'); echo $slug; // результат: "primer-teksta-dlya-generacii-slug"

    4. Вы также можете настроить Slugify, указав различные параметры:

    $slugify = new Slugify([
        'lowercase' => false,
        'regexp' => '/([^a-zA-Z0-9]|-)+/'
    ]);

    $slugify = new Slugify([ 'lowercase' => false, 'regexp' => '/([^a-zA-Z0-9]|-)+/' ]);

    Теперь у вас есть инструмент для генерации slug в Symfony с помощью библиотеки Slugify. Не забудьте проверить документацию по библиотеке для получения дополнительной информации и возможностей.

    Другие ответы (0)

    Пока нет других ответов. Будьте первым, кто поможет автору.

    Ответить на вопрос

    комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *

    Вам также может быть интересно