0

On my Symfony 2.7.10 project I have an entity form which loads a material object from the database via the 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(),
    ]);

Additionally I'm pre setting a form field which is dynamically loaded after the users chose a material size. This is done via an EventSubscriber

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

The class looks like this:

...
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);
    }
}

Via an ajax Action I'm loading the form field for endTerms after the user selected the material.

After the form is submitted I'm storing the material entity and the selected endTerm in the session and redirect to the next form step:

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);

    //...
}

The problem: The user should have the possibility to edit his configuration, so on the next step he can "edit his configuration" and go back which I do by redirecting to the above action with the form.

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

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

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

How can I achieve, that the stored session values are getting prefilled to the form?

Is the best practise to route them via the $options parameter to the form and then do some if else validation ?

Any hints for a solution or how to do better are warmly welcome. Thanks

rogaa
  • 370
  • 2
  • 16
  • Consider moving your prefill stuff from the listener to the controller. In your controller you can then check to see if the session is set and if not prefill. – Cerad Mar 02 '16 at 15:26
  • Thanks for you suggestion, but the listener is called dynamically - so how can I combine this? – rogaa Mar 09 '16 at 12:21
  • I am basically suggesting that you get rid of the form listener completely. I don't fully understand your question but it seems unnecessary. – Cerad Mar 09 '16 at 12:41
  • to abstract the question a little bit: I have a form entity, where you can choose a size of a material (radio buttons). When you pick a size, there is an ajax function to dynamically loads another entity based radio field element on what size you've chosen (oneToMany relation). On the result page (after the submit) you should be able to "edit" your previous choice and can jump back to the form. The system knows what material size you've chosen (stored in session), therefore when you try to edit the configuration it should be prefilled. Hope this is a bit more understandable – rogaa Mar 09 '16 at 12:50

1 Answers1

0

I finally managed a working solution.

Quick summary of the problem:

  • A dynamic form with a material entity to choose a size (P10, P20 etc.)
  • If a size has been picked, you should now see a new choice field with some endterms (e.g. leftTerm, rightTerm)
  • the user now submits the form and then see his previous configuration in a table
  • On this result page, he can now choose an edit option to change his setting
  • On submit the values are stored in the session
  • On Edit the user should see the form already selected with his configuration (and then can change from P10 to P20 etc.)

The hint which helped me a lot is: - Symfony2 Setting a default choice field selection

Now to the code...

In my action which leads to the configuration form I added some session pre confiugration

// create session cart
$request->getSession()->set('token', []); // token reset per configuration
if (!$request->getSession()->has('cart')) {
    $request->getSession()->set('cart', []);
}
  • Now in the form action I set the PersistentObject to get usage of it in the FormType and the depending EventSubscribers
  • Additionally I extended the options parameter of my createForm calling.
  • Later in the submit condition I just update the cart session array.

    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
    }
    
  • In the form type I extend the configureOptions array with a 'sessionCart' => null, default value

  • And then there is the data option which is either false (if the session value is null) or I'll get the Object like this:

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

Finally an ajax action when pressing the edit button

// 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();

Hope this helps someone in the future or if anyone has some cents to add (enhancements etc.) You're warmly welcome!

Community
  • 1
  • 1
rogaa
  • 370
  • 2
  • 16