简体   繁体   中英

How to nest Symfony form classes?

I am having issues figuring out the logic of how to create nested forms for the three entities I have: Films , Actors and Locations . I generated my Symfony entities (+ orm.xml ) from my database following the instructions in the symfony docs here .

My ultimate goal would to be have one page where the user can perform any of the following actions:

  • Create a new Films object
  • Select Films from a dropdown menu, and then create a new Actors object to associate to it
  • Select Films from a dropdown menu, and then create a new Locations object to associate to it

( Actors and Locations both have a 1-to-many join with the Films table)

However , I've been struggling with the concept of nested forms in Symfony for a long time and in order to "walk before I can run" I'm just trying to put each of the above into separate routes with separate forms:

  • /newfilm
  • /newactor
  • /newlocation

/New-film I can get working without problem. However, with either of the other two, anything I try doesn't seem to work. The below is my code, if someone can explain the "theory" of nested forms in Symfony to avoid keep hitting this wall would be very much appreciated...!

As my problem is the same for both Actor and Location, I'm only putting the code for for Actors (and Films ) as I realise it's quite a lot already:

~~~~~Controller~~~~~

It is this second route ( /newactor ) which has the embedded/nested formType:

class DefaultController extends FOSRestController
{
    /**
     * @Route("/newfilm", name="new_film")
     */
    public function newFilmAction(Request $request)
    {
        $film = new Films();
        $form = $this->CreateFormBuilder($film)
            ->add('film_title','text',array('label'=>'Film title'))
            ->add('Save','submit',array('label'=>'Add new film'))
            ->getForm();
        $form->handleRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($film);
            $em->flush();
            return $this->redirectToRoute('success_addFilm');
        }

        return $this->render('AppBundle:Default:newfilm.form.html.twig', array(
            'form' => $form->createView(),
        ));
    }


    /**
     * @Route("/newactor", name="new_actor")
     */
    public function newActorAction(Request $request)
    {
        $actor = new Actors();
        $form = $this->createForm(new ActorType(), $actor);
        $form->handleRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($actor);
            $em->flush();
            return $this->redirectToRoute('success_addActor');
        }

        return $this->render('AppBundle:Default:newactor.form.html.twig', array(
            'form' => $form->createView(),
        ));
    }
}

~~~~~Films~~~~~

Films.php

/**
 * Films
 */
class Films
{
    /**
     * @var integer
     */
    private $filmid;

    /**
     * @var string
     */
    private $film_title;

    /**
     * @var \AppBundle\Entity\Actors
     */
    private $actor;



    /**
     * Get filmid
     * @return integer
     */
    public function getFilmid()
    {
        return $this->filmid;
    }

    /**
     * Get film_title
     *
     * @return string
     */
    public function getFilm_title()
    {
        return $this->film_title;
    }

    /**
     * Set film_title
     * @param string $film_title
     * @return Films
     */
    public function setFilm_title($film_title)
    {
        $this->film_title = $film_title;
        return $this;
    }

    /**
     * Set actor
     *
     * @param \AppBundle\Entity\Actors $actor
     *
     * @return Actors
     */
    public function setActor(\AppBundle\Entity\Actors $actor = null)
    {
        $this->actor = $actor;

        return $this;
    }

    /**
     * Get actor
     *
     * @return \AppBundle\Entity\Actors
     */
    public function getActor()
    {
        return $this->actor;
    }
}

Films.orm.xml

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
  <entity name="AppBundle\Entity\Films" table="Films">
    <indexes>
      <index name="fk_Films_actors1_idx" columns="actor_id"/>
    </indexes>
    <id name="filmid" type="integer" column="filmid">
      <generator strategy="IDENTITY"/>
    </id>
    <field name="film_title" type="text" column="film_title" length="65535" nullable="false">
      <options>
        <option name="fixed"/>
      </options>
    </field>
    <many-to-one field="actor" target-entity="Actors" fetch="LAZY">
      <join-columns>
        <join-column name="actor_id" referenced-column-name="actorid"/>
      </join-columns>
    </many-to-one>
  </entity>
</doctrine-mapping>

FilmType.php

class FilmType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('film_title');
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class'=>'AppBundle\Entity\Films'
        ));
    }

    public function getName()
    {
        return 'film';
    }
}

~~~~~Actors~~~~~

Actors.php

/**
 * Entries
 */
class Entries
{
    /**
     * @var integer
     */
    private $actorid;


    /**
     * @var string
     */
    private $actorName;




    /**
     * Set actorid
     *
     * @param integer $actorid
     *
     * @return Actors
     */
    public function setActorid($actorid)
    {
        $this->actorid = $actorid;

        return $this;
    }

    /**
     * Get actorid
     *
     * @return integer
     */
    public function getActorid()
    {
        return $this->actorid;
    }

    /**
     * Set actorName
     *
     * @param string $actorName
     *
     * @return Actors
     */
    public function setActorName($actorName)
    {
        $this->actorName = $actorName;

        return $this;
    }

    /**
     * Get actorName
     *
     * @return string
     */
    public function getActorName()
    {
        return $this->actorName;
    }
}

Actors.orm.xml

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
  <entity name="AppBundle\Entity\Actors" table="Actors">
    <id name="actorid" type="integer" column="actorid">
      <generator strategy="IDENTITY"/>
    </id>
    <field name="actorName" type="text" column="actorName" length="65535" nullable="true">
      <options>
        <option name="fixed"/>
      </options>
    </field>
  </entity>
</doctrine-mapping>

ActorType

class ActorType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('actorName')
            ->add('film','entity',array(
              'class'=>'AppBundle:Films',
                'query_builder'=>function(EntityRepository $er) {
                    return $er->createQueryBuilder('f')
                        ->orderBy('f.film_title','ASC');
                }
            ));
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class'=>'\AppBundle\Entity\Actors'
        ));
    }

    public function getName()
    {
        return 'actor';
    }
}

My current error message is:

Catchable Fatal Error: Object of class AppBundle\\Entity\\Films could not be converted to string

500 Internal Server Error - ContextErrorException

I have read answers that say to add in the function to my Films.php :

public function __toString() {
    return $this->name;
}

However, when I do that, I then get the error:

Error: Method AppBundle\\Entity\\Films::__toString() must not throw an exception

Other possible ideas I've come across online (but unfortunately with no success) are:

  • Setting the Forms as services
  • Data transformers

Add 'choice_label' => 'film_title', to your form builder. That or you can implement a __toString() function within your Film entity that returns the film title.

Solution:

$builder
    ->add('actorName')
    ->add('film','entity',array(
        'class'=>'AppBundle:Films',
        'choice_label'  =>  'film_title',
        'query_builder'=>function(EntityRepository $er) {
            return $er->createQueryBuilder('f')
                ->orderBy('f.film_title','ASC');
        }
    ));

Also, you may want to keep your entity names singular (eg: Film, not Films; Actor not Actors) as this may cause unnecessary issues when dealing with x-to-many entity relationships.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM