В моем проекте Symfony 6.4 я пытаюсь объявить абстрактный сервис в Services.yaml, выполнив следующие действия:
imports:
- { resource: services/ }
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
services:
_defaults:
autowire: true
autoconfigure: true
# parent form
App\Admin\Form\HostingFormAbstract:
arguments:
$authorizationChecker: '@security.authorization_checker'
$tokenStorage: '@security.token_storage'
$phpHandlers: '%php_handlers%'
$nodeJsHandlers: '%nodeJs_handlers%'
abstract: true
# children forms
App\Admin\Form\HostingFormCreate:
parent: App\Admin\Form\HostingFormAbstract
class: App\Admin\Form\HostingFormCreate
calls:
- [setGroupManager, ['@App\Services\Managers\GroupManager']]
tags: [form.type]
App\Admin\Form\HostingFormUpdate:
parent: App\Admin\Form\HostingFormAbstract
class: App\Admin\Form\HostingFormUpdate
calls:
- [setHostnameManager, ['@App\Services\Managers\HostnameManager']]
tags: [form.type]
...
Класс HostingFormAbstract расширяет AbstractType из пакета symfony/form:
namespace App\Admin\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints\NotBlank;
abstract class HostingFormAbstract extends AbstractType
{
private TokenStorageInterface $tokenStorage;
private AuthorizationCheckerInterface $authorizationChecker;
private array $phpHandlers;
private array $nodeJsHandlers;
public function __construct(TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authorizationChecker, array $phpHandlers, array $nodeJsHandlers)
{
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
$this->phpHandlers = $phpHandlers;
$this->nodeJsHandlers = $nodeJsHandlers;
}
...
И при этом я получаю следующую ошибку при работе с моим приложением Symfony:
The service "App\Admin\Form\HostingFormAbstract" tagged "form.type" must not be abstract.
Для большего контекста я переписываю приложение Symfony 3.4 в новое приложение Symfony 6.4.
В версии 3.4 все работало нормально, но теперь кажется, что в Services.yaml нельзя объявить абстрактный сервис/класс, расширяющий класс AbstractType.
Я попытался добавить собственный тег в yaml для этого сервиса, но это ничего не изменило.
Я не нашел обходного пути в документации Symfony, и мне кажется странным, что вы не можете этого сделать. Возможно, я что-то пропустил.
Если у кого-нибудь есть идеи, как это сделать...
Спасибо !
Изменить, рабочее решение:
Кажется, что-то не так с моими несколькими файлами .yaml, регистрирующими службы.
Вот что мне нужно было сделать, чтобы зарегистрировать мой сервис:
autoconfigure: false
в свой сервис App\Admin\Form\HostingFormAbstract.imports:
- { resource: services/ }
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
...
App\Admin\Form\HostingFormAbstract:
class: App\Admin\Form\HostingFormAbstract
autoconfigure: false
arguments:
$authorizationChecker: '@security.authorization_checker'
$tokenStorage: '@security.token_storage'
$phpHandlers: '%php_handlers%'
$nodeJsHandlers: '%nodeJs_handlers%'
abstract: true
App\Admin\Form\HostingFormCreate:
parent: App\Admin\Form\HostingFormAbstract
class: App\Admin\Form\HostingFormCreate
calls:
- [ setGroupManager, [ '@App\Services\Managers\GroupManager' ] ]
tags: [ form.type ]
App\Admin\Form\HostingFormUpdate:
parent: App\Admin\Form\HostingFormAbstract
class: App\Admin\Form\HostingFormUpdate
calls:
- [ setHostnameManager, [ '@App\Services\Managers\HostnameManager' ] ]
tags: [ form.type ]
Да, я добавляю HostingFormAbstract в некоторые дочерние службы. Я обновил код блока services.yaml
в своем посте, чтобы показать вам, как я это делаю. @Серад
Похоже, это должно сработать. Может быть, autowire подбирает класс без файла определения вашего сервиса? У вас есть только один файл Services.yaml?
У меня есть несколько файлов yaml, в которых я устанавливаю сервисы. Существует основной файл services.yaml
, и я создал несколько других файлов .yaml, в которых заполняю некоторые сервисы, соответствующие категории (forms.yaml
, admins.yaml
, commands.yaml
и т. д.). Эти файлы находятся в подпапке под названием «services», и я импортирую их через основной файл services.yaml
с помощью ключа «imports». Я обновил свой пост, чтобы было более понятно.
Возможно, я нахожусь на совершенно неправильном пути, но autowire может дважды подключить ваш абстрактный класс из-за нескольких файлов Services.yaml. В частности, если ваш главный файл config/service.yaml содержит обычные строки `App\:resource: '../src/'`, то он подберет все и попытается создать одну и ту же службу дважды.
Я также думал, что это может быть проблема, связанная с моими разными файлами .yaml, но у меня не было проблем с другими службами, столь же постоянными, как эта, ни в одном из других файлов .yaml... Если я зарегистрирую службу HostingFormAbstract в главное services.yaml
К сожалению, у меня такая же проблема :/
На самом деле ты был совершенно прав @Cerad, это была часть проблемы, спасибо! Другая часть была в ответе yceruto
Это поведение по умолчанию для параметра autoconfigure
: тег form.type
устанавливается для всех классов FormTypeInterface
в области действия службы.
Однако вы можете исключить любую услугу из автоматической пометки, установив autoconfigure: false
следующим образом:
App\Admin\Form\HostingFormAbstract:
autoconfigure: false
// ...
и тогда ошибка должна исчезнуть.
Вы правы, он работает, если для автоконфигурации установлено значение false! Но это не единственное, что мне пришлось сделать: я также переместил свою службу HostingFormAbstract и все дочерние службы в мой основной файл Services.yaml вместо form.yaml, чтобы регистрация работала. Итак, @Cerad тоже был прав: что-то не так с моими файлами нескольких служб. Спасибо вам обоим за помощь!
Вы пытаетесь внедрить HostingFormAbstract напрямую в какое-то место? Обычно у вас есть несколько дополнительных классов форм, расширяющих HostingFormAbstract, которые имеют атрибут контейнера службы
parent
. symfony.com/doc/current/service_container/parent_services.html