代码之家  ›  专栏  ›  技术社区  ›  Mike Doe Backs

只有表单有效时,才能将symfony中的表单数据映射到对象?

  •  0
  • Mike Doe Backs  · 技术社区  · 7 年前

    想象一下symfony中的示例形式:

    public function buildForm(FormBuilderInterface $builder)
    {
        $builder
            ->add('email', EmailType::class, [
                'constraints' => 
                    new NotBlank(),
                    new IsUnique(),
                ],
            ])
            ->add('password', PasswordType::class, [
                'constraints' => 
                    new NotBlank(),
                    new IsStrongEnough(),
                ],
            ])
    }
    

    现在,当我提交表单并确保其有效时,我希望 $form->getData() 把我的DTO打回 CreateAccountCommand :

    final class CreateAccountCommand
    {
        private $email;
        private $password;
    
        public function __construct(string $email, string $password)
        {
            $this->email = $email;
            $this->password = $password;
        }
    
        public function getEmail(): string
        {
            return $this->email;
        }
    
        public function getPassword(): string
        {
            return $this->password;
        }
    }
    

    控制器示例:

    $form = $this->formFactory->create(CreateAccountForm::class);
    $form->handleRequest($request);
    
    if ($form->isSubmitted() && $form->isValid()) {
        $this->commandBus->dispatch($form->getData());
    
        return new JsonResponse([]);
    }
    

    我不能直接用这个类 data_class ,因为窗体显然希望模型具有允许空值的setter。表单本身工作得很好,验证也是如此。

    我试着用 Data mapper 方法,但 mapFormsToData 方法在验证之前被调用。

    这有可能吗?或者我应该以数组的形式获取数据,并在窗体外部创建对象?

    3 回复  |  直到 7 年前
        1
  •  0
  •   Cid Gordon    7 年前

    以下是我如何使用symfony 4.1处理表单

    假设您需要一个简单的添加表单

    形式

    class CreateAccountForm extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder)
        {
            //same method than yours
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults(array('data_class' => 'App\Entity\CreateAccountCommand'));
        }
    }
    

    控制器

    public function add(Request $request)
    {
        $createAccountCommand = new CreateAccountCommand();
    
        //The object is "injected" to the form, that way, it's mapped when submitted
        $form = $this->get('form.factory')->create(CreateAccountForm::class, $createAccountCommand);
    
        //Form was submitted
        if ($request->isMethod('POST') && $form->handleRequest($request)->isValid())
        {
            //no need to getData(), the object $createAccountCommand is directly mapped to the form and can be used as is
            $em = $this->getDoctrine()->getManager();
            //persist, convert to json, or whatever
            $em->persist($createAccountCommand);
            $em->flush();
    
            return ($this->redirectToRoute('someRoute'));
        }
    
        //form not submitted, or has been submit with errors (isValid() == false)
        return ($this->render('account/add.html.twig',
                array('form' => $form->createView())));
    }
    
        2
  •  0
  •   Fabien Papet    7 年前

    有一种方法,但对于你想做的事情来说,这太复杂了。您需要为此使用数据映射器。

    我知道这不是最好的解决方案,但最简单的解决方案是(在我看来)向模型类中添加setter并允许空值(solution1)。另一个解决方案是为表单使用另一个对象,并在提交后生成命令(您不需要修改命令-解决方案2)。

    public function handleForm(Request $request)
    {
        /// SOLUTION 1
        $form = $this->createForm(RegisterFormType::class, new CreateAccountCommand());
        $form->handleRequest($request);
    
        if($form->isSubmitted() && $form->isValid()) {
            $command = $form->getData();
            // do whatever you want
        }
        // ... 
    
        // SOLUTION 2
        $obj = new \stdClass();
        $obj->login = '';
        $obj->password = '';
        $form = $this->createForm(LoginFormType::class, $obj);
        $form->handleRequest($request);
    
        if($form->isSubmitted() && $form->isValid()) {
            $data = $form->getData();
            $command = new CreateAccountCommand($data->login, $data->password);
            // do whatever you want
        }
    }
    

    使用此模型:

    final class CreateAccountCommand
    {
        //// ... 
        /**
         * @param string $email
         */
        public function setEmail(string $email): void
        {
            $this->email = $email;
        }
    
        /**
         * @param string $password
         */
        public function setPassword(string $password): void
        {
            $this->password = $password;
        }
    
    }
    
        3
  •  0
  •   Mike Doe Backs    7 年前

    php-berlin小组的同事为我的这个确切的用例创建了一个极好的工具: the Rich Model Forms .