Symfony 3.4 — FOSUserBundle — ошибка перезаписи команды создания

#php #symfony #fosuserbundle

#php #symfony #fosuserbundle

Вопрос:

Я пытаюсь переопределить команду создания пользователя FOSUser bundle.

Я следовал этому руководству: https://blog.overnetcity.com/2012/10/15/symfony2-fosuserbundle-comment-etendre-la-commande-par-defaut-de-creation-dun-utilisateur/

Но когда я запускаю свою новую команду, я получаю эту ошибку:

Не удается выполнить автоматическое подключение службы «Site PagesBundleCommandCreateUserCommand»: аргумент «$userManipulator» метода «FOS UserBundleCommandCreateUserCommand::__construct()» ссылается на класс «FOS UserBundleUtil UserManipulator» но такой службы не существует. Возможно, вам следует присвоить этот класс существующему сервису «fos_user.util.user_manipulator».

Кажется, что ни у кого не было этой проблемы до меня. Тем не менее, я уважал руководство, импорт, все, что было необходимо.

Я показываю вам свой код:

 <?php

namespace SitePagesBundleCommand;

use SymfonyComponentConsoleInputInputArgument;
use SymfonyComponentConsoleInputInputOption;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use FOSUserBundleModelUser;
use FOSUserBundleCommandCreateUserCommand as BaseCommand;

class CreateUserCommand extends BaseCommand
{
    /**
     * @see Command
     */
    protected function configure()
    {
        parent::configure();
        $this
            ->setName('atelech:user:create')
            ->getDefinition()->addArguments(array(
                new InputArgument('nom', InputArgument::REQUIRED, 'Le nom'),
                new InputArgument('prenom', InputArgument::REQUIRED, 'Le prenom')
            ))
        ;
        $this->setHelp(<<<EOT
// L'aide qui va bien
EOT
            );
    }

    /**
     * @see Command
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $username   = $input->getArgument('username');
        $email      = $input->getArgument('email');
        $password   = $input->getArgument('password');
        $nom        = $input->getArgument('nom');
        $prenom     = $input->getArgument('prenom');
        $inactive   = $input->getOption('inactive');
        $superadmin = $input->getOption('super-admin');

        /** @var FOSUserBundleModelUserManager $user_manager */
        $user_manager = $this->getContainer()->get('fos_user.user_manager');

        /** @var AcmeAcmeUserBundleEntityUser $user */
        $user = $user_manager->createUser();
        $user->setUsername($username);
        $user->setEmail($email);
        $user->setPlainPassword($password);
        $user->setEnabled((Boolean) !$inactive);
        $user->setSuperAdmin((Boolean) $superadmin);
        $user->setNom($nom);
        $user->setPrenom($prenom);

        $user_manager->updateUser($user);

        $output->writeln(sprintf('Created user <comment>%s</comment>', $username));
    }

    /**
     * @see Command
     */
    protected function interact(InputInterface $input, OutputInterface $output)
    {
        parent::interact($input, $output);
        if (!$input->getArgument('nom')) {
            $nom = $this->getHelper('dialog')->askAndValidate(
                $output,
                'Please choose a nom:',
                function($nom) {
                    if (empty($nom)) {
                        throw new Exception('nom can not be empty');
                    }

                    return $nom;
                }
            );
            $input->setArgument('nom', $nom);
        }
        if (!$input->getArgument('prenom')) {
            $prenom = $this->getHelper('dialog')->askAndValidate(
                $output,
                'Please choose a prenom:',
                function($prenom) {
                    if (empty($prenom)) {
                        throw new Exception('prenom can not be empty');
                    }

                    return $prenom;
                }
            );
            $input->setArgument('prenom', $prenom);
        }
    }
}
  

Я хотел добавить два атрибута «name» и «first name»

Спасибо за вашу помощь!

Комментарии:

1. Я никогда не использовал FOSUserBundle, но руководство действительно старое и связано с версией symfony2, а не с 3.4 (большие изменения между этими версиями).

2. Да, я знаю, но это было просто для мелочи ^^

Ответ №1:

итак..

проверено на Symfony 3.4.24 и friendsofsymfony / user-bundle (2.0)

и следующий код соответствует источникам fosuserbundle:

 <?php

namespace AppBundleCommand;

use SymfonyComponentConsoleInputInputArgument;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use FOSUserBundleCommandCreateUserCommand as BaseCommand;
use SymfonyComponentConsoleQuestionQuestion;

class CreateUserCommand extends BaseCommand
{
    /**
     * @see Command
     */
    protected function configure()
    {
        parent::configure();
        $this
            ->setName('atelech:user:create')
            ->getDefinition()->addArguments(
                array(
                    new InputArgument('nom', InputArgument::REQUIRED, 'Le nom'),
                    new InputArgument('prenom', InputArgument::REQUIRED, 'Le prenom'),
                )
            );
        $this->setHelp(
            <<<EOT
// L'aide qui va bien
EOT
        );
    }

    /**
     * @see Command
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $username = $input->getArgument('username');
        $email = $input->getArgument('email');
        $password = $input->getArgument('password');
        $nom = $input->getArgument('nom');
        $prenom = $input->getArgument('prenom');
        $inactive = $input->getOption('inactive');
        $superadmin = $input->getOption('super-admin');

        $userManager = $this->getContainer()->get('fos_user.user_manager');

        /** @var AcmeAcmeUserBundleEntityUser $user */
        $user = $userManager->createUser();
        $user->setUsername($username);
        $user->setEmail($email);
        $user->setPlainPassword($password);
        $user->setEnabled((Boolean)!$inactive);
        $user->setSuperAdmin((Boolean)$superadmin);
        $user->setNom($nom);
        $user->setPrenom($prenom);

        $userManager->updateUser($user);

        $output->writeln(sprintf('Created user <comment>%s</comment>', $username));
    }

    /**
     * {@inheritdoc}
     */
    protected function interact(InputInterface $input, OutputInterface $output)
    {
        parent::interact($input, $output);

        $questions = array();

        foreach (['nom', 'prenom'] as $key) {
            if (!$input->getArgument($key)) {
                $question = new Question("Please choose a $key:");
                $question->setValidator(
                    function ($username) use ($key) {
                        if (empty($username)) {
                            throw new Exception("$key can not be empty");
                        }

                        return $username;
                    }
                );
                $questions[$key] = $question;
            }
        }

        foreach ($questions as $name => $question) {
            $answer = $this->getHelper('question')->ask($input, $output, $question);
            $input->setArgument($name, $answer);
        }
    }
}
  

и ничего в service.yml — все автоматически подключено правильно

проверьте ваше пространство имен, пожалуйста 🙂

Комментарии:

1. Теперь, когда я пишу свою команду в CMD, у меня возникает эта ошибка: вспомогательный «диалог» не определен : / Я думаю, это из-за моей версии symfony (3.4)… И я не знаю, как я могу перезаписать свою команду создания в этой версии FOSUserBundle:/ (извините за мой плохой английский, я француз)

2. ну, вспомогательный диалог был удален из symfony 3.0, насколько я помню. попробуйте использовать это вместо — symfony.com/doc/current/components/console/helpers /…

3. также можете ли вы показать свою версию FOSBundle? почему вы не можете ее обновить?

4. В моем composer.json есть: «friendsofsymfony / user-bundle»: «~ 2.0». Это хорошо? Спасибо за вашу ссылку! Я прочитаю это

5. Большое вам спасибо! 🙂 По минусам, если я обновлю пакет, это не нарушит все, что я сделал до сих пор? (перегрузка некоторых методов, регистрация и т.д.)?

Ответ №2:

Почему ошибка

У меня была эта ошибка в symfony 4, но это проблема для всех symfony из 3.3 и nevest.

 In DefinitionErrorExceptionPass.php line 54:

  Cannot autowire service "AppCommandAppUserCreateCommand": argument "$user  
  Manipulator" of method "__construct()" references class "FOSUserBundleUti  
  lUserManipulator" but no such service exists. You should maybe alias this   
  class to the existing "fos_user.util.user_manipulator" service.                                                                                        
  

это потому, что из Symfony 3.3 службы по умолчанию являются частными и автоматически подключаются. Для исправления вы должны описать свой личный аргумент конструктора команды в services.yml .

Этот код должен устранить проблему:

     AppCommandAppUserCreateCommand:
        arguments: ['@fos_user.util.user_manipulator']
  

Вторая причина этой ошибки. Вы не переопределяете конструктор, но если конструктор расширяемого класса не объявлен, php использует конструктор родительского класса. Этот:

     public function __construct(UserManipulator $userManipulator)
    {
        $this->userManipulator = $userManipulator;
    }
  

таким образом, даже если вы ее не ввели, UserManipulator используется, и это является причиной ошибки.

Если вы хотите узнать больше, я рекомендую руководство:https://symfonycasts.com/screencast/symfony-3.3/autowiring-aliases

Как я решил эту проблему?

В моем случае я отказался от использования, FOSUserBundleUtilUserManipulator потому что его метод create слишком ограничен. Я создаю класс, который ее расширяет:

src/Util/AppUserManipulator.php

 <?php


namespace AppUtil;

use AppEntityUser;
use FOSUserBundleEventUserEvent;
use FOSUserBundleFOSUserEvents;
use FOSUserBundleModelUserManagerInterface;
use FOSUserBundleUtilUserManipulator;
use SymfonyComponentEventDispatcherEventDispatcherInterface;
use SymfonyComponentHttpFoundationRequestStack;
use SymfonyComponentHttpFoundationRequest;

class AppUserManipulator extends UserManipulator
{
    /**
     * User manager.
     *
     * @var UserManagerInterface
     */
    private $userManager;

    /**
     * @var EventDispatcherInterface
     */
    private $dispatcher;

    /**
     * @var RequestStack
     */
    private $requestStack;

    /**
     * UserManipulator constructor.
     *
     * @param UserManagerInterface     $userManager
     * @param EventDispatcherInterface $dispatcher
     * @param RequestStack             $requestStack
     */
    public function __construct(UserManagerInterface $userManager, EventDispatcherInterface $dispatcher, RequestStack $requestStack)
    {
        parent::__construct($userManager, $dispatcher, $requestStack);
        $this->userManager = $userManager;
        $this->dispatcher = $dispatcher;
        $this->requestStack = $requestStack;
    }

    /**
     * Creates a user and returns it.
     *
     * @param string $username
     * @param string $password
     * @param string $email
     * @param bool   $active
     * @param bool   $superadmin
     * @param string $firstName
     * @param string $lastName
     * @param string $phoneNumber
     *
     * @return FOSUserBundleModelUserInterface
     */
    public function createAppUser($username, $password, $email, $active, $superadmin, $firstName, $lastName, $phoneNumber)
    {
        /**
         * @var User
         */
        $user = $this->userManager->createUser();
        if($user instanceof User) {
            $user->setUsername($username);
            $user->setEmail($email);
            $user->setPlainPassword($password);
            $user->setEnabled((bool)$active);
            $user->setSuperAdmin((bool)$superadmin);
            $user->setFirstName($firstName);
            $user->setLastName($lastName);
            $user->setPhoneNumber($phoneNumber);
            $this->userManager->updateUser($user);

            $event = new UserEvent($user, $this->getRequest());
            $this->dispatcher->dispatch(FOSUserEvents::USER_CREATED, $event);

        }
        return $user;
    }

    /**
     * @return Request
     */
    private function getRequest()
    {
        return $this->requestStack->getCurrentRequest();
    }
}
  

Команда, которую я создал, содержит код src/Command/AppUserCreateCommand.php

 <?php

namespace AppCommand;

use AppUtilAppUserManipulator;
use FOSUserBundleCommandCreateUserCommand;
use FOSUserBundleUtilUserManipulator;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputArgument;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleInputInputOption;
use SymfonyComponentConsoleOutputOutputInterface;
use SymfonyComponentConsoleQuestionQuestion;
use SymfonyComponentConsoleStyleSymfonyStyle;

class AppUserCreateCommand extends CreateUserCommand
{
    const PROPERTIES = [
        'first_name' => 'First Name',
        'last_name' => 'Last Name',
        'phone_number' => 'Phone Number'
    ];

    protected static $defaultName = 'app:user:create';

    private $userManipulator;

    public function __construct(AppUserManipulator $userManipulator)
    {
        parent::__construct($userManipulator);
        $this->userManipulator = $userManipulator;
    }


    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        parent::configure();
        $this->setName('app:user:create')
            ->setHelp(preg_replace('/fos:user:create/', 'app:user:create', $this->getHelp()));

        foreach (self::PROPERTIES as $name => $description) {
            $this->addArgument($name, InputArgument::REQUIRED, $description);
        }
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // code from parent start
        $username = $input->getArgument('username');
        $email = $input->getArgument('email');
        $password = $input->getArgument('password');
        $inactive = $input->getOption('inactive');
        $superadmin = $input->getOption('super-admin');
        // code from parent end

        foreach (self::PROPERTIES as $name => $description) {
            ${$name} = $input->getArgument($name);
        }

        $this->userManipulator->createAppUser($username, $password, $email, !$inactive, $superadmin, ...array_keys(self::PROPERTIES));

        $output->writeln(sprintf('Created user <comment>%s</comment>', $username));
    }

    /**
     * {@inheritdoc}
     */
    protected function interact(InputInterface $input, OutputInterface $output)
    {
        parent::interact($input, $output);

        $questions = array();

        foreach (self::PROPERTIES as $name => $description) {
            if (!$input->getArgument($name)) {
                $question = new Question("Please choose a $name:");
                $question->setValidator(function ($property) use ($name){
                    if (empty($property)) {
                        throw new Exception("$name can not be empty");
                    }

                    return $property;
                });
                $questions[$name] = $question;
            }
        }

        foreach ($questions as $name => $question) {
            $answer = $this->getHelper('question')->ask($input, $output, $question);
            $input->setArgument($name, $answer);
        }
    }
}
  

для использования введите

 ./bin/console app:user:create
  

примечание: используя мое решение, вы не должны изменять, services.yaml поскольку службы, определенные мной, например, AppUserManipulator , по умолчанию автоматически подключаются.