如何使用数据原型 symfony 2.8 创建 3 层表单
How to create 3 levels form with data-prototypes symfony 2.8
我有 3 个实体:(CV、DomaineCompetence 和 Competences)
CV 可以有很多 DomaineCompetence,DomaineCompetence 可以有很多 Competences,每个实体都有一个 formType。
简历实体
class Cv
{
//...
/**
* @OneToMany(targetEntity="DomaineCompetenceCv", mappedBy="cv", cascade={"persist"})
*
*/
private $domainesCompetence;
领域能力实体
class DomaineCompetenceCv
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* @ORM\ManyToOne(targetEntity="Cv", inversedBy="domainesCompetence")
* @ORM\JoinColumn(name="cv_id", referencedColumnName="id")
*/
private $cv;
/**
* @ORM\OneToMany(targetEntity="CompetenceCv", mappedBy="domaineCompetence", cascade={"persist"})
*
*/
private $competences;
CompetenceCv
class CompetenceCv
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* @ORM\Column(name="niveau", type="integer")
*/
private $niveau;
/**
* @ORM\ManyToOne(targetEntity="DomaineCompetenceCv", inversedBy="competences")
* @ORM\JoinColumn(name="domaine_id", referencedColumnName="id")
*/
private $domaineCompetence;
CVForm
public function buildForm(FormBuilderInterface $builder, array $options)
{ $builder
//...
->add('domainesCompetence', CollectionType::class, array(
'entry_type' => DomaineCompetenceCvForm::class,
'allow_add' => true,
'by_reference' => false,
))
;
}
DomaineCompetenceCvForm
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom')
->add('competences', CollectionType::class, array(
'entry_type' => CompetenceCvForm::class,
'allow_add' => true,
'by_reference' => true,
))
;
}
CompetencesCvForm
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom')->add('niveau') ;
}
Cv 的 Twig
<form role="form" action="" {{ form_enctype(form) }} method="POST">
{{ form_widget(form._token) }}
//...
<h4 class="no-margin-top has-divider text-highlight">Compétences</h4>
<div id="domaineCV" class="domaineCV" data-prototype="{{ form_widget(form.domainesCompetence.vars.prototype)|e('html_attr') }}">
{% for com in form.domainesCompetence %}
<div class="" data-prototype="{{ form_widget(com.vars.prototype)|e('html_attr') }}">
</div>
{% endfor %}
</div>
</form>
我使用javaScript添加了多个DomaineCompetence但是问题是Competence的形式不显示
有人可以帮助我吗?谢谢大家。
JS 领域能力
<script type="text/javascript">
$(document).ready(function() {
var $container = $('div#domaineCV');
var $addLink = $('<a href="#" id="add_category" class="btn btn-default">Ajouter une Domaine de compétence</a>');
$container.append($addLink);
$addLink.click(function(e) {
addCategory($container);
e.preventDefault(); // évite qu'un # apparaisse dans l'URL
return false;
});
var index = $container.find(':input').length;
if (index == 0) {
addCategory($container);
} else {
$container.children('div').each(function() {
addDeleteLink($(this));
});
}
function addCategory($container) {
var $prototype = $($container.attr('data-prototype').replace(/__name__label__/g, 'Catégorie n°' + (index+1))
.replace(/__name__/g, index));
addDeleteLink($prototype);
$container.append($prototype);
index++;
}
function addDeleteLink($prototype) {
$deleteLink = $('<a href="#" class="btn btn-danger">Supprimer</a>');
$prototype.append($deleteLink);
$deleteLink.click(function(e) {
$prototype.remove();
e.preventDefault(); =
return false;
});
}
});
我在我的本地环境中重现了您的问题,相同的堆栈 (Symfony 2.8.20)
从您的屏幕截图中,我可以看到 DomainesCompetence 表单显示在 Cv 表单中,因为您创建了用于处理此集合的 js。但是您确定实际的 js 处理每个 DomaineCompetence 表单中的 competences 集合吗?
也许您需要 customize your CompetenceCvType collection prototype。请提供反馈。
** 编辑:肮脏的解决方案(可行但需要重构!!!)**
首先,当您呈现 Cv 表单模板时,会填充 DomaineCompetence(嵌入在您的 Cv 表单中)表单原型,但嵌入的 Competence 表单原型不会填充在您的 DomaineCompetence 表单中。您可以像这样在控制器中填充二级表单集合:
<?php
namespace AppBundle\Controller;
use AppBundle\Form\CvType;
use AppBundle\Form\DomaineCompetenceCvType;
use AppBundle\Form\CompetenceCvType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class CvController extends Controller
{
/**
* @Route("/new")
*/
public function newAction()
{
$form = $this->createForm(CvType::class);
$domaineCompetenceCvForm = $this
->createForm(
CompetenceCvType::class,
null,
array('auto_initialize' => false
)
);
$form->get('domainesCompetence')
->add($domaineCompetenceCvForm)
;
return $this->render(
':cv:new.html.twig',
array(
'form' => $form->createView(),
)
);
}
}
接下来像这样更新您的模板:
{% extends 'base.html.twig' %}
{% form_theme form _self %}
{% block _appbundle_cv_domainesCompetence_entry_widget %}
{{ form_row(form.nom) }}
{% endblock %}
{% block title %}AppBundle:Cv:new{% endblock %}
{% block body %}
<h1>Welcome to the Cv:new page</h1>
<form role="form" action="" {{ form_enctype(form) }} method="POST">
{{ form_widget(form._token) }}
<h4 class="no-margin-top has-divider text-highlight">Compétences</h4>
Domaines
<div style="border: 1px solid green" id="domaineCV" class="domaineCVs" data-prototype="{{ form_widget(form.domainesCompetence.vars.prototype)|e('html_attr') }}">
<div style="border: 1px solid yellow" id="competencesProto" data-prototype="{{ form_widget(form.children.domainesCompetence)|e('html_attr') }}">
</div>
</div>
</form>
{% endblock %}
{% block javascripts %}
{{ parent() }} {# load your jquery and bootstrap deps #}
<script type="text/javascript">
{# custom js handling your collections here #}
</script>
{% endblock %}
那么这是js:
$(document).ready(function() {
var $prototype = '';
var $container = $('div#domaineCV');
var $competencesContainer = '';
var $competencesProtoContainer = $('div#competencesProto');
var $addLink = $('<a href="#" id="add_category" class="btn btn-success">Ajouter une Catégorie</a>');
var $addCompetence = $('<a href="#" id="add_competence" class="btn btn-warning">Ajouter une compétence</a>');
$container.after($addLink);
$addLink.click(function(e) {
addCategory($container);
e.preventDefault(); // évite qu'un # apparaisse dans l'URL
return false;
});
$addCompetence.click(function(e) {
addCompetence($competencesContainer);
e.preventDefault(); // évite qu'un # apparaisse dans l'URL
return false;
});
var index = $container.find(':input').length;
var competencesIndex = 0;
if (index == 0) {
addCategory($container);
competencesIndex = $competencesContainer.find(':input').length;
} else {
$container.children('div').each(function() {
addDeleteLink($(this));
});
}
function addCategory($container) {
$prototype = $($container.attr('data-prototype').replace(/__name__label__/g, 'Catégorie n°' + (index+1))
.replace(/__name__/g, index));
$container.prepend($prototype);
$competencesContainer = $('div#competencesProto');
$competencesContainer.after($addCompetence);
addCompetence($competencesContainer);
addDeleteLink($prototype);
index++;
}
function addCompetence($competencesContainer) {
var $competenceProto = $($competencesProtoContainer.attr('data-prototype').replace(/__name__label__/g, 'Catégorie n°' + (competencesIndex+1))
.replace(/__name__/g, competencesIndex));
$competencesContainer.append($competenceProto);
$prototype.append($competenceProto);
addCompetenceDeleteLink($competenceProto);
competencesIndex++;
}
function addDeleteLink($element) {
$deleteLink = $('<a href="#" class="btn btn-danger">Supprimer catégorie</a>');
$element.append($deleteLink);
$deleteLink.click(function(e) {
$element.remove();
e.preventDefault();
return false;
});
}
function addCompetenceDeleteLink($element) {
$deleteCompetenceLink = $('<a href="#" class="btn btn-danger">Supprimer competence</a>');
$element.append($deleteCompetenceLink);
$deleteCompetenceLink.click(function(e) {
$element.remove();
e.preventDefault();
return false;
});
}
});
我有 3 个实体:(CV、DomaineCompetence 和 Competences) CV 可以有很多 DomaineCompetence,DomaineCompetence 可以有很多 Competences,每个实体都有一个 formType。
简历实体
class Cv
{
//...
/**
* @OneToMany(targetEntity="DomaineCompetenceCv", mappedBy="cv", cascade={"persist"})
*
*/
private $domainesCompetence;
领域能力实体
class DomaineCompetenceCv
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* @ORM\ManyToOne(targetEntity="Cv", inversedBy="domainesCompetence")
* @ORM\JoinColumn(name="cv_id", referencedColumnName="id")
*/
private $cv;
/**
* @ORM\OneToMany(targetEntity="CompetenceCv", mappedBy="domaineCompetence", cascade={"persist"})
*
*/
private $competences;
CompetenceCv
class CompetenceCv
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* @ORM\Column(name="niveau", type="integer")
*/
private $niveau;
/**
* @ORM\ManyToOne(targetEntity="DomaineCompetenceCv", inversedBy="competences")
* @ORM\JoinColumn(name="domaine_id", referencedColumnName="id")
*/
private $domaineCompetence;
CVForm
public function buildForm(FormBuilderInterface $builder, array $options)
{ $builder
//...
->add('domainesCompetence', CollectionType::class, array(
'entry_type' => DomaineCompetenceCvForm::class,
'allow_add' => true,
'by_reference' => false,
))
;
}
DomaineCompetenceCvForm
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom')
->add('competences', CollectionType::class, array(
'entry_type' => CompetenceCvForm::class,
'allow_add' => true,
'by_reference' => true,
))
;
}
CompetencesCvForm
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom')->add('niveau') ;
}
Cv 的 Twig
<form role="form" action="" {{ form_enctype(form) }} method="POST">
{{ form_widget(form._token) }}
//...
<h4 class="no-margin-top has-divider text-highlight">Compétences</h4>
<div id="domaineCV" class="domaineCV" data-prototype="{{ form_widget(form.domainesCompetence.vars.prototype)|e('html_attr') }}">
{% for com in form.domainesCompetence %}
<div class="" data-prototype="{{ form_widget(com.vars.prototype)|e('html_attr') }}">
</div>
{% endfor %}
</div>
</form>
我使用javaScript添加了多个DomaineCompetence但是问题是Competence的形式不显示
有人可以帮助我吗?谢谢大家。
<script type="text/javascript">
$(document).ready(function() {
var $container = $('div#domaineCV');
var $addLink = $('<a href="#" id="add_category" class="btn btn-default">Ajouter une Domaine de compétence</a>');
$container.append($addLink);
$addLink.click(function(e) {
addCategory($container);
e.preventDefault(); // évite qu'un # apparaisse dans l'URL
return false;
});
var index = $container.find(':input').length;
if (index == 0) {
addCategory($container);
} else {
$container.children('div').each(function() {
addDeleteLink($(this));
});
}
function addCategory($container) {
var $prototype = $($container.attr('data-prototype').replace(/__name__label__/g, 'Catégorie n°' + (index+1))
.replace(/__name__/g, index));
addDeleteLink($prototype);
$container.append($prototype);
index++;
}
function addDeleteLink($prototype) {
$deleteLink = $('<a href="#" class="btn btn-danger">Supprimer</a>');
$prototype.append($deleteLink);
$deleteLink.click(function(e) {
$prototype.remove();
e.preventDefault(); =
return false;
});
}
});
我在我的本地环境中重现了您的问题,相同的堆栈 (Symfony 2.8.20)
从您的屏幕截图中,我可以看到 DomainesCompetence 表单显示在 Cv 表单中,因为您创建了用于处理此集合的 js。但是您确定实际的 js 处理每个 DomaineCompetence 表单中的 competences 集合吗?
也许您需要 customize your CompetenceCvType collection prototype。请提供反馈。
** 编辑:肮脏的解决方案(可行但需要重构!!!)**
首先,当您呈现 Cv 表单模板时,会填充 DomaineCompetence(嵌入在您的 Cv 表单中)表单原型,但嵌入的 Competence 表单原型不会填充在您的 DomaineCompetence 表单中。您可以像这样在控制器中填充二级表单集合:
<?php
namespace AppBundle\Controller;
use AppBundle\Form\CvType;
use AppBundle\Form\DomaineCompetenceCvType;
use AppBundle\Form\CompetenceCvType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class CvController extends Controller
{
/**
* @Route("/new")
*/
public function newAction()
{
$form = $this->createForm(CvType::class);
$domaineCompetenceCvForm = $this
->createForm(
CompetenceCvType::class,
null,
array('auto_initialize' => false
)
);
$form->get('domainesCompetence')
->add($domaineCompetenceCvForm)
;
return $this->render(
':cv:new.html.twig',
array(
'form' => $form->createView(),
)
);
}
}
接下来像这样更新您的模板:
{% extends 'base.html.twig' %}
{% form_theme form _self %}
{% block _appbundle_cv_domainesCompetence_entry_widget %}
{{ form_row(form.nom) }}
{% endblock %}
{% block title %}AppBundle:Cv:new{% endblock %}
{% block body %}
<h1>Welcome to the Cv:new page</h1>
<form role="form" action="" {{ form_enctype(form) }} method="POST">
{{ form_widget(form._token) }}
<h4 class="no-margin-top has-divider text-highlight">Compétences</h4>
Domaines
<div style="border: 1px solid green" id="domaineCV" class="domaineCVs" data-prototype="{{ form_widget(form.domainesCompetence.vars.prototype)|e('html_attr') }}">
<div style="border: 1px solid yellow" id="competencesProto" data-prototype="{{ form_widget(form.children.domainesCompetence)|e('html_attr') }}">
</div>
</div>
</form>
{% endblock %}
{% block javascripts %}
{{ parent() }} {# load your jquery and bootstrap deps #}
<script type="text/javascript">
{# custom js handling your collections here #}
</script>
{% endblock %}
那么这是js:
$(document).ready(function() {
var $prototype = '';
var $container = $('div#domaineCV');
var $competencesContainer = '';
var $competencesProtoContainer = $('div#competencesProto');
var $addLink = $('<a href="#" id="add_category" class="btn btn-success">Ajouter une Catégorie</a>');
var $addCompetence = $('<a href="#" id="add_competence" class="btn btn-warning">Ajouter une compétence</a>');
$container.after($addLink);
$addLink.click(function(e) {
addCategory($container);
e.preventDefault(); // évite qu'un # apparaisse dans l'URL
return false;
});
$addCompetence.click(function(e) {
addCompetence($competencesContainer);
e.preventDefault(); // évite qu'un # apparaisse dans l'URL
return false;
});
var index = $container.find(':input').length;
var competencesIndex = 0;
if (index == 0) {
addCategory($container);
competencesIndex = $competencesContainer.find(':input').length;
} else {
$container.children('div').each(function() {
addDeleteLink($(this));
});
}
function addCategory($container) {
$prototype = $($container.attr('data-prototype').replace(/__name__label__/g, 'Catégorie n°' + (index+1))
.replace(/__name__/g, index));
$container.prepend($prototype);
$competencesContainer = $('div#competencesProto');
$competencesContainer.after($addCompetence);
addCompetence($competencesContainer);
addDeleteLink($prototype);
index++;
}
function addCompetence($competencesContainer) {
var $competenceProto = $($competencesProtoContainer.attr('data-prototype').replace(/__name__label__/g, 'Catégorie n°' + (competencesIndex+1))
.replace(/__name__/g, competencesIndex));
$competencesContainer.append($competenceProto);
$prototype.append($competenceProto);
addCompetenceDeleteLink($competenceProto);
competencesIndex++;
}
function addDeleteLink($element) {
$deleteLink = $('<a href="#" class="btn btn-danger">Supprimer catégorie</a>');
$element.append($deleteLink);
$deleteLink.click(function(e) {
$element.remove();
e.preventDefault();
return false;
});
}
function addCompetenceDeleteLink($element) {
$deleteCompetenceLink = $('<a href="#" class="btn btn-danger">Supprimer competence</a>');
$element.append($deleteCompetenceLink);
$deleteCompetenceLink.click(function(e) {
$element.remove();
e.preventDefault();
return false;
});
}
});