I hope you help me. I use Symfony 2.x and Doctrine 2.x , and I would like to create one form consisting of two objects. By filling out this form, I want the data to be stored in two objects of the doctrine.
For simplicity, I gave an example. A multilingual online store must have a product name and description in English and French. I want to use one form to create a new product. This form of creation will include data from the Product object (id; productTranslations; price, productTranslations), as well as from the ProductTranslation object (id; name; description, language, product). The resulting product product form has the following fields (Name; Description; Language (EN / FR), Price).
The Product and ProductTranslation objects are linked to each other through a one-to-many bidirectional relationship. The site is owned by ProductTranslation.
After submitting the form, I want to save data for both objects (Product and ProductTranslation). That's where it all goes wrong. I can not save data.
So I tried the following:
Product Entity :
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; use Symfony\Component\Validator\Constraints as Assert; class Product { private $id; private $price; private $productTranslations; public function __construct() { $this->productTranslations = new ArrayCollection(); } public function getId() { return $this->id; } public function setPrice($price) { $this->price = $price; return $this; } public function getPrice() { return $this->price; } public function setProductTranslations($productTranslations) { $this->productTranslations = $productTranslations; return $this; } public function getProductTranslations() { return $this->productTranslations; } public function addProductTranslation(\AppBundle\Entity\ProductTranslation $productTranslation) { $this->productTranslations[] = $productTranslation; return $this; } public function removeProductTranslation(\AppBundle\Entity\ProductTranslation $productTranslation) { $this->productTranslations->removeElement($productTranslation); } }
ProductTranslation Entity :
<?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; class ProductTranslation { private $id; private $name; private $description; private $language; private $product; public function getId() { return $this->id; } public function setName($name) { $this->name = $name; return $this; } public function getName() { return $this->name; } public function setDescription($description) { $this->description = $description; return $this; } public function getDescription() { return $this->description; } public function setLanguage($language) { $this->language = $language; return $this; } public function getLanguage() { return $this->language; } public function setProduct(\AppBundle\Entity\Product $product = null) { $this->product = $product; return $this; } public function getProduct() { return $this->product; } }
Producttype
<?php namespace AppBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\MoneyType; class ProductType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('productTranslations', ProductTranslationType::class, array('label' => false, 'data_class' => null)); $builder ->add('price', MoneyType::class) ; } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\Product' )); } }
ProductTranslationType
<?php namespace AppBundle\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; class ProductTranslationType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('name', TextType::class) ->add('description', TextareaType::class ) ->add('language', ChoiceType::class, array('choices' => array('en' => 'EN', 'fr' => 'FR'))) ; } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( 'data_class' => 'AppBundle\Entity\ProductTranslation' )); } }
Productcontroller
<?php namespace AppBundle\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use AppBundle\Entity\Product; use AppBundle\Form\ProductType; use AppBundle\Entity\ProductTranslation; class ProductController extends Controller { public function newAction(Request $request) { $em = $this->getDoctrine()->getManager(); $product = new Product(); $productTranslation = new ProductTranslation(); $form = $this->createForm('AppBundle\Form\ProductType', $product); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $em = $this->getDoctrine()->getManager(); $product->getProductTranslations()->add($product); $productTranslation->setProduct($product); $em->persist($productTranslation); $em->flush(); return $this->redirectToRoute('product_show', array('id' => $product->getId())); } return $this->render('product/new.html.twig', array( 'product' => $product, 'form' => $form->createView(), )); } }
Error
Warning: spl_object_hash() expects parameter 1 to be object, string given 500 Internal Server Error - ContextErrorException
I looked at the cookbook for reference: http://symfony.com/doc/current/book/forms.html#embedded-forms , however I was not able to get it working.
Update 1
I have not yet found the answer to my question. Following the comments below, I looked at the associations. I have implemented corrections in the ProductController, which allows me to check whether the data is correctly inserted into the database. The data was inserted correctly, but I can not insert it through the form. Hope someone can help me.
Productcontroller
<?php namespace AppBundle\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use AppBundle\Entity\Product; use AppBundle\Form\ProductType; public function newAction(Request $request) { $em = $this->getDoctrine()->getManager(); $product = new Product(); $productTranslation = new ProductTranslation(); $productTranslation->setProduct($product); $productTranslation->setName('Product Q'); $productTranslation->setDescription('This is product Q'); $productTranslation->setLanguage('EN'); $product->setPrice(95); $product->addProductTranslation($productTranslation); $em->persist($product); $em->persist($productTranslation); $em->flush(); $form = $this->createForm('AppBundle\Form\ProductType', $product); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $em = $this->getDoctrine()->getManager(); $product->getProductTranslations()->add($product); $productTranslation->setProduct($product); $em->persist($productTranslation); $em->flush(); return $this->redirectToRoute('product_show', array('id' => $product->getId())); } return $this->render('product/new.html.twig', array( 'product' => $product, 'form' => $form->createView(), )); }
Now I get the following error message:
Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Product#$productTranslations", got "string" instead.
Update 2
The var_dump () parameter from the product variable in the ProductController newAction before saving the data shows:
object(AppBundle\Entity\Product)[493] private 'id' => null private 'price' => float 3 private 'productTranslations' => object(Doctrine\Common\Collections\ArrayCollection)[494] private 'elements' => array (size=4) 'name' => string 'abc' (length=45) 'description' => string 'alphabet' (length=35) 'language' => string 'en' (length=2) 0 => object(AppBundle\Entity\ProductTranslation)[495] ...