【问题标题】:Symfony 2.7 - prefill entity form from previously stored session valuesSymfony 2.7 - 从先前存储的会话值中预填充实体表单
【发布时间】:2016-03-02 11:09:12
【问题描述】:

在我的 Symfony 2.7.10 项目中,我有一个实体表单,它通过 queryBuilder 从数据库加载一个实体对象。

$builder
    ->add('size', 'entity', [
        'class' => 'AppBundle\Entity\Material',
        'query_builder' => function (EntityRepository $er) {
            return $er->createQueryBuilder('m')
                ->select('m')
                ->where('m.idproduct = :idproduct')->setParameter('idproduct', $this->product->getIdProduct())
                ->groupBy('m.size')
                ->orderBy('m.id');
        },
        'choice_label' => 'size',
        'expanded' => true,
        'constraints' => new NotBlank(),
    ]);

此外,我预先设置了一个表单字段,该字段在用户选择材料尺寸后动态加载。 这是通过 EventSubscriber 完成的

$builder->addEventSubscriber(new AddEndTermsSubscriber('size', $options['locale']));

类如下所示:

...
class AddEndTermsSubscriber implements EventSubscriberInterface
{
    private $propertySize;

    private $locale;

    public function __construct($propertySize, $locale)
    {
        $this->propertySize = $propertySize;
        $this->locale = $locale;
    }

    public static function getSubscribedEvents()
    {
        return [
            FormEvents::PRE_SET_DATA => 'preSetData',
        ];
    }

    private function addFormField(FormInterface $form, Material $material)
    {
        $form->add('endterm', 'entity', [
            'class' => 'AppBundle\Entity\MaterialT',
            'query_builder' => function (EntityRepository $er) use ($material) {
                return $er->createQueryBuilder('t')
                    ->select('mt')
                    ->from('AppBundle:MaterialT', 'mt')
                    ->leftJoin('mt.m', 'm')
                    ->where('m.size = :size')
                    ->setParameter('size', $material->getSize());
            },
            'expanded' => true,
            'mapped' => false,
            'required' => false,
            'placeholder' => false,
            'choice_label' => 'name'.$this->locale,
        ]);
    }

    public function preSetData(FormEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        if ($data === null) {
            return;
        }

        $accessor = PropertyAccess::createPropertyAccessor();

        $formMaterial = $accessor->getValue($data, $this->propertySize);
        $material = ($formMaterial) ? $formMaterial : new Material();

        $this->addFormField($form, $material);
    }
}

在用户选择材料后,我正在通过 ajax 操作为 endTerms 加载表单字段。

提交表单后,我将材料实体和选定的 endTerm 存储在会话中并重定向到下一个表单步骤:

if ($form->get('size')->getData() != null &&
    $request->request->get('endterm') != null) {
    $request->getSession()->set('token/size', $form->get('size')->getData());

    $idEndterm = $request->request->get('endterm');
    $endterm = $this->getDoctrine()->getRepository('AppBundle:MaterialT')
        ->createQueryBuilder('mt')
        ->leftJoin('mt.m', 'm')
        ->where('m.id = :id')
        ->setParameter('id', $idEndterm)
        ->getQuery()
        ->getSingleResult();

    $request->getSession()->set('token/endterm', $endterm);

    //...
}

问题: 用户应该可以编辑他的配置,所以在下一步他可以“编辑他的配置”并返回,我通过表单重定向到上述操作来做到这一点。

public function materialDetailsAction(Request $request)
{
    $material = new Material();

    $product = $request->getSession()->get('token/product');

    $form = $this->createForm(new MaterialType($product), $material, ['locale' => $request->getLocale()]);
//...
}

我怎样才能实现存储的会话值被预填充到表单中?

最好的做法是通过 $options 参数将它们路由到表单,然后做一些 if else 验证?

任何关于解决方案的提示或如何做得更好都受到热烈欢迎。 谢谢

【问题讨论】:

  • 考虑将您的预填充内容从侦听器移至控制器。然后,您可以在您的控制器中检查会话是否已设置,如果未预填充。
  • 感谢您的建议,但监听器是动态调用的 - 那么我该如何组合呢?
  • 我基本上是建议你完全摆脱表单监听器。我不完全理解你的问题,但似乎没有必要。
  • 稍微抽象一下问题:我有一个表单实体,您可以在其中选择材料的大小(单选按钮)。当您选择大小时,有一个 ajax 函数可以根据您选择的大小(oneToMany 关系)动态加载另一个基于实体的单选字段元素。在结果页面上(提交后),您应该能够“编辑”您之前的选择并可以跳回表单。系统知道您选择的材料尺寸(存储在会话中),因此当您尝试编辑配置时,应该预先填充。希望这更容易理解

标签: php ajax forms symfony entity


【解决方案1】:

我终于找到了一个可行的解决方案。

问题的快速总结:

  • 具有材质实体的动态表单以选择尺寸(P10、P20 等)
  • 如果选择了尺寸,您现在应该会看到一个带有一些尾项(例如 leftTerm、rightTerm)的新选择字段
  • 用户现在提交表单,然后在表格中查看他之前的配置
  • 在此结果页面上,他现在可以选择一个编辑选项来更改他的设置
  • 提交时,值存储在会话中
  • 在编辑时,用户应该会看到已经选择了他的配置的表单(然后可以从 P10 更改为 P20 等)

对我帮助很大的提示是: - Symfony2 Setting a default choice field selection

现在开始代码...

在导致配置表单的操作中,我添加了一些会话预配置

// create session cart
$request->getSession()->set('token', []); // token reset per configuration
if (!$request->getSession()->has('cart')) {
    $request->getSession()->set('cart', []);
}
  • 现在在表单操作中,我设置PersistentObject 以在FormType 和依赖的EventSubscribers 中使用它
  • 此外,我扩展了 createForm 调用的 options 参数。
  • 稍后在提交条件中,我只是更新 购物车会话数组

    public function materialDetailsAction(Request $request)
    {
        // back to start if session empty
        if ($request->getSession()->get('token/product') == null) {
            return $this->redirectToRoute('configurator_product');
        }
        $product = $request->getSession()->get('token/product');
    
        // persistence manager
        PersistentObject::setObjectManager($this->getDoctrine()->getManager());
    
        $material = new Material();
        $form = $this->createForm(new MaterialType($product), $material, [
            'locale' => $request->getLocale(),
            'sessionCart' => ($request->getSession()->has('editId')) ? $request->getSession()->get('token') : null,
        ]);
    
        // ... validation etc.
    
        // update session for shopping cart
        $shoppingCart = $request->getSession()->get('cart');
        if ($request->getSession()->has('editId')) {
            $shoppingCart[$request->getSession()->get('editId') -1] = $request->getSession()->get('token');
        } else {
            $shoppingCart[] = $request->getSession()->get('token');
        }
    
        $request->getSession()->set('cart', $shoppingCart);
    
        // ... redirect
    }
    
  • 表单类型中,我使用'sessionCart' => null,默认值扩展了configureOptions数组

  • 然后是 data 选项,该选项要么为 false(如果会话值为 null),要么我将得到这样的 Object:

$defaultData = ($options['sessionCart'] != null) ? PersistentObject::getObjectManager()->getRepository('AppBundle:Material') ->find($options['sessionCart']['size']->getId()) : false;

当按下 edit 按钮时,最后一个 ajax 动作

// get selected leaflet id and current session
$id = $request->request->get('id');
$session = $request->getSession();

// get selected configuration (table ID != array ID)
$selectedToken = $session->get('cart')[$id - 1];

// reset token
$request->getSession()->set('token', []);
$request->getSession()->set('editId', $id);

// set token from selected configuration
foreach ($selectedToken as $key => $value) {
    $session->set('token/'.$key, $value);
}

return new JsonResponse();

希望这对将来的某人有所帮助,或者如果有人有一些需要添加(增强功能等)的人,欢迎您!

【讨论】:

    猜你喜欢
    • 2013-03-08
    • 2019-02-21
    • 2014-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-12
    • 1970-01-01
    • 2013-07-14
    相关资源
    最近更新 更多