Reputation: 191
I work with symfony 3 and doctrine and this is my problem:
I want to create dishes composed of ingredients of different quantity.
The database will look like this:
[ Dish ] <===> [ IngredientDish ] <===> [ Ingredient ]
[------] [----------------] [------------]
[- name] [- Dish ] [-name ]
[ ] [- Ingredient ] [ ]
[ ] [- quantity ] [ ]
[------] [----------------] [------------]
This is my code :
Dish.php
/**
* @ORM\Table(name="dish")
* @ORM\Entity(repositoryClass="AppBundle\Repository\DishRepository")
*/
class Dish
{
/**
* @ORM\OneToMany(targetEntity="AppBundle\Entity\IngredientDish",
* mappedBy="dish")
*/
private $ingredientsDish;
[...]
public function addIngredientDish(IngredientDish $ingredient)
{
$this->ingredientsDish[] = $ingredient;
return $this;
}
public function getIngredientsDish()
{
return $this->ingredientsDish;
}
}
Ingredient.php
/**
* @ORM\Table(name="ingredient")
* @ORM\Entity(repositoryClass="AppBundle\Repository\IngredientRepository")
*/
class Ingredient
{
/**
* @ORM\OneToMany(targetEntity="AppBundle\Entity\IngredientDish",
* mappedBy="ingredient")
* @Assert\Type(type="AppBundle\Entity\IngredientDish")
*/
private $ingredientsDish;
[...]
public function addIngredientDish(IngredientDish $ingredientDish)
{
$this->ingredientDish[] = $ingredientDish;
return $this;
}
public function getingredientsDish()
{
return $this->ingredients;
}
}
IngredientDish.php
/**
* @ORM\Table(name="ingredient_dish")
* @ORM\Entity(repositoryClass="AppBundle\Repository\IngredientDishRepository")
*/
class IngredientDish
{
/**
* @ORM\Column(name="quantity", type="smallint")
* @Assert\NotBlank()
* @Assert\Length(min=1)
*/
private $quantity = 1;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Ingredient",
* inversedBy="ingredientsDish")
* @Assert\Type(type="AppBundle\Entity\Ingredient")
*/
private $ingredient;
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Dish",
* inversedBy="ingredientsDish")
* @ORM\JoinColumn()
* @Assert\Type(type="AppBundle\Entity\Dish")
*/
private $dish;
public function __construct(Ingredient $ingredient, Dish $dish, $quantity = 1)
{
$this->setIngredient($ingredient);
$this->setDish($dish);
$this->setQuantity($quantity);
}
public function setQuantity($quantity)
{
$this->quantity = $quantity;
return $this;
}
public function getQuantity()
{
return $this->quantity;
}
public function setIngredient(Ingredient $ingredient)
{
$this->ingredient = $ingredient;
return $this;
}
public function getIngredient()
{
return $this->ingredient;
}
public function setDish(Dish $dish)
{
$this->dish = $dish;
return $this;
}
public function getDish()
{
return $this->dish;
}
}
My test code
$em = $this->getDoctrine()->getManager();
//Get an apple pie
$dish = $em->getRepository('AppBundle:Dish')->find(6);
//Get an apple
$ingredient = $em->getRepository('AppBundle:Ingredient')->find(11);
$quantityApple = 5;
$ingredientDish = new IngredientDish($ingredient, $dish, $quantityApple);
$ingredient->addIngredientDish($ingredientDish);
$dish->addIngredientDish($ingredientDish);
$em->persist($ingredientDish);
$em->persist($dish);
$em->flush();
After execution, i have an interesting entry:
mysql> select * from ingredient_dish;
+----+---------------+----------+---------+
| id | ingredient_id | quantity | dish_id |
+----+---------------+----------+---------+
| 1 | 11 | 5 | 6 |
+----+---------------+----------+---------+
But after, if I try to get my dish:
$dish = $em->getRepository('AppBundle:Dish')->find(6);
dump($dish->getIngredientsDish());
It has no ingredients :
PersistentCollection {#1180 ▼
-snapshot: []
-owner: Dish {#1146 ▶}
-association: array:15 [ …15]
-em: EntityManager {#1075 …11}
-backRefFieldName: "dish"
-typeClass: ClassMetadata {#1157 …}
-isDirty: false
#collection: ArrayCollection {#1181 ▼
-elements: [] <<<<<<<<<<<<<<<<<<<<< EMPTY
}
#initialized: false
}
The database is not empty after the execution of my test code, so I think there is an error of getter. Can you help me, do you see something false ?
Thanks you for your help ! :)
Upvotes: 2
Views: 90
Reputation: 15686
I think that everything is fine, but you got mislead by lazy loading which is apparently smarter than you think it is. ;-)
When you do
$dish->getIngredientsDish();
you receive PersistentCollection
which extends AbstractLazyCollection
.
But the collection is still not fetched from DB(!)
Take a look closer into your var_dump
result
PersistentCollection {#1180 ▼
-snapshot: []
-owner: Dish {#1146 ▶}
-association: array:15 [ …15]
-em: EntityManager {#1075 …11}
-backRefFieldName: "dish"
-typeClass: ClassMetadata {#1157 …}
-isDirty: false
#collection: ArrayCollection {#1181 ▼
-elements: [] <<<<<<<<<<<<<<<<<<<<< EMPTY //<= yeah empty, but...
}
#initialized: false // <= it's still not initialized!
}
As you can see there's initialized
property which says that the collection is still not initialized (not fetched form DB).
Just try to use it. It will fetch the collection on first usage.
Upvotes: 1
Reputation: 7764
First, there are some problems with you annotations mappings. Make the follow changes:
/**
* @ORM\Table(name="dish")
* @ORM\Entity(repositoryClass="AppBundle\Repository\DishRepository")
*/
class Dish
{
/**
* @ORM\Column(name="dish_id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $dish_id;
/**
* @ORM\OneToMany(targetEntity="AppBundle\Entity\IngredientDish",
* mappedBy="dish")
*/
private $ingredientsDish = null;
public function __construct() {
$this->ingredientsDish = new ArrayCollection();
}
...
}
/**
* @ORM\Table(name="ingredient_dish")
* @ORM\Entity(repositoryClass="AppBundle\Repository\IngredientDishRepository")
*/
class IngredientDish
{
...
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Dish",
* inversedBy="ingredientsDish")
* @ORM\JoinColumn(name="dish_id", referencedColumnName="country_id")
* @Assert\Type(type="AppBundle\Entity\Dish")
*/
private $dish;
...
}
In the Dish entity you need to have a constructor for the ArrayCollection, and set it to null.
You can also take a look at this other post of mine (on stackoverflow for a reference:
Doctrine Entities Relations confusing
Upvotes: 0