【问题标题】:Array to CollectionType in Symfony 3.4 formsSymfony 3.4 形式的 CollectionType 数组
【发布时间】:2018-10-06 05:33:25
【问题描述】:

我有实体的数组 Cart,我想生成一般形式,看起来像在屏幕上。

如您所见,我希望在每一行中都有可编辑字段 Quantity,它代表购物车实体,并且我希望能够一次更新所有这些字段。

class Cart
{
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @ORM\ManyToOne(targetEntity="User", inversedBy="carts")
 */
private $userId;

/**
 * @ORM\ManyToOne(targetEntity="Product", inversedBy="carts")
 */
protected $product;

/**
 * @ORM\Column(type="integer")
 */
private $quantity;

/*gettes & setters */
}

目前,我有一个表单,它想要接收 CollectionType 来处理它,但是 - 我只有一个实体数组,所以它正在转储 LogicalException。

我需要做什么 - 有什么方法可以将数组解析为 CollectionType,或者我可以通过其他方式从数据库中获取一组购物车实体?:

$carts=$this->getDoctrine()->getRepository(Cart::class)->findByUserId($user);

【问题讨论】:

    标签: php forms symfony collections symfony-forms


    【解决方案1】:

    Symfony Documentation on How to Embed a Collection of Forms 中有一个示例说明如何实现您希望实现的目标。

    对于您的特定用例,您需要创建一个UserCartsForm 和一个单独的CartsForm。

    在您的UserCart 中,将carts 字段添加为CollectionType。 然后 Symfony 会将字段处理为一系列表单。

    src/AppBundle/Form/UserCart.php

    namespace AppBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\Extension\Core\Type as FormType;
    
    class UserCartsForm extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
             $builder->add('carts', FormType\CollectionType::class, [
                 'label' => false,
                 'entry_type' => CartsForm::class,
                 'entry_options' => array('label' => false),
             ]);
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'data_class' => User::class,
            ]);
        }
    }
    

    将您希望在表单上编辑的字段添加到 CartsForm

    src/AppBundle/Form/CartsForm.php

    namespace AppBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\Extension\Core\Type as FormType;
    
    class CartsForm extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
             $builder->add('quantity', FormType\IntegerType::class, [
               'label' => false
                //...
             ]);
             //...
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'data_class' => Cart::class,
            ]);
        }
    }
    

    在您的控制器中,将用户实体引用为您的 UserCartsForm 数据。

    src/AppBundle/Controller/DefaultController.php

    namespace AppBundle\Controller;
    
    use AppBundle\Form\UserCartsForm;
    
    class DefaultController extends Controller
    {
    
       /**
        * @Route('/user/{id}/carts')
        */
       public function userCartsAction(Request $request, User $user)
       {
          $form = $this->createForm(UserCartsForm::class, $user);
          $form->handleRequest($request);
          if ($form->isSubmitted() && $form->isValid()) {
             //... process entity
             //$this->getDoctrine()->getManager()->flush();
             return $this->redirectToRoute('some_route');
          }
    
          return $this->render('user_carts_form.html.twig', [
              'form' => $form
          ]);
       }
    }
    

    然后您应该能够轻松地从树枝模板中检索数据以按照您的意愿进行渲染。

    app/Resources/views/user_carts_form.html.twig

    {% form_start(form) %}
       <table>
       <thead>
       <tr>
           <td>Name</td>
           <td>Quantity</td>
           <td></td>
       </tr>
       </thead>
       <tbody>
       {% for cart in form.carts %}
       {% set cartEntity = cart.vars.data %}
       <tr>
           <td>{{ cartEntity.product.name }}</td>
           <td>{{ form_widget(cart.quantity) }}</td>
           <td><a class="button" href="{{ path('remove_cart_action', { id: cartEntity.id }) }}">Delete <icon/></a></td>
       <tr>
       {% endfor %}
       </tbody>
       </table>
       <button type="submit">Submit</button>
    {% form_end(form) %}
    

    实体约束更新

    默认情况下,Symfony 将在验证表单时使用分配给实体的所有约束 (Default),从而导致 $form-&gt;isValid() 返回 false。

    https://symfony.com/doc/3.4/validation/groups.html

    如果没有指定组,则属于该组的所有约束 Default 将被应用。

    要解决此问题,请使用Validation Groups 分隔实体约束并在其各自的表单上声明所需的组。

    例子:

    src/AppBundle/Entity/User.php

    namespace AppBundle\Entity;
    
    use Symfony\Component\Validator\Constraints as Assert;
    
    /**
     * @ORM\Entity
     */
    class User
    {
        
        /**
         * @Assert\NotBlank(groups={"registration"})
         */
        private $username;
        
        //...
    }
    

    然后到所需表单上的use your validation group(s),在本例中为RegistrationForm,您将AbstractTye::configureOptions 中所需的组声明为OptionsResolver:$defaults 之一。

    src/AppBundle/Form/RegistrationForm.php

    namespace AppBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    
    class RegistrationForm extends AbstractType
    {
        //...
    
         public function configureOptions(OptionsResolver $resolver)
         {
             $resolver->setDefaults([
                 'data_class' => User::class,
                 'validation_groups' => ['registration']
             ]);
         }
    
    }
    

    现在User::NotBlank 约束将仅适用于RegistrationForm::isValid() 或任何其他声明注册验证组的表单。

    【讨论】:

    • 需要更改一些东西以 100% 与我的项目相匹配,但至少效果很好。谢谢伙计,我欠你一大杯啤酒:D
    • 但还有一件事——如何正确提交和坚持?表单没有进入 isValid && isSubmitted 语句,所以它只是“本地”更改购物车实体,而不将其保存到数据库中
    • 检查表单错误,看看为什么它没有通过有效。我不确定您对表单或实体有什么限制。如果约束在实体上,您将需要创建组。如果启用了 CSRF 令牌,请确保您的表单中有 CSRF 令牌,因为这将阻止表单有效。
    • 它使用来自用户实体的 plainPassword 字段抛出错误,该字段具有“NotBlank”断言。有办法正确遵守吗?
    • 删除此断言后,一切正常,但如果您能帮助我处理该错误,那就太好了...提交表单时可能有点“如果”?我不知道...
    猜你喜欢
    • 1970-01-01
    • 2019-03-31
    • 2020-07-30
    • 2019-01-07
    • 1970-01-01
    • 1970-01-01
    • 2022-01-01
    • 1970-01-01
    • 2017-01-15
    相关资源
    最近更新 更多