Search code examples
phpsymfonysymfony-formssymfony-2.3

FormComponent - Pass Entity with Form to Template


Lets say i have a one to many relation:

  • A company can own several pictures
  • I have company entity and image entity

Now, I want to display all pictures of a company within a template. And i also want to make theese pictures directly editable. I thought of adding a form to each Entity of the DoctrineArrayCollection and pass them to the template. In Template if somebody clicks on a picture the corresponding should be fade in, the should be able to edit the pictures description and pass it through ajax to a controller.

In my entity I added a field without annotations:

    private $form;

    public function setForm(MyPictureForm $form)
    {
        $this->form = $form;
    }

    public function getForm()
    {
        return $this->form;
    }

Now in my controller I add a form instance to every picture of the company:

     //office images with forms
        $officeImages = array();
        foreach($company->getOfficeImages() as $image)
        {
            $form = $this->get('companybundle.imagedescription.form.factory')->createForm();
            $form->setData($image);
            $image->setForm($form->createView());
            array_push($officeImages, $image);
        }


        return $this->render('CompanyBundle:Company:Show/show.html.twig', array(
            'company'         => $company,
            'officeImages'    => $officeImages
        ));

    }

And in my template i render it like this way:

           {% for image in officeImages %}
            <a href="#" title="{% if image.description %}{{ company.description }}{% else %}CLICK HERE FOR EDIT{% endif %}">

                {% if image.image %}
                        <img src="{{ vich_uploader_asset(image, 'image') | imagine_filter('company_office_image_thumb') }}"
                             alt="{% if image.description %}{{ company.description }}{% endif %}"/>
                {% else %}
                    {% image '@UserBundle/Resources/public/img/nopic_logo.jpg' output='/images/nopic_logo.jpg' %}
                    <img src="{{ asset_url }}" alt="Joblogo"/>
                    {% endimage %}
                {% endif %}

            </a>
                {{ form(image.form) }}
            {% else %}
                <p>Es sind noch keine Images vorhanden</p>
            {% endfor %}

At the end there is a lot if javascript stuff which handels the fade in / fade out of the form and their submitting.

Is this the correct way to handle my case? I think not cause passing a form for every picture seems like overhead?

The reason why I am working with forms instead of simply passing data out of a manual added input field is csrf protection and the smart usage of the form component.


Solution

  • As you said keeping form object in entity isn't a good idea. It should represents model data.

    I've got two solutions for this:

    I. Pass array of forms with image id as a key

    $officeImages = array();
    $imageForms = array();
    
    foreach($company->getOfficeImages() as $image)
    {
        $form = $this->get('companybundle.imagedescription.form.factory')->createForm();
        $form->setData($image);
    
        $imageForms[$image->getId()] = $form->createView();
    }
    
    
    return $this->render('CompanyBundle:Company:Show/show.html.twig', array(
        'company'         => $company,
        'officeImages'    => $officeImages,
        'imageForms'      => imageForms
    ));
    

    and in show.html.twig

    {% for image in officeImages %}
    
         {# your image display code #}
    
         {{ form(imageForms[image.id]) }}
    {% endfor %}
    

    II. render partial for single image

    in controller

    public function showAndEditImageAction(Image $image) 
    {
         $form = $this->get('companybundle.imagedescription.form.factory')->createForm();
         $form->setData($image);
    
         return $this->render(
            'CompanyBundle:Company:Show/showAndEditImage.html.twig', array(
            'image' => $image,
            'imageForm'  => $form->createView()
         ));
    }
    

    in twigs

    {# CompanyBundle:Company:Show/showAndEditImage.html.twig #}
    
    {# your image display code #}
    
    {{ form(imageform) }}
    
    {# CompanyBundle:Company:Show/show.html.twig #}
    
    {% for image in officeImages %}
        {{ render(controller('CompanyBundle:Company:showAndEditImage', 
            { 'image': image })) }}
    {% endfor %}