1
votes

A dropdown selection menu in a Form is available by using a EntityType form type. It is usefull if you add data. But when you try to edit data with the same FormType class, your $request data are overwritten by your EntityType.

How use the same FormType class when editing the data? (eg. editAction in controller) How pass Request $request data to FormType fields as "defaults, or selected" for element EntityType::class in FormBuilder? Is there something in $builder->add() method I can use like if(['choice_value'] !=== null ) xx : yy?

How to get something like html select selected, from Request object and pass it to xxxFormType class and bind to right EntityType::class element there.

<select>
  <option value="volvo">Volvo</option>
  <option value="vw">VW</option>
  <option value="audi" selected>Audi</option>
</select> 

I've looked at EntityType Field, How to Dynamically Modify Forms Using Form Events and lots of StackOverflow posts, but can't find proper solution.

Controller:

public function editProdPackageAction(Request $request, ProdPackage $prodPackage)
{
    $form = $this->createForm(ProdPackageFormType::class, $prodPackage);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $prodPackage = $form->getData();

        $em = $this->getDoctrine()->getManager();
        $em->persist($prodPackage);
        $em->flush();

        $this->addFlash('success', 'MSG#011E');

        return $this->redirectToRoute('admin_package_list');
    }


    return $this->render(':admin/forms/ProdPackage:edit.html.twig',
        [
            'ppg' => $form->createView(),
        ]
    );
}

Formtype:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add(
            'idCatBig',
            EntityType::class,
            [
                'label'         => '* category',
                'class'         => 'AppBundle\Entity\ProdCategoryBig',
                'choice_value'  => 'id',
                'choice_label'  => 'shortName',

                'multiple'      => false,
                'expanded'      => false,
            ]
        )
        ->add(
            'dateStart',
            DateType::class,
            [
                'label'                     => '* some date',
                'data'  => new \DateTime('now'),
                'choice_translation_domain' => true,
            ]
        )
        ->add(
            'dateEnd',
            DateType::class,
            [
                'label' => '* till',
                    'data'  => new \DateTime('now'),
            ]
        )
        ->add(
            'packageName',
            TextType::class,
            [
                'label' => '* package',
                'attr'  => ['placeholder' => 'default Name'],
                'data'  => 'your default value',
            ]
        )
3
Your question is hard to read. Remove opinions, clearly state your goals and what you have tried.svgrafov
No, it isn't clear.Stephan Vierkant
I think it is clear, he wants to show the existing data on edit instead of having it overwritten by the default data he is setting for create purposes. I personally use listeners, I doubt there is a way around that, I would be interested though if anyone has a cleaner solution (that also avoids adding code to the controller because I like all my edit functions to look alike)Joe Yahchouchi
Your question is hard to read because of your opinion ("great for adding terrible for editing data", "tons of code", "fight Goliat vs David..") and caps (FORM ENTITY TYPE). You've got an interesting question, but you've messed up. When (at least) two people think your question is hard to read, try to ask questions how you can improve it. Just denying it ("It's all clear here") or shouting a people ("What Is not clear!") won't help you.Stephan Vierkant
Thanks for updating your question. I've upvoted it.Stephan Vierkant

3 Answers

1
votes

Problem

You are overriding an object by using the data option.

https://symfony.com/doc/3.2/reference/forms/types/entity.html#data:

( ! ) The data option always overrides the value taken from the domain data (object) when rendering. This means the object value is also overriden when the form edits an already persisted object, causing it to lose its persisted value when the form is submitted.

Solution 1: use the controller

So the solution is pretty simple: don't use data. Instead, set default values in your addProdPackageAction action (or whatever it is called):

public function editProdPackageAction(Request $request)
{
    $prodPackage = new ProdPackage();
    $prodPackage->setDateEnd(new Datetime('now'));

    //example: use Request or other controller methods to modify your entity
    if ($this->getUser()->hasRole('ROLE_ADMIN')) {
        $prodPackage->setCreatedByAdmin(true);
    }

    //your form
}

Solution 2: use your entity constructor

Alternatively, you can use your entity constructor method:

class ProdPackage
{
    //your attributes
    $private $dateEnd;

    public function __construct()
    {
        $this->dateEnd = new Datetime('now');
    }
}
1
votes

This is what I do in my form, I set a "pre set data listener" to check whether or not this is an edit or a create

function buildForm(FormBuilderInterface $builder, array $options) {
    $builder->add(
    'dateStart',
    DateType::class,
    [
    'label' => '* some date'
    //I don't set a data field here because it is an edit
    'choice_translation_domain' => true,
    ]
    )

    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $data = $event->getData();
        $form = $event->getForm();
        if (!$data || null === $data->getId()) {
            //if the entity does not have an ID it means that this is a new entity not an edit. because all edits have been in the database and have an id
            $builder->add(
            'dateStart',
            DateType::class,
            ['label' => '* some date',
            'data' => new \DateTime('now'), //this is a create so I override my previous setting and set a default data
            'choice_translation_domain' => true,]
            )
        }
    });
}

I mainly use this trick to change form fields from required to non required between edits and creates for password fields for example,and sometimes if there is something exceedingly complicated. To change data, honestly, setting default data in constructor is cleaner as Stephan suggests

0
votes

Found another interesting solution

in formTypeclass at $builder

->add(
                    'trBegin',
                    DateTimeType::class,
                    [
                        'label'       => 'Tourney Begins',
                        'required'    => true,
                        'date_widget' => 'single_text',
                        'time_widget' => 'single_text',
                        'date_format' => 'dd.MM.yyyy',
                    ]
                )

and continuing building form add:

        $builder->get('trBegin')->addModelTransformer(
            new CallbackTransformer(
                function ($value) {
                        if (!$value) {
                                return new \DateTime('now + 60 minutes');
                        }

                        return $value;
                },
                function ($value) {
                        return $value;
                }
            )
        );

it sets default date at moment when form is created. This method is very usefull also for EntityType object, where you can pass id of field in form and get selected your current real choice from database (not all list from the begining) very useful when using EntityField also for editing forms

 $builder->get('productCategory')
        ->addModelTransformer(new CallbackTransformer(
            function ($id) {
                if (!$id) {
                    return;
                }

                return $this->em->getRepository('AppBundle:ProductCategory')->find($id);
            },
            function($category) {
                return $category->getId();
            }
        ));