簡體   English   中英

Doctrine 2與連接表中的其他列的多對多關系

[英]Doctrine 2 Many to Many relation with additional columns in join table

所以,我現在已經玩了一段時間使用學說並在一些基礎項目中使用它,但我決定回過頭來深入了解它能做些什么。

我現在決定切換到symfony 2作為我選擇的框架,並且正在研究更深入的教義2可以做什么。

我一直試圖解決的一件事是學說中的多對多關系。 我開始建立一個配方系統,正在研究配方和配料之間的關系,這給了我3個實體,配方,配方成分和成分。 我不能使用直接多對多關系的原因是因為我想在每個成分的連接表(單位和數量)中存儲兩個額外的列。

我目前遇到的問題是實體仍然存在,但是沒有插入連接表中的recipe_id。 我已經嘗試了所有我能想到的東西,並通過每個線程和網站尋找答案。 我確信這是我完全不知道的事情。 請幫助,下面是我到目前為止的代碼:

<?php
namespace Recipe\RecipeBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
 * @ORM\Entity
 * @ORM\Table(name="recipe")
 * @ORM\HasLifecycleCallbacks()
 */
class Recipe{

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

/**
 * @ORM\OneToMany(targetEntity="RecipeIngredient", mappedBy="recipe", cascade=       {"persist"})
 */
protected $ingredients;
/**
 * @ORM\Column(type="string")
 * @var string $title
 *
 */
protected $title;
/**
 * Constructor
 */
public function __construct()
{
    $this->ingredients = new \Doctrine\Common\Collections\ArrayCollection();
}

/**
 * Get id
 *
 * @return integer
 */
public function getId()
{
    return $this->id;
}

/**
 * Add ingredients
 *
 * @param \Recipe\RecipeBundle\Entity\RecipeIngredient $ingredients
 * @return Recipe
 */
public function addIngredient(\Recipe\RecipeBundle\Entity\RecipeIngredient $ingredients)
{
    $ingredients->setRecipe($this);
    $this->ingredients[] = $ingredients;

    return $this;
}

/**
 * Remove ingredients
 *
 * @param \Recipe\RecipeBundle\Entity\RecipeIngredient $ingredients
 */
public function removeIngredient(\Recipe\RecipeBundle\Entity\RecipeIngredient $ingredients)
{
    $this->ingredients->removeElement($ingredients);
}

/**
 * Get ingredients
 *
 * @return \Doctrine\Common\Collections\Collection
 */
public function getIngredients()
{
    return $this->ingredients;
}

/**
 * Set title
 *
 * @param string $title
 * @return Recipe
 */
public function setTitle($title)
{
    $this->title = $title;

    return $this;
}

/**
 * Get title
 *
 * @return string
 */
public function getTitle()
{
    return $this->title;
}
}

和recipeIngredient

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

/**
 * @ORM\ManyToOne(targetEntity="Recipe", inversedBy="ingredients")
 * */
protected $recipe;

/**
 * @ORM\ManyToOne(targetEntity="Ingredient", inversedBy="ingredients" , cascade={"persist"})
 * */
protected $ingredient;

/**
 * @ORM\Column(type="string")
 * @var string  $quantity
 *
 */
protected $quantity;

/**
 * @ORM\Column(type="string")
 * @var string $unit
 *
 */
protected $unit;

/**
 * Get id
 *
 * @return integer
 */
public function getId()
{
    return $this->id;
}

/**
 * Set quantity
 *
 * @param string $quantity
 * @return RecipeIngredient
 */
public function setQuantity($quantity)
{
    $this->quantity = $quantity;

    return $this;
}

/**
 * Get quantity
 *
 * @return string
 */
public function getQuantity()
{
    return $this->quantity;
}

/**
 * Set unit
 *
 * @param string $unit
 * @return RecipeIngredient
 */
public function setUnit($unit)
{
    $this->unit = $unit;

    return $this;
}

/**
 * Get unit
 *
 * @return string
 */
public function getUnit()
{
    return $this->unit;
}

/**
 * Set recipe
 *
 * @param \Recipe\RecipeBundle\Entity\Recipe $recipe
 * @return RecipeIngredient
 */
public function setRecipe(\Recipe\RecipeBundle\Entity\Recipe $recipe = null)
{
    $this->recipe = $recipe;

    return $this;
}

/**
 * Get recipe
 *
 * @return \Recipe\RecipeBundle\Entity\Recipe
 */
public function getRecipe()
{
    return $this->recipe;
}

/**
 * Set ingredient
 *
 * @param \Recipe\RecipeBundle\Entity\Ingredient $ingredient
 * @return RecipeIngredient
 */
public function setIngredient(\Recipe\RecipeBundle\Entity\Ingredient $ingredient = null)
{
    $this->ingredient = $ingredient;

    return $this;
}

/**
 * Get ingredient
 *
 * @return \Recipe\RecipeBundle\Entity\Ingredient
 */
public function getIngredient()
{
    return $this->ingredient;
}
}

你的基本想法是正確的。 如果你想擁有一個ManyToMany關系,但你需要在連接表中添加額外的字段,那么完全按照你的描述:使用一個具有2個ManyToOne關系和一些附加字段的新實體。

很遺憾,您還沒有提供控制器代碼,因為很可能您的問題就在那里。

基本上如果你做的事情如下:

$ri = new RecipeIngredient;
$ri->setIngredient($i);
$ri->setRecipe($r);
$ri->setQuantity(1);
$em->persist($ri);
$em->flush();

您應始終在數據庫表中獲得正確的記錄,並正確填寫recipe_id和ingredient_id。

檢查你的代碼以下內容也應該有效,盡管我個人認為這對錯誤更敏感:

$ri = new RecipeIngredient;
$ri->setIngredient($i);
$ri->setQuantity(1);
// here we assume that Recipe->addIngredient also does the setRecipe() for us and 
// that the cascade field is set correctly to cascade the persist on $ri
$r->addIngredient($ri);
$em->flush();

為了進一步閱讀,我建議關於這個主題的其他主題,例如: Doctrine2:在參考表中使用額外列處理多對多的最佳方法

如果我正確理解這個模型,配方的構造及其相關的recipeIngredients是並發的。 如果調用了receipeIngredient-> setRecipe(),那么在持久化並且沒有id之前,您可能沒有id,缺省的null將放在recipeIngredient-> recipe字段中。 這通常使用級聯處理:“persist”(示例中的配方字段不存在,但您可以在控制器中顯式處理它:

/** 
  * Creates a new Recipe entity.
  *
  */
public function createAction(Request $request)
{
  $em = $this->getDoctrine()->getManager();
  $form = $this->createForm(new RecipeType());  
  $form->bind($request);

  if ($form->isValid()){
    $data = $form->getData();
    $recipeId = $data->getId();
    $recipeIngredients=$data->getIngredients();
    $recipe=$em->getRepository('reciperecipeBundle:Recipe')
               ->findOneById($RecipeId);
    if (null === $Recipe)
      {$Recipe=new Recipe();}  
    foreach ($recipeIngredients->toArray() as $k => $i){
      $recipeIngredient=$em->getRepository('reciperecipeBundle:recipeIngredient')
                           ->findOneById($i->getId());
      if (null === $recipeIngredient)
        {$recipeIngrediente=new RecipeIngredient();}  
      $recipe->addIngredient($i);

      // Next line *might* be handled by cascade: "persist"        
      $em->persist($recipeIngredient);
    }  
    $em->persist($Recipe);
    $em->flush();
    return $this->redirect($this->generateUrl('Recipe', array()));
  }   

  return $this->render('reciperecipeBundle:Recipe:new.html.twig'
                      ,array('form'   => $form->createView()));  
}

我不確定這是否是一個解決方案,但它很容易嘗試,可能會有所幫助。 當我創建這種類型的關系時,我用來編寫另一個anotation,即@ORM \\ JoinColumn ,就像在這個例子中一樣:

我們有一個實體A,一個實體B和一個代表關系的AB類,並添加了一些其他字段,就像你的情況一樣。

我的關系如下:

use Doctrine\ORM\Mapping as ORM;

/**
 *  
 *
 * @ORM\Table(name="a_rel_b")
 * @ORM\Entity
 */
class AB
{

    /**
     * @var integer
     *  @ORM\Id
     * @ORM\ManyToOne(targetEntity="A", inversedBy="b")
     * @ORM\JoinColumn(name="a_id", referencedColumnName="id")  
     **/

    private $a;


    /**
     * @var integer
     *  @ORM\Id
     * @ORM\ManyToOne(targetEntity="B", inversedBy="a")
     * @ORM\JoinColumn(name="b_id", referencedColumnName="id")
     **/
    private $b;
// ...

name表示關系表中字段的名稱,而referencedColumnName是引用實體表中id字段的名稱(即b_id是a_rel_b中引用表B中列id的列)

你不能,因為它不再是一種關系[這是def,兩個原始實體的集合的笛卡爾積的子集]。

你需要一個中間實體,引用RecipeIngredient - 稱之為RecipeElementRecipeEntry或者左右,並添加你想要的字段。

您也可以在Recipe添加地圖,在其中保存您保存的每種Ingredient的屬性,如果沒有重復,則易於維護。

如需進一步閱讀,請查看這個熱門問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM