Prevent duplicates in the database in a many-to-many relationship

前端 未结 2 587
闹比i
闹比i 2020-12-29 15:04

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 add

2条回答
  •  暖寄归人
    2020-12-29 15:29

    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:

    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

提交回复
热议问题