Reputation: 159
I'm working on a simple Symfony API and I'm having trouble setting a relation between two entities. I have this two classes: Recipe and Ingredient. A Recipe can have many ingredients and an ingredient can be present in many recipes so I used a ManyToMany association.
This is my Recipe class:
namespace RecipesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Recipe
*
* @ORM\Table(name="recipe")
* @ORM\Entity(repositoryClass="RecipesBundle\Repository\RecipeRepository")
*/
class Recipe
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=255, nullable=true)
*/
private $description;
/**
* @var string
*
* @ORM\Column(name="image", type="string", length=255, nullable=true)
*/
private $image;
/**
* @ORM\ManyToMany(targetEntity="Ingredient", inversedBy="recipes")
* @ORM\JoinTable(name="recipes_ingredients")
*/
private $ingredients;
public function __construct() {
$this->ingredients = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Recipe
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set description
*
* @param string $description
*
* @return Recipe
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set image
*
* @param string $image
*
* @return Recipe
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* @return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set ingredients
*
* @param array $ingredients
* @return Recipe
*/
public function setIngredients($ingredients) {
foreach($ingredients as $ingredient) {
$this->ingredients->add($ingredient);
}
return $this;
}
/**
* Get ingredients
*
* @return ArrayCollection
*/
public function getIngredients() {
return $this->ingredients;
}
}
This is my Ingredient class:
namespace RecipesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Ingredient
*
* @ORM\Table(name="ingredient")
*
@ORM\Entity(repositoryClass="RecipesBundle\Repository\IngredientRepository")
*/
class Ingredient
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* @var int
*
* @ORM\Column(name="amount", type="integer")
*/
private $amount;
/**
* @ORM\ManyToMany(targetEntity="Recipe", mappedBy="ingredients")
*/
private $recipes;
public __construct() {
$this->recipes = new ArrayCollection();
}
/**
* Get id
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Ingredient
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set amount
*
* @param integer $amount
*
* @return Ingredient
*/
public function setAmount($amount)
{
$this->amount = $amount;
return $this;
}
/**
* Get amount
*
* @return int
*/
public function getAmount()
{
return $this->amount;
}
}
And this is my controller:
class RecipeController extends Controller
{
/**
* @Route("/recipes")
*/
public function recipesAction(Request $request)
{
switch($request->getMethod()) {
case 'GET':
$recipes = $this->getDoctrine()->getRepository(Recipe::class)->findAll();
$return = $recipes;
break;
case 'POST':
$recipe = new Recipe();
$recipeData = json_decode($request->getContent(), true);
$recipe->setName($recipeData['name']);
$recipe->setDescription($recipeData['description']);
$recipe->setImage($recipeData['imagePath']);
$ingredients = $recipeData['ingredients'];
//var_dump($ingredients);die;
$recipe->setIngredients($ingredients);
//var_dump($recipe);die;
$em = $this->getDoctrine()->getManager();
$em->persist($recipe);
$em->flush();
$return = 'Recipe saved successfully';
break;
case 'PUT':
$recipes = json_decode($request->getContent(), true);
$em = $this->getDoctrine()->getManager();
foreach($recipes as $updating_recipe) {
$recipe = $this->getDoctrine()->getRepository(Recipe::class)->findOneBy(array('name' => $updating_recipe['name']));
if($recipe)
{
$recipe->setName($updating_recipe['name']);
$recipe->setDescription($updating_recipe['description']);
$recipe->setImage($updating_recipe['image']);
$recipe->setIngredients($updating_recipe['ingredients']);
$em->flush();
}
}
$return = 'Recipes saved successfully';
break;
case 'DELETE':
break;
}
return new JsonResponse($return);
}
}
The problem occurs when I try to save a new recipe (post request). I get the following error:
Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "RecipesBundle\Entity\Recipe#$ingredients", got "array" instead.
I don't understand why I get this error if I use the setIngredients correctly to add the ingredients to the array collection.
Any ideas on what could be wrong? Thanks in advance
EDIT 1:
I'm adding the dump of the ingredients as requested:
array (size=1)
0 =>
array (size=2)
'name' => string 'Spaghetti' (length=9)
'amount' => int 10
Upvotes: 0
Views: 549
Reputation: 159
Ok the problem is solved. As Sami indicated, I had to iterate through the ingredients that come in the request and create an Ingredient object for each one. Then add them to an ArrayCollection and set it using $recipe->setIngredients();
Thank you very much for your help.
Upvotes: 0