Forms 如何嵌套Symfony表单类?

Forms 如何嵌套Symfony表单类?,forms,symfony,doctrine,entity,Forms,Symfony,Doctrine,Entity,我在弄清楚如何为我拥有的三个实体创建嵌套形式的逻辑时遇到了问题:电影、演员和地点。我按照Symfony文档中的说明从数据库生成了Symfony实体(+orm.xml) 我的最终目标是有一个页面,用户可以在其中执行以下任何操作: 创建一个新对象 从下拉菜单中选择“影片”,然后创建新的演员对象以与其关联 从下拉菜单中选择“胶片”,然后创建一个新的位置对象以与其关联 (演员和地点都与电影表有一对多的连接) 然而,我在Symfony中长期以来一直在与嵌套表单的概念作斗争,为了“先走后跑”,我只是尝试

我在弄清楚如何为我拥有的三个实体创建嵌套形式的逻辑时遇到了问题:电影、演员和地点。我按照Symfony文档中的说明从数据库生成了Symfony实体(+
orm.xml

我的最终目标是有一个页面,用户可以在其中执行以下任何操作:

  • 创建一个新对象
  • 从下拉菜单中选择“影片”,然后创建新的演员对象以与其关联
  • 从下拉菜单中选择“胶片”,然后创建一个新的位置对象以与其关联
(演员和地点都与
电影
表有一对多的连接)

然而,我在Symfony中长期以来一直在与嵌套表单的概念作斗争,为了“先走后跑”,我只是尝试将上述每一个表单放在单独的路径中:

  • /newfilm
  • /newactor
  • /newlocation
/New film
我可以毫无问题地开始工作。然而,对于另外两个,我尝试的任何方法似乎都不起作用。下面是我的代码,如果有人能在Symfony中解释嵌套表单的“理论”,以避免不断撞到这堵墙,我将非常感激

因为我的问题对于演员和位置都是一样的,我只是把演员(和电影)的代码放在这里,因为我意识到已经有很多了:

~~~~~~控制器~~~~~ 第二个路由(
/newactor
)具有嵌入/嵌套的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.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;
    }
}
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';
    }
}
/**
 * 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;
    }
}
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>
<?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>
~~~~~~演员~~~~~ Actors.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;
    }
}
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';
    }
}
/**
 * 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\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>
<?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>
我当前的错误消息是:

可捕获的致命错误:AppBundle\Entity\Films类的对象无法转换为字符串

500内部服务器错误-ContextErrorException

我读过一些答案,上面说要将函数添加到我的
Films.php

public function __toString() {
    return $this->name;
}
但是,当我这样做时,我会得到错误:

错误:方法AppBundle\Entity\Films::\uuuToString()不能引发异常

我在网上遇到的其他可能的想法(但不幸没有成功)有:

  • 将表单设置为服务
  • 数据转换器

'choice\u label'=>“film\u title',
添加到表单生成器中。或者您可以在返回电影标题的电影实体中实现一个
\uuu toString()
函数

解决方案:

$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');
        }
    ));

此外,您可能希望实体名称保持单数(例如:Film,而不是Films;Actor而不是Actors),因为在处理x对多实体关系时,这可能会导致不必要的问题。

为什么使用xml?yaml更具可读性感谢您的关注:)我一直在尝试使用“最佳实践”,并阅读了这篇文章,其中建议使用XML,以便将捆绑包解耦以跨项目使用……否则,我同意注释会让我不那么头疼@那篇文章过时了。如果想要“最佳实践”,应该使用注释。非常感谢链接-注释非常清晰,尽管对于解耦包来说,将数据库映射放在单独的文件中而不是与实体php文件交错似乎是有意义的…?在配置文件中使用yaml,在实体和控制器类中尽可能多地使用注释可能的
choice\u标签
已修复,谢谢!您关于保持实体名称单数的评论-我也对此感到好奇。但是,数据库表的“最佳实践”是以复数形式命名表中包含的内容?您可以为实体指定一个单数名称,但为表指定另一个名称,例如@ORM\table(name=“products”),并且您的类名应为Product。