Search code examples
phpformssymfonyformbuilder

Populate Multiple Forms Symfony2


I have a Controller where I'm creating a list of open Jobs and populate them in a table via Twig. What I want now is that the last field of each line is an upload form, so that you can add files to one specific job. Unfortunately I have no idea how to handle form requests for multiple forms in one Controller.

Here is the Controller I have now:

/**
 * @Route("/job/pending", name="pendingJobs")
 */
public function jobAction(Request $request)
{
    $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');

    $em = $this->getDoctrine()->getManager();
    $file = new File();
    $form = $this->createFormBuilder($file)
        ->add('file')
        ->add('job','entity',array(
            'class' => 'AppBundle:Job',
            'choice_label' => 'insuranceDamageNo',
        ))
        ->add('save', 'submit', array('label' => 'Create Task'))
        ->getForm();

    $form->handleRequest($request);

    if ($form->isValid()) {

        $job = $em->getRepository("AppBundle:Job")->find($form->getData()->getJob());

        $file->setFile($form->getData()->getFile());
        $file->setPath($form->getData()->getPath());
        $file->setJob($job);

        $em->persist($file);
        $em->flush();

        return $this->redirectToRoute("pendingJobs");
    }



    $jobs = $em->getRepository("AppBundle:Job")->findBy(array(
        'receipt' => true,
        'receiptStatus' => true,
    ));

    return $this->render(
        'default/pending.html.twig',
        array(
            'jobs' => $jobs,
            'form' => $form->createView(),
        )
    );


}

The form works perfectly except for the fact it is only one form AND the "Job" entity is a dropdown list. I would like to have it "pre-selected" for each job to have the right id if possible.

I found something about "createNamedBuilder" HERE (last post) but it is in french and neither do I understand french, nor does the API help at all.

I thought about a foreach for the $jobs, but how do I separate the form handles?

Any hint appreciated!


Solution

  • Create three actions in your controller. One for the mainpage, one for each upload form and one to handle the form:

    /**
     * @Route("/job", name="pendingJobs")
     */
    public function jobAction(Request $request) {
        $em = $this->getDoctrine()->getManager();
    
        $jobs = $em->getRepository("AppBundle:Job")->findAll();
    
        return $this->render(
                    'default/pending.html.twig', array(
                    'jobs' => $jobs,
                )
        );
    }
    
    /*
     * renders an uploadform as a partial from jobAction
     */
    public function jobrowAction(Request $request, Job $job) {
        //$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');
    
        $em = $this->getDoctrine()->getManager();
    
        $file = new File();
        $file->setJob($job); // so that we know to what job this upload belongs!
    
        $form = $this->createUploadForm($file, $job->getId());
    
        return $this->render(
                    'default/pending_job_row.html.twig', array(
                    'job' => $job,
                    'form' => $form->createView(),
                )
        );
    }
    
    /*
     * renders and processes an uploadform
     * 
     * @Route("/job/{id}/update", name="job_upload")
     * @Method("POST")
     */
    public function uploadAction(Request $request, $id) {
        $em = $this->getDoctrine()->getManager();
    
        $file = new File();
    
        // this time we set the job property again cause we only receiced the jobId from the route
        $job = $em->getRepository("AppBundle:Job")->findOneBy(array('id' => $id));
        if (!$job) {
            throw $this->createNotFoundException('Unable to find Job entity.');
        }
        $file->setJob($job);
    
        $form = $this->createUploadForm($file, $id);
        $form->handleRequest($request);
    
        if ($form->isValid()) {
    
            $job = $em->getRepository("AppBundle:Job")->find($form->getData()->getJob());
    
            $file->setFile($form->getData()->getFile());
            $file->setPath($form->getData()->getPath());
            $file->setJob($job);
    
            $em->persist($file);
            $em->flush();
    
            return $this->redirectToRoute("pendingJobs");
        }
    
        // if the form is not valid show the form again with errors
        return $this->render(
                    'default/error.html.twig', array(
                    'form' => $form->createView(),
                )
        );
    }
    
    private function createUploadForm(File $file, $jobId)
    {
        $form = $this->createFormBuilder($file, array(
                    'action' => $this->generateUrl('job_upload', array('id' => $jobId)),
                    'method' => 'POST',
                ))
                ->add('file')
                ->add('save', 'submit', array('label' => 'Create Task'))
                ->getForm();
    
        return $form;
    }
    

    Then make two Twig files:

    {# default/pending.html.twig #}
    
    {% extends 'base.html.twig' %}
    
    {% block body %}
        <table>
            {% for job in jobs %}
                <tr>
                    <td>{{ job.title }}</td>
                    <td>{{ render(controller('AppBundle:Default:jobrow', { 'job': job })) }}</td>
                </tr>
            {% endfor %}
        </table>
    {% endblock %}
    

    and:

    {# default/pending_job_row.html.twig #}
    
    {{ form(form) }}
    

    In your File entity are two methods missing:

    /**
     * Set job
     *
     * @param \AppBundle\Entity\Job $job
     *
     * @return File
     */
    public function setJob(\AppBundle\Entity\Job $job = null)
    {
        $this->job = $job;
    
        return $this;
    }
    
    /**
     * Get job
     *
     * @return \AppBundle\Entity\Job
     */
    public function getJob()
    {
        return $this->job;
    }