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

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

问题描述

我在一家餐厅网站的后台工作.当我添加时,我可以通过两种方式添加配料.

I'm working on a back office of a restaurant's website. When I add a dish, I can add ingredients in two ways.

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

In my form template, I manually added a text input field. I applied on this field the autocomplete method of jQuery UI that allows:

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

但是,当我提交表单时,每种成分都会插入数据库中(您会告诉我的正常行为).对于不存在的成分,它是好的,但我不想再次插入已经插入的成分.

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.

然后我想到了 Doctrine 事件,比如 prePersist().但我不知道如何进行.我想知道您是否有任何想法.

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.

这是我的 DishTypebuildForm 方法:

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: AcmeAdminBundleEventListenerUniqueIngredient
        tags:
            - { name: doctrine.event_listener, event: preUpdate }
            - { name: doctrine.event_listener, event: prePersist }

侦听器同时触发 prePersist(用于新添加的菜肴)和 preUpdate 以更新现有菜肴.

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.

代码如下:

<?php

namespace AcmeAdminBundleEventListener;

use DoctrineORMEventLifecycleEventArgs;

use AcmeAdminBundleEntityDish;
use AcmeAdminBundleEntityIngredient;

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('AcmeAdminBundleEntityIngredient')->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('AcmeAdminBundleEntityIngredient')->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 专家,因此请仔细测试您的代码

希望这会有所帮助!

普鲁士

这篇关于在多对多关系中防止数据库中的重复的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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