我需要在其父表单中嵌入一个实体表单。我跟随官方documentation将一张表格嵌入另一个与一对多关系的表格中。
问题是虽然我设法正确显示表单,但是当需要持久保存另一个子实体时,它可以正常使用前两个对象,当我尝试创建第三个对象时,它会删除数组中的第二个对象并创建第三个。
示例:我有一个与WorkExperience实体有一对多关系的CV实体
代码:
实体:
class Curriculum
{
/**
* @ORM\OneToMany(targetEntity="appBundle\Entity\WorkExperience", mappedBy="curriculum", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private $workExperience;
public function __construct()
{
$this->workExperience = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add workExperience
*
* @param appBundle\Entity\WorkExperience $workExperience
* @return Curriculum
*/
public function addWorkExperience(appBundle\Entity\WorkExperience $workExperience)
{
$this->workExperience->add($workExperience);
$workExperience->setCurriculum($this);
return $this;
}
/**
* Remove workExperience
*
* @param appBundle\Entity\WorkExperience $workExperience
*/
public function removeWorkExperience(appBundle\Entity\WorkExperience $workExperience)
{
$this->workExperience->removeElement($workExperience);
}
/**
* Get workExperience
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getWorkExperience()
{
return $this->workExperience;
}
}
WorkExperience:
class WorkExperience
{
/**
* @ORM\ManyToOne(targetEntity="appBundle\Entity\Curriculum", inversedBy="workExperience")
* @ORM\JoinColumn(name="curriculum", referencedColumnName="id")
*/
private $curriculum;
/**
* Set curriculum
*
* @param string $curriculum
* @return WorkExperience
*/
public function setCurriculum($curriculum)
{
$this->curriculum = $curriculum;
return $this;
}
/**
* Get curriculum
*
* @return string
*/
public function getCurriculum()
{
return $this->curriculum;
}
}
然后,formType(我只创建了workExperience表单,因为它是我在collectionType字段中需要的)
WorkExperienceType:
<?php
namespace appBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use appBundle\Entity\WorkExperience;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Intl\Intl;
use Symfony\Component\Form\Extension\Core\Type\DateType;
class WorkExperienceType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('fromDate', DateType::class, array(
'widget' => 'single_text',
'html5' => false,
'attr' => ['class' => 'form-control js-datepicker'],
'format' => 'dd-MM-yyyy',
'label' => 'From')
)
->add('toDate', DateType::class, array(
'widget' => 'single_text',
'html5' => false,
'attr' => ['class' => 'form-control js-datepicker'],
'format' => 'dd-MM-yyyy',
'label' => 'To',
'required' => false,
)
)
->add('ongoing', 'checkbox', array('required' => false,))
->add('jobProfile', EntityType::class, array(
'class' => 'appBundle:JobProfile',
'query_builder' => function (EntityRepository $er) use ($options) {
return $er->createQueryBuilder('j')
->where('j.company = :company')
->setParameter('company', $options['company']);
},
'choice_label' => 'name',
'required' => false,
'placeholder' => 'Job Profile'
))
->add('employerName', 'text', array('label' => "Name"))
->add('employerCity', 'text', array('label' => "City"))
->add('activities', 'textarea', array('label' => "Activities and responsibilities"));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefined('company');
$resolver->setDefaults(array(
'data_class' => WorkExperience::class,
));
}
}
课程管理员:
我只向您展示表单管理,因为我遇到了问题(如果您想知道我没有传递ID,因为连接的用户只能有一个课程)
<?php
namespace appBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use appBundle\Entity\Curriculum;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use appBundle\Form\Type\WorkExperienceType;
use appBundle\Form\Type\EducationCvType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Intl\Intl;
use Doctrine\Common\Collections\ArrayCollection;
use appBundle\Entity\WorkExperience;
class CurriculumController extends Controller
{
public function curriculumEditAction(Request $request)
{
if (!$this->getUser() || !$this->get('session')) {
$response = $this->generateUrl('homepage');
return new RedirectResponse($response);
}
$em = $this->getDoctrine()->getManager();
$curriculum = $this->getUser()->getEmployee()->getCurriculum();
$originalWorkExperience = new ArrayCollection();
foreach ($curriculum->getWorkExperience() as $workExperience) {
$originalWorkExperience->add($workExperience);
}
$originalEducation = new ArrayCollection();
foreach ($curriculum->getEducationTraining() as $educationTraining) {
$originalEducation->add($educationTraining);
}
$countries = Intl::getRegionBundle()->getCountryNames();
$formCurriculum = $this->createFormBuilder($curriculum)
->add('workExperience', CollectionType::class, array(
'entry_type' => WorkExperienceType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => false,
'type' => new WorkExperience(),
'entry_options' => array(
'company' => $this->getUser()->getEmployee()->getCompany(),
),
))
->add('save', 'submit', array('label' => 'Save changes', 'attr' => array('class' => 'btn btn-blue')))
->getForm();
if ('POST' === $request->getMethod()) {
$formCurriculum->bind($request);
if ($formCurriculum->isValid()) {
$curriculum = $formCurriculum->getData();
try {
foreach ($curriculum->getWorkExperience() as $workExperience) {
$em->persist($workExperience);
$em->flush();
}
// remove the relationship between the tag and the Task
foreach ($originalWorkExperience as $workExperience) {
if (false === $curriculum->getWorkExperience()->contains($workExperience)) {
// remove the Task from the Tag
$workExperience->getCurriculum()->removeWorkExperience($workExperience);
// if you wanted to delete the Tag entirely, you can also do that
$em->remove($workExperience);
}
}
$em->persist($curriculum);
$em->flush();
$request->getSession()
->getFlashBag()
->add('success', 'edit_curriculum_ok')
;
} catch (Exception $e) {
$request->getSession()
->getFlashBag()
->add('error', 'edit_curriculum_ko')
;
}
return $this->render('appBundle:Curriculum:curriculum_edit.html.twig', array('curriculum' => $curriculum, 'formCurriculum' => $formCurriculum->createView(),));
}
}
return $this->render('appBundle:Curriculum:curriculum_edit.html.twig', array('curriculum' => $curriculum, 'formCurriculum' => $formCurriculum->createView(),));
}
}
模板:
{% form_theme formCurriculum _self %}
{% block work_experience_widget %}
<div class="form-group row">
<div class="col-sm-6">
<label for="">Nombre Empresa</label>
{{ form_widget(form.employerName, {'attr': {'class': 'form-control'}}) }}
</div>
<div class="col-sm-5">
<label for="">Categoría Profesional</label>
{{ form_widget(form.jobProfile, {'attr': {'class': 'form-control'}}) }}
</div>
<div class="col-sm-1">
<a href="#" class="btn btn-danger btn-xs text-center remove-tag"><i class="glyphicon glyphicon-remove"></i></a>
</div>
</div>
<div class="form-group row">
<div class="col-sm-3">
<label for="">Fecha desde</label>
<div class="input-group">
{{ form_widget(form.fromDate) }}
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</div>
</div>
<div class="col-sm-5">
<label for="">Fecha hasta</label>
<div class="input-group">
<span class="input-group-addon">
{{ form_widget(form.ongoing) }}
</span>
{{ form_widget(form.toDate) }}
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</div>
</div>
<div class="col-sm-4">
<label for="">Ciudad</label>
{{ form_widget(form.employerCity, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
<div class="form-group">
<label for="">Actividades y Responsabilidades</label>
{{ form_widget(form.activities, {'attr': {'class': 'form-control'}}) }}
{{ form_rest(form) }}
{{ form_errors(form)}}
</div>
<hr />
{% endblock %}
{{ form_start(formCurriculum) }}
<div class="col-lg-8 col-xs-12">
<h3>Experiencia Laboral</h3>
<div class="workExperience" data-prototype="{{ form_widget(formCurriculum.workExperience.vars.prototype)|e('html_attr') }}">
{% for workExperience in formCurriculum.workExperience %}
<div class="workExperienceUnit">
{{ form(workExperience)}}
</div>
{% endfor %}
</div>
<div class="form-group text-right">
{{ form_widget(formCurriculum.save, {'attr': {'class': 'btn btn-blue'}}) }}
</div>
<div class="clearfix"></div>
</div>
{{ form_rest(formCurriculum) }}
{{ form_end(formCurriculum) }}
表单集合的Ajax和js:
{% block javascripts %}
{{ parent() }}
<script type="text/javascript">
var $collectionHolder;
// setup an "add a tag" link
var $addWorkExperienceLink = $('<button type="button" class="btn btn-primary width100x100 add_workexperience_link"><span class="badge"><i class="glyphicon glyphicon-plus"></i></span> Agregar nueva experiencia laboral</button>');
var $newLinkLi = $('<div class="form-group row"><div class="col-sm-12"></div></div>').append($addWorkExperienceLink);
jQuery(document).ready(function() {
$collectionHolder = $('div.workExperience');
// add the "add a tag" anchor and li to the tags ul
$collectionHolder.append($newLinkLi);
// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolder.data('index', $collectionHolder.find(':input').length);
$addWorkExperienceLink.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see next code block)
addWorkExperienceForm($collectionHolder, $newLinkLi);
});
});
function addWorkExperienceForm($collectionHolder, $newLinkLi) {
// Get the data-prototype explained earlier
var prototype = $collectionHolder.data('prototype');
// get the new index
var index = $collectionHolder.data('index');
$collectionHolder.data('index', index + 1);
// Replace '$$name$$' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(/__name__/g, index);
// Display the form in the page in an li, before the "Add a tag" link li
var $newFormLi = $('<div class="workExperienceUnit"></div>').append(newForm);
$newLinkLi.before($newFormLi);
$('.remove-tag').click(function(e) {
e.preventDefault();
$(this).parent().parent().parent().remove();
return false;
});
}
// handle the removal, just for this example
$('.remove-tag').click(function(e) {
e.preventDefault();
$(this).parent().parent().parent().remove();
return false;
});
</script>
{% endblock %}
以下是截图,关键是它正确删除对象,唯一的问题是创建新对象时
答案 0 :(得分:0)
有些事情似乎很奇怪。