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

Adr*_*n G 7 many-to-many duplicates symfony

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

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

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

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

然后我想到了Doctrine事件,比如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', '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 ) );
}
Run Code Online (Sandbox Code Playgroud)

以及我处理表单的控制器中的方法:

<?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(),
        ));
}
Run Code Online (Sandbox Code Playgroud)

小智 8

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

我解决了它添加一个事件侦听器,如解释在这里.

services:
    my.doctrine.listener:
        class: Acme\AdminBundle\EventListener\UniqueIngredient
        tags:
            - { name: doctrine.event_listener, event: preUpdate }
            - { name: doctrine.event_listener, event: prePersist }
Run Code Online (Sandbox Code Playgroud)

听众触发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);

                }

            }

        }

    }

}
Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助!

pcruz