Generate same form type on same page multiple times Symfony2 Generate same form type on same page multiple times Symfony2 symfony symfony

Generate same form type on same page multiple times Symfony2


Now in Symfony 3.0 they changed the createNamedBuilder, so it's possible to solve this by only calling:

use AppBundle\Form\ShippingTrackCodeReturnType;$uniqueForm = $this->get('form.factory')->createNamedBuilder('ship_form_'.$orderRecord->getId(), ShippingTrackCodeReturnType::class, $orderRecord)->getForm();

So, you just need to loop to display and save them:

foreach ($invoice->getOrderRecords() as $key => $orderRecord) {    // creates the forms with different names    $returnShipTrackCodeForm = $this->get('form.factory')->createNamedBuilder('ship_form_'.$orderRecord->getId(), ShippingTrackCodeReturnType::class, $orderRecord)->getForm();    $returnShipTrackCodeForm->handleRequest($request);    if ($returnShipTrackCodeForm->isSubmitted() && $returnShipTrackCodeForm->isValid())     {        // flush object    }    $returnForms[$orderRecord->getId()] = $returnShipTrackCodeForm;}  


Hope this will help somebody.

The proper way will be to use Symfony's form.factory service to create a named builder and in turn create a form from that. I recommend creating a function for the form creation process as (as I understand, correct me if I', wrong) each FormBuilder instance must be different to create a different form.

Just as an explanation of the purpose of the code:I needed to show a list of open tickets in the help system with ability to assign a person (authorized without logging in) based on their login token to the ticket. That required to display N forms on single page that would be unique but generated from single "template" or Type if you will.

This code achieved that without any hacks. There is just no shorthand method to createForm with specific ID (name) from the controller AFAIK.

ex.

public function generateFormForTicketAssignment(Ticket $ticket) {       $ticketAssignType = new TicketAssignInvokeType();    $ticketAssignType->setNameSuffix($ticket->getId());    /**     * @var Symfony\Component\Form\FormBuilderInterface     */    $builder = $this->get('form.factory')->createNamedBuilder($ticketAssignType->getName(), $ticketAssignType );    $builder->setAutoinitialize(true);    $formTicket = $builder->getForm();    $formTicket->get('ticket')->setData($ticket);    return $formTicket;}

AND (this is important part) the ticketAssignInvokeType looks like this:

class TicketAssignInvokeType extends AbstractType {private $nameSuffix = null;private $name = 'ticket_assign_invoke';public function __constructor(string $suffix = null)   {    //parent::__construct();    $this->nameSuffix = $this->generateNameSuffix();}private function generateNameSuffix() {    if ($this->nameSuffix == null ||  $this->nameSuffix == '') {        $generator = new SecureRandom();        //change data to alphanumeric string        return bin2hex($generator->nextBytes(10));    }    return $this->nameSuffix;}/** *  * @param string $suffix When set suffix added to form will use selected  * string (note it should be unique for all forms on a page). */public function setNameSuffix($suffix){   $this->nameSuffix = $suffix;}public function buildForm(FormBuilderInterface $builder, array $options)   {    $builder        ->add('loginToken',new AuthenticateTokenType())        ->add('ticket',new TicketIDType())        ->add('save', 'submit', array('label' => 'form.button.assign'))    ;}public function getName()    {    if ($this->nameSuffix == null || $this->nameSuffix == "" ) {        $this->nameSuffix = $this->generateNameSuffix();    }    return $this->name .'_'. $this->nameSuffix;}public function configureOptions(OptionsResolver $resolver)    {   // ...}}

This is somewhat overly complex logic and testing for name suffix but at some point I could not figure out why all I go was form name without suffix hence the complexity.

PS. I have created forms as services and will later test if a service can be used as well as this was my first intention.

PS2. I am not sure about the setAutoinitialize method yet, but will test it furtner as well.