Symfony2:使用表单事件的动态表单
Symfony2: dynamic form using Form Events
我在 Symfony2 项目中制作 动态表单 时遇到了一些问题。我试着按照文档 here, but it hard to understand how to really proceed with it. I try to have a form like this on the page.
确实,我需要创建一个动态表单 当从之前的下拉列表中选择一个值时,下拉列表会动态更改它们的值。
此动态表单涉及 4 个实体:
Parcsimmobilier.php
<= ManyToOne Ensembles.php
<=
ManyToOne Batiments.php
<= ManyToOne Zonestechnique.php
1- first 下拉列表涉及 parc,当我在此下拉列表中选择一个值时,会出现整体下拉列表。整体下拉列表的值必须属于我首先选择的parcs。
2- 带有整体值的第二个下拉菜单:当我选择一个值时,出现第三个下拉菜单,它涉及我的区域类别(实体 Zonestechnique)。
3- 区域类别下拉菜单有 多个 选项,但是当我选择值“outside”时,最后一个带有出现 batiments 值,但不可避免地具有值 "none"。但是,如果我选择 其他值 而不是 "outside",则出现所有 baiments (属于我之前选择的合奏) 的 baiments 下拉列表。
4- 当我完成所有这些步骤时,会出现表格的其余部分(区域技术实体表格)
当我想在我的数据库中 add/create 一个新的 Zones 技术时,我必须继续这个表格。
这是我 entities
的代码:
//Parcsimmobilier entity
class Parcsimmobilier
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=150, nullable=false)
*/
private $nom;
/**
* @var \Doctrine\Common\Collections\Collection
*
* @ORM\OneToMany(targetEntity="Ensembles", mappedBy="parcsimmobilier")
*/
private $ensembles;
//end of Parcsimmobilier entity
//Ensembles entity
class Ensembles
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=150, nullable=false)
*/
private $nom;
/**
* @var \Parcsimmobilier
*
* @ORM\ManyToOne(targetEntity="Parcsimmobilier")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="parcsimmobilier_id", referencedColumnName="id")
* })
*/
private $parcsimmobilier;
//end of Ensembles entity
//Batiments entity
class Batiments
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=150, nullable=true)
*/
private $nom;
/**
* @var \Ensembles
*
* @ORM\ManyToOne(targetEntity="Ensembles")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="ensembles_id", referencedColumnName="id")
* })
*/
private $ensembles;
//end of Batiments entity
//Zonestechnique entity
class Zonestechnique
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=150, nullable=false)
*/
private $nom;
/**
* @var \Batiments
*
* @ORM\ManyToOne(targetEntity="Batiments")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="batiments_id", referencedColumnName="id")
* })
*/
private $batiments;
/**
* @var \Doctrine\Common\Collections\Collection
*
* @ORM\ManyToMany(targetEntity="Categorieszonestechnique")
* @ORM\JoinTable(name="zonestechnique_categorieszonestechnique",
* joinColumns={
* @ORM\JoinColumn(name="zonestechnique_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @ORM\JoinColumn(name="categorieszonestechnique_id", referencedColumnName="id")
* }
* )
*/
private $categorieszonestechnique;
根据文档,这是我的表单类型,ZonesTechniqueType.php
:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class ZonestechniqueType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('parcsimmobilier', 'entity', array(
'class' => 'EnexgirDatabaseBundle:Parcsimmobilier',
'property' => 'nom',
'multiple' => false,
'required' => true,
'mapped' => false,
'empty_value' => '-- sélectionner un parc --',
'label' => 'Choisir le parc immobilier : ',
'attr' => array('class' => 'col-xs-3 form-control')))
->add('nom')
->add('localisation')
->add('commentaire')
->add('categorieszonestechnique')
->add('batiments');
$formModifier = function (FormInterface $form, Parcsimmobilier $parc = null) {
$ensembles = null === $parc ? array() : $parc->getEnsembles();
$form->add('ensembles', 'entity', array(
'class' => 'EnexgirDatabaseBundle:Ensembles',
'property' => 'nom',
'multiple' => false,
'required' => true,
'mapped' => true,
'empty_value' => '-- sélectionner un ensemble --',
'label' => 'Choisir l\'ensemble : ',
'query_builder' => function(EntityRepository $er) use ($parc) {
return $er->createQueryBuilder('e')
->where("e.parcsimmobilier = :parcsimmobilier_id")
->orderBy('e.nom', 'ASC')
->setParameter(':parcsimmobilier_id', $parc);
},
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity
$data = $event->getData();
$formModifier($event->getForm(), $data->getEnsembles());
}
);
$builder->get('parcsimmobilier')->addEventListener(
FormEvents::POST_SUBMIT,
这是我使用的控制器方法:
public function addZonesConformitesAction(Request $request) {
$em=$this->getDoctrine()->getManager();
$zoneConformite = $em->getRepository('EnexgirDatabaseBundle:Zonestechnique');
$zoneConformite = new Zonestechnique;
$form=$this->createForm(new ZonestechniqueType(), $zoneConformite);
$form->handleRequest($request);
//instruction if à plusieurs conditions: ici si la method est POST et si le formulaire est valide
if ($request->isMethod('POST') | ($form->isValid())) {
$form->bind($request);
$zoneConformite = $form->getData();
$em->persist($zoneConformite);
$em->flush();
return $this->redirect($this->generateUrl('indexRechercheZones'));
}
//retourner le formulaire d'ajout si c'est invalide
else {
return $this->render('EnexgirGestionEquipementsTechniquesBundle:ZonesTechnique:ajouterZonesTechniqueConformites.html.twig', array('form' => $form->createView() ));
}
}
最后,我的树枝的块内容:
{% block content %}
<div class="panel-heading">
<h3 class="panel-title"> Gestion Des Zones techniques -<small> (ajout)</small></h3>
</div>
{# Panel Body #}
<div class="panel-body">
{# Menu #}
<div class="panel-body">
<ul class="nav nav-tabs">
<li role="presentation">
<a href="{{ path('indexRechercheZones') }}">Visualisation des Zones techniques</a>
</li>
<li role="presentation" class="active">
<a>Ajouter une Zone technique et ses Conformités</a>
</li>
</ul>
</div>
{# Formulaire #}
<div class="page-header">
<h4><b><u>Ajouter une zone technique et ses conformites</u></b><small> (créer une zone technique qui n'existe pas encore, ainsi que ses conformités respectives)</small></h4>
<br>
<form action="{{ path('ajouterZonesConformites_process') }}" method="POST" {{ form_enctype(form) }}>
<div>
{{ form_start(form) }}
{{ form_row(form.parcsimmobilier) }} {# <select id="meetup_sport" ... #}
{{ form_row(form.ensembles) }} {# <select id="meetup_position" ... #}
{{ form_end(form) }}
{# validation #}
<input type="submit" value="Ajouter" class="btn btn-success"/>
</div>
<br>
</form>
</div>
</div>
{% endblock %}
目前,我还没有在我的树枝中引入Ajax代码,因为我有这个错误:
Attempted to call method "getEnsembles" on class
"Enexgir\DatabaseBundle\Entity\Zonestechnique" in
C:\wamp\www\MyPathToMyProject\src\Enexgir\DatabaseBundle\Form\ZonestechniqueType.php
line 65.
这是第 65 行的代码:
$formModifier($event->getForm(), $data->getEnsembles());
我知道 Ensembles.php
和 Parcsimmobilier.php
与 Zonestechnique.php
没有关系,但我按照文档进行操作,但我不太了解这是如何工作的。有人可以帮助我理解并制作此表格吗?
提前致谢。
symfony 表单事件系统肯定可以使用一些流线型。话虽如此,这是有可能实现的。对于将出现的每个下拉列表,您将需要 2 个表单事件 (PRE_SET_DATA/POST_SUBMIT)。您应该能够将所有 PRE_SET_DATA 事件合并到 1 个函数中,但是您必须在单独的函数中处理 POST_SUBMIT 事件,因为它们绑定到表单
您需要将 if 语句更改为:
if($request->isMethod('POST') && !$request->isXmlHttpRequest() && $form->isValid())
你也不需要打电话:
$form->bind($request);
如果您已经打过电话:
$form->handleRequest($request);
后者是第一个的替代品。
仅通过 "processing" 非 AJAX 请求的表单,这将允许您的表单获取更新的数据并触发表单事件和 return 它。
@Chausser 提出了一个很好的观点,因为在该部分的代码中 $formModifier($event->getForm(), $data->getEnsembles());
您传递了 Ensembles
的实例,因此不需要再次调用 $data->getEnsembles()
。
希望对你有帮助
我在 Symfony2 项目中制作 动态表单 时遇到了一些问题。我试着按照文档 here, but it hard to understand how to really proceed with it. I try to have a form like this on the page.
确实,我需要创建一个动态表单 当从之前的下拉列表中选择一个值时,下拉列表会动态更改它们的值。
此动态表单涉及 4 个实体:
Parcsimmobilier.php
<= ManyToOneEnsembles.php
<= ManyToOneBatiments.php
<= ManyToOneZonestechnique.php
1- first 下拉列表涉及 parc,当我在此下拉列表中选择一个值时,会出现整体下拉列表。整体下拉列表的值必须属于我首先选择的parcs。
2- 带有整体值的第二个下拉菜单:当我选择一个值时,出现第三个下拉菜单,它涉及我的区域类别(实体 Zonestechnique)。
3- 区域类别下拉菜单有 多个 选项,但是当我选择值“outside”时,最后一个带有出现 batiments 值,但不可避免地具有值 "none"。但是,如果我选择 其他值 而不是 "outside",则出现所有 baiments (属于我之前选择的合奏) 的 baiments 下拉列表。
4- 当我完成所有这些步骤时,会出现表格的其余部分(区域技术实体表格)
当我想在我的数据库中 add/create 一个新的 Zones 技术时,我必须继续这个表格。
这是我 entities
的代码:
//Parcsimmobilier entity
class Parcsimmobilier
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=150, nullable=false)
*/
private $nom;
/**
* @var \Doctrine\Common\Collections\Collection
*
* @ORM\OneToMany(targetEntity="Ensembles", mappedBy="parcsimmobilier")
*/
private $ensembles;
//end of Parcsimmobilier entity
//Ensembles entity
class Ensembles
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=150, nullable=false)
*/
private $nom;
/**
* @var \Parcsimmobilier
*
* @ORM\ManyToOne(targetEntity="Parcsimmobilier")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="parcsimmobilier_id", referencedColumnName="id")
* })
*/
private $parcsimmobilier;
//end of Ensembles entity
//Batiments entity
class Batiments
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=150, nullable=true)
*/
private $nom;
/**
* @var \Ensembles
*
* @ORM\ManyToOne(targetEntity="Ensembles")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="ensembles_id", referencedColumnName="id")
* })
*/
private $ensembles;
//end of Batiments entity
//Zonestechnique entity
class Zonestechnique
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=150, nullable=false)
*/
private $nom;
/**
* @var \Batiments
*
* @ORM\ManyToOne(targetEntity="Batiments")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="batiments_id", referencedColumnName="id")
* })
*/
private $batiments;
/**
* @var \Doctrine\Common\Collections\Collection
*
* @ORM\ManyToMany(targetEntity="Categorieszonestechnique")
* @ORM\JoinTable(name="zonestechnique_categorieszonestechnique",
* joinColumns={
* @ORM\JoinColumn(name="zonestechnique_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @ORM\JoinColumn(name="categorieszonestechnique_id", referencedColumnName="id")
* }
* )
*/
private $categorieszonestechnique;
根据文档,这是我的表单类型,ZonesTechniqueType.php
:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class ZonestechniqueType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('parcsimmobilier', 'entity', array(
'class' => 'EnexgirDatabaseBundle:Parcsimmobilier',
'property' => 'nom',
'multiple' => false,
'required' => true,
'mapped' => false,
'empty_value' => '-- sélectionner un parc --',
'label' => 'Choisir le parc immobilier : ',
'attr' => array('class' => 'col-xs-3 form-control')))
->add('nom')
->add('localisation')
->add('commentaire')
->add('categorieszonestechnique')
->add('batiments');
$formModifier = function (FormInterface $form, Parcsimmobilier $parc = null) {
$ensembles = null === $parc ? array() : $parc->getEnsembles();
$form->add('ensembles', 'entity', array(
'class' => 'EnexgirDatabaseBundle:Ensembles',
'property' => 'nom',
'multiple' => false,
'required' => true,
'mapped' => true,
'empty_value' => '-- sélectionner un ensemble --',
'label' => 'Choisir l\'ensemble : ',
'query_builder' => function(EntityRepository $er) use ($parc) {
return $er->createQueryBuilder('e')
->where("e.parcsimmobilier = :parcsimmobilier_id")
->orderBy('e.nom', 'ASC')
->setParameter(':parcsimmobilier_id', $parc);
},
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity
$data = $event->getData();
$formModifier($event->getForm(), $data->getEnsembles());
}
);
$builder->get('parcsimmobilier')->addEventListener(
FormEvents::POST_SUBMIT,
这是我使用的控制器方法:
public function addZonesConformitesAction(Request $request) {
$em=$this->getDoctrine()->getManager();
$zoneConformite = $em->getRepository('EnexgirDatabaseBundle:Zonestechnique');
$zoneConformite = new Zonestechnique;
$form=$this->createForm(new ZonestechniqueType(), $zoneConformite);
$form->handleRequest($request);
//instruction if à plusieurs conditions: ici si la method est POST et si le formulaire est valide
if ($request->isMethod('POST') | ($form->isValid())) {
$form->bind($request);
$zoneConformite = $form->getData();
$em->persist($zoneConformite);
$em->flush();
return $this->redirect($this->generateUrl('indexRechercheZones'));
}
//retourner le formulaire d'ajout si c'est invalide
else {
return $this->render('EnexgirGestionEquipementsTechniquesBundle:ZonesTechnique:ajouterZonesTechniqueConformites.html.twig', array('form' => $form->createView() ));
}
}
最后,我的树枝的块内容:
{% block content %}
<div class="panel-heading">
<h3 class="panel-title"> Gestion Des Zones techniques -<small> (ajout)</small></h3>
</div>
{# Panel Body #}
<div class="panel-body">
{# Menu #}
<div class="panel-body">
<ul class="nav nav-tabs">
<li role="presentation">
<a href="{{ path('indexRechercheZones') }}">Visualisation des Zones techniques</a>
</li>
<li role="presentation" class="active">
<a>Ajouter une Zone technique et ses Conformités</a>
</li>
</ul>
</div>
{# Formulaire #}
<div class="page-header">
<h4><b><u>Ajouter une zone technique et ses conformites</u></b><small> (créer une zone technique qui n'existe pas encore, ainsi que ses conformités respectives)</small></h4>
<br>
<form action="{{ path('ajouterZonesConformites_process') }}" method="POST" {{ form_enctype(form) }}>
<div>
{{ form_start(form) }}
{{ form_row(form.parcsimmobilier) }} {# <select id="meetup_sport" ... #}
{{ form_row(form.ensembles) }} {# <select id="meetup_position" ... #}
{{ form_end(form) }}
{# validation #}
<input type="submit" value="Ajouter" class="btn btn-success"/>
</div>
<br>
</form>
</div>
</div>
{% endblock %}
目前,我还没有在我的树枝中引入Ajax代码,因为我有这个错误:
Attempted to call method "getEnsembles" on class "Enexgir\DatabaseBundle\Entity\Zonestechnique" in C:\wamp\www\MyPathToMyProject\src\Enexgir\DatabaseBundle\Form\ZonestechniqueType.php line 65.
这是第 65 行的代码:
$formModifier($event->getForm(), $data->getEnsembles());
我知道 Ensembles.php
和 Parcsimmobilier.php
与 Zonestechnique.php
没有关系,但我按照文档进行操作,但我不太了解这是如何工作的。有人可以帮助我理解并制作此表格吗?
提前致谢。
symfony 表单事件系统肯定可以使用一些流线型。话虽如此,这是有可能实现的。对于将出现的每个下拉列表,您将需要 2 个表单事件 (PRE_SET_DATA/POST_SUBMIT)。您应该能够将所有 PRE_SET_DATA 事件合并到 1 个函数中,但是您必须在单独的函数中处理 POST_SUBMIT 事件,因为它们绑定到表单
您需要将 if 语句更改为:
if($request->isMethod('POST') && !$request->isXmlHttpRequest() && $form->isValid())
你也不需要打电话:
$form->bind($request);
如果您已经打过电话:
$form->handleRequest($request);
后者是第一个的替代品。
仅通过 "processing" 非 AJAX 请求的表单,这将允许您的表单获取更新的数据并触发表单事件和 return 它。
@Chausser 提出了一个很好的观点,因为在该部分的代码中 $formModifier($event->getForm(), $data->getEnsembles());
您传递了 Ensembles
的实例,因此不需要再次调用 $data->getEnsembles()
。
希望对你有帮助