Symfony 3.4 - 如果在多对多关系上不存在,则持续存在

Posted

技术标签:

【中文标题】Symfony 3.4 - 如果在多对多关系上不存在,则持续存在【英文标题】:Symfony 3.4 - Persist if not exists on manytomany relation 【发布时间】:2018-01-08 10:56:32 【问题描述】:

我面临一个非常烦人的问题。我有一个 Recipe 实体,其中包括一些具有 ManyToMany 关系的成分(带有一个成分实体)和一个用于映射的 RecipeIngredient 实体。 用户可以通过文本输入添加食谱并指定该食谱的成分。 我的问题是,当用户键入我的数据库中已经存在的成分名称时,我想在我的数据库中获取一个并将其与新配方映射,并且显然不要坚持使用任何双重成分。

Recipe.php

/**
 * @ORM\Entity
 * @ORM\Table(name="recipe")
 */
class Recipe

  /**
   * @ORM\Id
   * @ORM\Column(type="integer")
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  private $id;

  /**
   * @ORM\Column(type="string", unique=false)
   */
  private $title;

  /**
   * @var array Ingredient[]
   */
  private $ingredients;

  /**
   * @ORM\OneToMany(targetEntity="RecipeIngredient", mappedBy="recipe_id", cascade="all")
   */
  private $recipeIngredients;

  /**
   * @ORM\ManyToOne(targetEntity="User", inversedBy="recipes")
   * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
   */
  private $user;

Ingredient.php

/**
 * @ORM\Entity()
 * @UniqueEntity(
 *   fields="name"
 * )
 * @ORM\Table(name="ingredient")
 */
class Ingredient

  /**
   * @ORM\Id
   * @ORM\Column(type="integer")
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  private $id;

  /**
   * @ORM\Column(type="string")
   */
  private $name;

  /**
   * @ORM\OneToMany(targetEntity="RecipeIngredient", mappedBy="ingredient_id", fetch="EXTRA_LAZY", cascade="all")
   */
  private $recipesIngredient;

RecipeIngredient.php

/**
 * Class RecipesIngredient
 * @package AppBundle\Entity
 *
 * @ORM\Entity
 * @ORM\Table(name="recipe_ingredient")
 */
class RecipeIngredient

  /**
   * @ORM\Id
   * @ORM\ManyToOne(targetEntity="Recipe", inversedBy="recipeIngredients", cascade="persist")
   * @ORM\JoinColumn(nullable=false)
   */
  private $recipe;

  /**
   * @ORM\Id
   * @ORM\ManyToOne(targetEntity="Ingredient", inversedBy="recipesIngredient", cascade="persist")
   * @ORM\JoinColumn(nullable=false)
   */
  private $ingredient;

谢谢你帮助我。

编辑

创建配方的函数

/**
   * Creates a new recipe entity.
   *
   * @Route("/new/recipe", name="recipe_new")
   * @Method("GET", "POST")
   *
   * @param $request Request
   * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
   */
  public function newAction(Request $request)
  
    $needAccount = true;

    if ($this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY') || $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_REMEMBERED')) 
      $needAccount = false;
    

    $recipe = null;

    $error = $request->getSession()->get("loginError");

    $errorMsg = "";
    if ($error !== null) 
      $errorMsg = $error->getMessageKey();
    

    $request->getSession()->remove("loginError");

    if (!$needAccount) 
      $recipe = new Recipe($this->getUser());
      $form = $this->createForm('AppBundle\Form\RecipeType', $recipe);
      $form->handleRequest($request);

      if ($form->isSubmitted() && $form->isValid()) 
        $this->setRecipe($recipe);

        $em = $this->getDoctrine()->getManager();
        $em->persist($recipe);
        $em->flush();

        $mail = (new \Swift_Message("Nouvelle recette"))
          ->setFrom("*************")
          ->setTo("*********")
          ->setBody(
            "Une recette vient d'être créée : *********" . $recipe->getUrl(),
            'text/plain'
          );
        $this->get("mailer")->send($mail);

        return $this->redirectToRoute('recipe_show', array('url' => $recipe->getUrl()));
      

      return $this->render('@App/pages/recipe/new.html.twig', array(
        'needAccount' => $needAccount,
        "errorLogin" => $errorMsg,
        'recipe' => $recipe,
        'form' => $form->createView()
      ));
     else 
      $recipe = new Recipe(new User());

      return $this->render('@App/pages/recipe/new.html.twig', array(
        'needAccount' => $needAccount,
        "errorLogin" => $errorMsg,
        'recipe' => $recipe,
        'form' => null
      ));
    
  

函数集配方

private function setRecipe(Recipe $recipe)
  
    if ($recipe->getUrl() === "") 
      $recipe->setUrl(preg_replace('/[^a-z0-9]+/', '-', strtolower(str_replace(explode(",", "ç,æ,œ,á,é,í,ó,ú,à,è,ì,ò,ù,ä,ë,ï,ö,ü,ÿ,â,ê,î,ô,û,å,ø"), explode(",", "c,ae,oe,a,e,i,o,u,a,e,i,o,u,a,e,i,o,u,y,a,e,i,o,u,a,o"), $recipe->getTitle()))) . '-' . time());
    
    $recipe->setUpdatedAt(new \DateTime());

    foreach ($recipe->getSteps() as $step) 
      $step->setRecipe($recipe);
      $step->setDescription(preg_replace_callback(
        '/\\\\x([a-f0-9]2)/i',
        function ($matches) 
          return chr(hexdec($matches[1]));
        ,
        $step->getDescription()));
    

    foreach ($recipe->getIngredients() as $ingredient) 
      $ingredient->setName(preg_replace_callback('/\\\\x([a-f0-9]2)/i',
        function ($matches) 
          return chr(hexdec($matches[1]));
        ,
        $ingredient->getName()));

      $ingredientExists = count($this->getDoctrine()->getManager()->getRepository('AppBundle:Ingredient')->createQueryBuilder('i')
        ->select('i.id')
        ->where('i.name LIKE :name')
        ->setParameter('name', '%' . $ingredient->getName() . '%')
        ->getQuery()
        ->execute()) > 0;

      if (!$ingredientExists) 
        $this->getDoctrine()->getManager()->persist($ingredient);
        $this->getDoctrine()->getManager()->flush();

        $addedIngredient = $this->getDoctrine()->getManager()->getRepository('AppBundle:Ingredient')->createQueryBuilder('i')
          ->select('i.id')
          ->where('i.name LIKE :name')
          ->setParameter('name', '%' . $ingredient->getName() . '%')
          ->getQuery()
          ->execute();

        $ingredient->setId($addedIngredient[0]['id']);
       else 
        $recipe->removeIngredient($ingredient);

        $ingredientBDD = $this->getDoctrine()->getManager()->getRepository('AppBundle:Ingredient')->createQueryBuilder('i')
          ->select('i.id, i.name')
          ->where('i.name LIKE :name')
          ->setParameter('name', '%' . $ingredient->getName() . '%')
          ->getQuery()
          ->execute()[0];

        $ingredient->setId($ingredientBDD["id"]);
        $ingredient->setName($ingredientBDD["name"]);
      

      $recipeIngredient = new RecipeIngredient();
      $recipeIngredient->setIngredient($ingredient);
      $recipeIngredient->setRecipe($recipe);
      $recipeIngredient->setQuantity($ingredient->getQuantity());
      $recipeIngredient->setUnity($ingredient->getUnity());

      $recipe->addRecipeIngredient($recipeIngredient);
      $ingredient->addRecipeIngredient($recipeIngredient);
    
  

【问题讨论】:

包含你在控制器中使用的代码。 是的 - 我们需要查看您正在使用的表单/控制器代码 我编辑问题以添加控制器代码 【参考方案1】:

我解决了这个问题,方法是“手动”保留成分并进行所有需要的更改(编辑注释,将 RecipeIngredient 中的 $ingredient 更改为 $ingredient_id,...)。

而这种方式正是我想要的方式。

【讨论】:

以上是关于Symfony 3.4 - 如果在多对多关系上不存在,则持续存在的主要内容,如果未能解决你的问题,请参考以下文章

如果我在多对多关系中添加相同的 NSManagedObject 会发生啥?

春季 JPA |在多对多关系中搜索

具有多对多关系的 Symfony 5 对象序列化超时

在多对多关系中删除正确

如何在多对多关系中更新日期

Symfony 查询生成器(多对多关系)