Как работать с slug в Symfony?
Здравствуйте!
Никак не получается настроить роутер через {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(); } }
Покажите вывод 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 весь
"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
Вот такое:
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 ------------------------------------------------------------------------- ---------- ---------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------
Всё решилось.
Помог рецепт Виктора
В 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 стоит falsedoctrine: # ... 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 который плюс-минус решает проблему
Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.
Пока нет других ответов. Будьте первым, кто поможет автору.
Ответить на вопрос
Для работы с slug в Symfony можно воспользоваться библиотекой "Slugify", которая поможет генерировать URL-дружелюбные строки (человеко-понятные URL) на основе заданного текста.
Вот пример того, как можно использовать Slugify в Symfony:
1. Установите библиотеку через Composer, добавив зависимость в ваш файл composer.json:
"cocur/slugify": "^4.0"
2. Обновите зависимости Composer:
composer update
3. Теперь вы можете использовать Slugify в вашем контроллере или сервисе:
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]|-)+/' ]);
Теперь у вас есть инструмент для генерации slug в Symfony с помощью библиотеки Slugify. Не забудьте проверить документацию по библиотеке для получения дополнительной информации и возможностей.