以多对多关系防止数据库中的重复

时间:2014-01-22 10:16:49

标签: symfony duplicates many-to-many

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

在我的表单模板中,我手动添加了一个文本输入字段。我在这个字段上应用了jQuery UI的自动完成方法,允许:

  • 选择现有成分(之前添加)
  • 添加新成分

然而,当我提交表格时,每个成分都会插入数据库中(您将告诉我的正常行为)。 对于不存在的成分,这是好的,但我不想再插入已插入的成分。

然后我想到了 Doctrine事件,就像prePersist()一样。但我不知道如何继续。我想知道你是否知道如何做到这一点。

以下是buildForm的{​​{1}}方法:

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 ) );
}

2 个答案:

答案 0 :(得分:8)

我遇到了同样的问题。我的实体是项目(在你的情况下的菜肴)和标签(成分)。

我通过添加事件监听器来解决它,如here所述。

services:
    my.doctrine.listener:
        class: Acme\AdminBundle\EventListener\UniqueIngredient
        tags:
            - { name: doctrine.event_listener, event: preUpdate }
            - { name: doctrine.event_listener, event: prePersist }

听众会触发prePersist(用于新添加的菜肴)和preUpdate以获取现有菜肴的更新。

代码检查成分是否已存在。如果成分存在,则使用它并丢弃新条目。

代码如下:

<?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);

                }

            }

        }

    }

}

注意:这似乎有效,但我不是Symfony / Doctrine专家,因此请仔细测试您的代码

希望这有帮助!

pcruz

答案 1 :(得分:0)

由于这篇文章是2岁,我不知道这里是否还需要帮助..无论如何..

您应该在Dish实体中放置addIngredient函数,此函数会检查当前成分集合中是否存在Ingredient对象。

addIngredient(Ingredient $ingredient){
    if (!$this->ingredients->contains($ingredient)) {
        $this->ingredients[] = $ingredient;
    }
}

希望它仍然可以帮助你。