Symfony2 built-in form does not save data in database

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; /** * Product * * @ORM\Table(name="product") * @ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository") */ class Product { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="price", type="decimal", precision=10, scale=0) */ private $price; /** * @ORM\OneToMany(targetEntity="AppBundle\Entity\ProductTranslation", mappedBy="product") */ private $productTranslations; public function __construct() { $this->productTranslations = new ArrayCollection(); } /** * Get id * * @return int */ public function getId() { return $this->id; } /** * Set price * * @param string $price * * @return Product */ public function setPrice($price) { $this->price = $price; return $this; } /** * Get price * * @return string */ public function getPrice() { return $this->price; } /** * Set productTranslations * * @param \stdClass $productTranslations * * @return Product */ public function setProductTranslations($productTranslations) { $this->productTranslations = $productTranslations; return $this; } /** * Get productTranslations * * @return \stdClass */ public function getProductTranslations() { return $this->productTranslations; } /** * Add productTranslation * * @param \AppBundle\Entity\ProductTranslation $productTranslation * * @return Product */ public function addProductTranslation(\AppBundle\Entity\ProductTranslation $productTranslation) { $this->productTranslations[] = $productTranslation; return $this; } /** * Remove productTranslation * * @param \AppBundle\Entity\ProductTranslation $productTranslation */ public function removeProductTranslation(\AppBundle\Entity\ProductTranslation $productTranslation) { $this->productTranslations->removeElement($productTranslation); } } 

ProductTranslation Entity :

 <?php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * ProductTranslation * * @ORM\Table(name="product_translation") * @ORM\Entity(repositoryClass="AppBundle\Repository\ProductTranslationRepository") */ class ProductTranslation { /** * @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="text") */ private $description; /** * @var string * * @ORM\Column(name="language", type="string", length=5) */ private $language; /** * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Product", inversedBy="productTranslations",cascade={"persist"}) * @ORM\JoinColumn(name="product_translation_id", referencedColumnName="id") * */ private $product; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set name * * @param string $name * * @return ProductTranslation */ 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 ProductTranslation */ public function setDescription($description) { $this->description = $description; return $this; } /** * Get description * * @return string */ public function getDescription() { return $this->description; } /** * Set language * * @param string $language * * @return ProductTranslation */ public function setLanguage($language) { $this->language = $language; return $this; } /** * Get language * * @return string */ public function getLanguage() { return $this->language; } /** * Set product * * @param \AppBundle\Entity\Product $product * * @return ProductTranslation */ public function setProduct(\AppBundle\Entity\Product $product = null) { $this->product = $product; return $this; } /** * Get product * * @return \AppBundle\Entity\Product */ 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 { /** * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('productTranslations', ProductTranslationType::class, array('label' => false, 'data_class' => null)); $builder ->add('price', MoneyType::class) ; } /** * @param OptionsResolver $resolver */ 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 { /** * @param FormBuilderInterface $builder * @param array $options */ 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'))) ; } /** * @param OptionsResolver $resolver */ 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; /** * Product controller. * */ class ProductController extends Controller { /** * Creates a new Product entity. * */ 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; /** * Creates a new Product entity. * */ public function newAction(Request $request) { $em = $this->getDoctrine()->getManager(); $product = new Product(); $productTranslation = new ProductTranslation(); /* Sample data insertion */ $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(); /* End sample data insertion */ $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] ... 
+6
source share
3 answers

The error is self-evident; productTranslations must be an array or an array. Instead, it is a string.

So, in the Product constructor:

 public function __construct() { $this->activityTranslations = new ArrayCollection(); $this->productTranslations = new \Doctrine\Common\Collections\ArrayCollection(); } 

For setter / getter you can use:

 public function addProductTranslation(AppBundle\Entity\ProductTranslation $pt) { $this->productTranslations[] = $pt; $pt->setProduct($this); return $this; } public function removeProductTranslation(AppBundle\Entity\ProductTranslation $pt) { $this->productTranslations->removeElement($pt); } public function getProductTranslations() { return $this->productTranslations; } 

Edit: In YAML with Symfony2.3. Here is the object mapping configuration I'm using (for emphasis where you need to add cascading persistence).

 //Product entity oneToMany: productTranslations: mappedBy: product targetEntity: App\Bundle\...Bundle\Entity\ProductTranslation cascade: [persist] // ProductTranslation entity manyToOne: product: targetEntity: App\Bundle\..Bundle\Entity\Product inversedBy: productTranslations joinColumn: name: product_id type: integer referencedColumnName: id onDelete: cascade 

Also, note that you do not need setProductTranslation() setter in the Product object, as add and remove tend to replace it.

Edit2:

In Symfony2, here's how I handle forms with collections:

 class ProductType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('productPrice','number',array('required' => false)) ->add('productTranslations', 'collection', array( 'type' => new ProducatTranslationType() )) ; } 

I do not know why you are not specifying a collection in your form type. is this a new version of symfony?

+4
source

I looked at your controller and I notice that you are saving ProductTranslation, however, the annotation for cascade={"persist"} in relation to the Product object is missing in the ProductTranslation object. It must be specified in the entity that you save if you want to save related objects.

+2
source
 $product->getProductTranslations()->add($product); $productTranslation->setProduct($product); 

Idk what you want to do here, but I believe that you need to use:

 $product->addProductTranslation($productTranslation); $productTranslation->setProduct($product); 

$ product-> getProductTranslations () returns an ArrayCollection of class 'ProductTranslation', and you combine this array with a value of type 'Product'.

This is a little inconsistent, if I'm wrong, can you tell me what you are doing in this sentence?

Thanks!

+2
source

All Articles