以多对多关系防止数据库中的重复 [英] Prevent duplicates in the database in a many-to-many relationship
问题描述
在我的表单模板中,我手动添加了一个文本输入字段。我在这个领域应用了jQuery UI的自动填充方法,它允许:
- 选择现有的成分(以前添加的)
- 添加新成分
但是,当我提交表单时,每个成分都插入数据库(正常行为,你会告诉我 )。
对于不存在的成分,它是好的,但我不想再插入已插入的成分。
然后我想到了>学说活动,如 prePersist()
。但我看不到如何进行。我想知道你有没有办法做到这一点。
这里是 buildForm
方法我的 DishType
:
<?php
public function buildForm(FormBuilderInterface $ builder,array $ options)
{
$ builder
- > add('category','entity',array('class' =>'PrototypeAdminBundle:DishCategory',
'property'=>'name',
'multiple'=> false))
- > add('title'文本')
- > add('description','textarea')
- > add('price','text')
- > add(' 'collection',array('type'=> new IngredientType(),
'allow_add'=> true,
'allow_delete'=> true,
))
- > add('image',new ImageType(),array('label'=>假));
}
和我的控制器中处理该表单的方法:
<?php
public function addDishAction()
{
$ dish = new碟();
$ form = $ this-> createForm(new DishType,$ dish);
$ request = $ this-> get('request');
if($ request-> getMethod()=='POST'){
$ form-> bind($ request);
if($ form-> isValid()){
$ em = $ this-> getDoctrine() - > getManager();
$ em-> persist($ dish);
$ em-> flush();
return $ this-> redirect($ this-> generateUrl('prototype_admin_get_dish',array('slug'=> $ dish-> getSlug())));
}
}
return $ this-> render('PrototypeAdminBundle:Admin:addDish.html.twig',array(
'form'=> $ form-> createView(),
));
}
我有同样的问题。我的实体是项目(你的情况下的菜肴)和标签(成分)。
我通过添加一个事件侦听器来解决它,如这里。
services:
my.doctrine.listener:
class:Acme\AdminBundle\EventListener\UniqueIngredient
标签:
- {name:doctrine.event_listener,event :preUpdate}
- {name:doctrine.event_listener,event:prePersist}
触发prePersist(用于新添加的菜肴)和preUpdate来更新现有菜肴。
代码检查配料是否已经存在。如果成分存在,它将被使用,新条目将被丢弃。
代码如下:
<?php
命名空间Acme\AdminBundle\EventListener;
使用Doctrine\ORM\Event\LifecycleEventArgs;
使用Acme\AdminBundle\Entity\Dish;
使用Acme\AdminBundle\Entity\Ingredient;
class UniqueIngredient
{
/ **
*将在新创建的实体上调用
* /
public函数prePersist(LifecycleEventArgs $ args)
{
$ entity = $ args-> getEntity();
//我们对Dishes只感兴趣
if($ entity instanceof Dish){
$ entityManager = $ args-> getEntityManager();
$ ingredients = $ entity-> getIngredients();
foreach($ ingredients as $ key => $ ingredients){
//让我们检查此成分的存在
$ results = $ entityManager-> ; getRepository('Acme\AdminBundle\Entity\Ingredient') - > findBy(array('name'=> $ ingredients-> getName()),array('id'=>'ASC' ));
//如果成分存在使用现有成分
if(count($ results)> 0){
$ ingredients [$ key] = $ results [0];
}
}
}
}
/ **
*在现有实体的更新中调用
*
*新成分已经创建并持久化(虽然没有刷新)
*,所以我们现在决定将它们添加到菜单中或删除重复的
* /
public function preUpdate(LifecycleEventArgs $ args)
{
$ entity = $ args-> getEntity();
//我们对Dishes只感兴趣
if($ entity instanceof Dish){
$ entityManager = $ args-> getEntityManager();
$ ingredients = $ entity-> getIngredients();
foreach($ ingredients as $ ingredients){
//让我们检查这个成分的存在
//按名称查找并按id排序保持较旧成分第一
$ results = $ entityManager-> getRepository('Acme\AdminBundle\Entity\Ingredient') - > findBy(array('name'=> $ ingredients-> getName() ),array('id'=>'ASC'));
//如果成分存在,将返回至少两行
//保持第一个并丢弃第二个
if(count($ results)> 1){
$ knownIngredient = $ results [0];
$ entity-> addIngredient($ knownIngredient);
//删除重复的成分
$ duplicatedIngredient = $ results [1];
$ entityManager-> remove($ duplicateatedIngredient);
} else {
//成分不存在,添加关系
$ entity-> addIngredient($ ingredients);
}
}
}
}
}
注意:这似乎是工作,但我不是Symfony / Doctrine专家,所以仔细测试你的代码<
$ b
I'm working on a back office of a restaurant's website. When I add a dish, I can add ingredients in two ways.
In my form template, I manually added a text input field. I applied on this field the autocomplete method of jQuery UI that allows:
- Select existing ingredients (previously added)
- Add new ingredients
However, when I submit the form, each ingredients are inserted in the database (normal behaviour you will tell me ). For the ingredients that do not exist it is good, but I don't want to insert again the ingredients already inserted.
Then I thought about Doctrine events, like prePersist()
. But I don't see how to proceed. I would like to know if you have any idea of the way to do it.
Here is the buildForm
method of my DishType
:
<?php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('category', 'entity', array('class' => 'PrototypeAdminBundle:DishCategory',
'property' => 'name',
'multiple' => false ))
->add('title', 'text')
->add('description', 'textarea')
->add('price', 'text')
->add('ingredients', 'collection', array('type' => new IngredientType(),
'allow_add' => true,
'allow_delete' => true,
))
->add('image', new ImageType(), array( 'label' => false ) );
}
and the method in my controller where I handle the form :
<?php
public function addDishAction()
{
$dish = new Dish();
$form = $this->createForm(new DishType, $dish);
$request = $this->get('request');
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($dish);
$em->flush();
return $this->redirect($this->generateUrl('prototype_admin_get_dish', array('slug' => $dish->getSlug())));
}
}
return $this->render('PrototypeAdminBundle:Admin:addDish.html.twig', array(
'form' => $form->createView(),
));
}
I was having the same problem. My entities were projects (dishes in your case) and tags (ingredients).
I solved it by adding an event listener, as explained here.
services:
my.doctrine.listener:
class: Acme\AdminBundle\EventListener\UniqueIngredient
tags:
- { name: doctrine.event_listener, event: preUpdate }
- { name: doctrine.event_listener, event: prePersist }
The listener triggers both prePersist (for newly added dishes) and preUpdate for updates on existing dishes.
The code checks if the ingredient already exists. If the ingredient exists it is used and the new entry is discarded.
The code follows:
<?php
namespace Acme\AdminBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Acme\AdminBundle\Entity\Dish;
use Acme\AdminBundle\Entity\Ingredient;
class UniqueIngredient
{
/**
* This will be called on newly created entities
*/
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
// we're interested in Dishes only
if ($entity instanceof Dish) {
$entityManager = $args->getEntityManager();
$ingredients = $entity->getIngredients();
foreach($ingredients as $key => $ingredient){
// let's check for existance of this ingredient
$results = $entityManager->getRepository('Acme\AdminBundle\Entity\Ingredient')->findBy(array('name' => $ingredient->getName()), array('id' => 'ASC') );
// if ingredient exists use the existing ingredient
if (count($results) > 0){
$ingredients[$key] = $results[0];
}
}
}
}
/**
* Called on updates of existent entities
*
* New ingredients were already created and persisted (although not flushed)
* so we decide now wether to add them to Dishes or delete the duplicated ones
*/
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
// we're interested in Dishes only
if ($entity instanceof Dish) {
$entityManager = $args->getEntityManager();
$ingredients = $entity->getIngredients();
foreach($ingredients as $ingredient){
// let's check for existance of this ingredient
// find by name and sort by id keep the older ingredient first
$results = $entityManager->getRepository('Acme\AdminBundle\Entity\Ingredient')->findBy(array('name' => $ingredient->getName()), array('id' => 'ASC') );
// if ingredient exists at least two rows will be returned
// keep the first and discard the second
if (count($results) > 1){
$knownIngredient = $results[0];
$entity->addIngredient($knownIngredient);
// remove the duplicated ingredient
$duplicatedIngredient = $results[1];
$entityManager->remove($duplicatedIngredient);
}else{
// ingredient doesn't exist yet, add relation
$entity->addIngredient($ingredient);
}
}
}
}
}
NOTE: This seems to be working but I am not a Symfony / Doctrine expert so test your code carefully
Hope this helps!
pcruz
这篇关于以多对多关系防止数据库中的重复的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!