以多对多关系防止数据库中的重复 [英] Prevent duplicates in the database in a many-to-many relationship

查看:105
本文介绍了以多对多关系防止数据库中的重复的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在餐厅网站的后台工作。当我添加时,我可以通过两种方式添加成分



在我的表单模板中,我手动添加了一个文本输入字段。我在这个领域应用了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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆