我在使用ManyToMany关系保存实体槽形式时遇到问题。
我无法保存关系“mappedBy”一侧的字段。
以下代码不会将任何内容保存到数据库中,也不会丢失任何错误:
transitionContext
它正在反过来。因此,如果我从CustomerType中保存一些东西,那么一切正常。
编辑:
下面的解决方案对我有用,但几天后我发现该解决方案存在问题。如果将使用已保存在数据库中的值提交表单,则Symfony将抛出错误。为了防止我必须检查给定的顾客是否已经分配给宠物。
检查当前分配的客户必须在功能开始时而不是在表单提交之后完成,因为在提交之后由于某种原因,Pet()对象包含提交的值,而不仅仅是存在于db中的值。
所以一开始我把所有已经分配的客户都放到了数组
containerView
表单提交后,我已检查提交的ID是否在数组中
// Entity/Pet
/**
* @var \Doctrine\Common\Collections\Collection
*
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Customer", mappedBy="pet", cascade={"persist"})
*/
private $customer;
/**
* Set customer
*
* @param \AppBundle\Entity\Customer $customer
* @return Pet
*/
public function setCustomer($customer)
{
$this->customer = $customer;
return $this;
}
// Entity/Customer
/**
* @var Pet
*
* @ORM\ManyToMany(targetEntity="AppBundle\Entity\Pet", inversedBy="customer", cascade={"persist"})
* @ORM\JoinTable(name="customer_pet",
* joinColumns={
* @ORM\JoinColumn(name="customer_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* @ORM\JoinColumn(name="pet_id", referencedColumnName="id")
* }
* )
*/
private $pet;
// PetType.php
$builder->add('customer', 'entity',
array(
'class' => 'AppBundle:Customer',
'property' => 'firstname',
'empty_value' => 'Choose owner',
'multiple' => true
));
答案 0 :(得分:1)
在Symfony2中,具有带有inversedBy教义注释的属性的实体是应该编辑由MANYTOMANY RELATION创建的EXTRA TABLE的实体。这就是为什么当您创建客户时,它会在该额外表中插入相应的行,从而保存相应的宠物。
如果您希望相反的行为发生,我建议:
//PetController.php
public function createAction(Request $request) {
$entity = new Pet();
$form = $this->createCreateForm($entity);
$form->submit($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
foreach ($form['customer']->getData()->getValues() as $v) {
$customer = $em->getRepository('AppBundle:Customer')->find($v->getId());
if ($customer) {
$customer->addPet($entity);
}
}
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('pet_show', array('id' => $entity->getId())));
}
return $this->render('AppBundle:pet:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
private function createCreateForm(Pet $entity) {
$form = $this->createForm(new PetType(), $entity, array(
'action' => $this->generateUrl('pet_create'),
'method' => 'POST',
));
return $form;
}
这两个是控制器中与Pet实体对应的标准Symfony2 CRUD生成的操作。
唯一的调整是在第一个操作中插入的 foreach 结构,这样您就可以强制将相同的宠物添加到您在表单中选择的每个客户,从而获得所需的行为。
看,很有可能这不是正确的方式,也不是正确的方式,但它是一种方式而且有效。希望它有所帮助。
答案 1 :(得分:0)
在我的服务<->项目场景中,服务具有“ inversedBy”,项目具有“ mappedBy”,我必须在项目控制器的编辑操作中执行此操作,以便在编辑项目时检查的服务将是坚持。
public function editAction(Request $request, Project $project = null)
{
// Check entity exists blurb, and get it from the repository, if you're inputting an entity ID instead of object ...
// << Many-to-many mappedBy hack
$servicesOriginal = new ArrayCollection();
foreach ($project->getServices() as $service) {
$servicesOriginal->add($service);
}
// >> Many-to-many mappedBy hack
$form = $this->createForm(ProjectType::class, $project);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
// << Many-to-many mappedBy hack
foreach ($servicesOriginal as $service) {
if (!$project->getServices()->contains($service)) {
$service->removeProject($project);
$em->persist($service);
}
}
foreach ($project->getServices() as $service) {
$service->addProject($project);
$em->persist($service);
}
// >> Many-to-many mappedBy hack
$em->persist($project);
$em->flush();
return; // I have a custom `redirectWithMessage()` here, use what you like ...
}
return $this->render("Your-template", [
$form => $form->createView(),
$project => $project,
]);
}
这既适用于从“ mappedBy”侧添加或删除多对多实体,因此EntityType
输入应按预期工作。
这是怎么回事,我们首先要构建一个“原始”集合,其中包含已经为此项目链接的所有服务实体。然后在保存表单时,我们确保:
重要:这分别取决于您实体的addService()
和addProject()
方法,以检查彼此的集合是否不包含重复项。如果不这样做,最终将导致有关重复记录插入的SQL级别错误。
在服务实体中,我有:
/**
* Add project
*
* @param Project $project
*
* @return Service
*/
public function addProject(Project $project)
{
if (!$this->projects->contains($project)) {
$this->projects->add($project);
}
if (!$project->getServices()->contains($this)) {
$project->getServices()->add($this);
}
return $this;
}
在项目实体中,我有:
/**
* Add service
*
* @param Service $service
*
* @return Project
*/
public function addService(Service $service)
{
if (!$this->services->contains($service)) {
$this->services->add($service);
}
if (!$service->getProjects()->contains($this)) {
$service->getProjects()->add($this);
}
return $this;
}
您也可以在控制器中进行检查,但是如果模型在可能的情况下对其进行了验证,这是有意义的,因为如果有任何来源的重复,该模型还是会破坏。
最后,在控制器的创建操作中,您可能甚至在$em->persist($project)
之前也需要此位。 (您将不需要使用“原始”集合,因为还不存在。)
// << Many-to-many mappedBy hack
foreach ($project->getServices() as $service) {
$service->addProject($project);
$em->persist($service);
}
// >> Many-to-many mappedBy hack
答案 2 :(得分:0)
我只是遇到了同样的问题,但我以不同的方式解决了。
更改控制器中的代码并不是更好的方法。 就我而言,我有一个 GenericController 来处理我所有的 CRUD,因此我无法将特定代码放入其中。
最好的方法是在你的 PetType 中添加一个这样的监听器:
// PetType.php
$builder->add('customer', 'entity',
array(
'class' => 'AppBundle:Customer',
'property' => 'firstname',
'empty_value' => 'Choose owner',
'multiple' => true
))
->addEventListener( FormEvents::SUBMIT, function( FormEvent $event ) {
/** @var Pet $pet */
$pet = $event->getData();
foreach ( $pet->getCustomers() as $customer ) {
$customer->addPet( $pet );
}
} );
这样您就可以将映射逻辑保持在同一位置。