2016-06-02 4 views
6

Надеюсь, вы могли бы мне помочь. Я использую Symfony 2.x и Doctrine 2.x и хотел бы создать одна форма, состоящая из двух объектов. Заполнив эту форму, я хочу, чтобы данные сохранялись в двух объектах доктрины.Symfony2 встроенная форма не сохраняется в базе данных

Для простоты я привел пример. Многоязычный интернет-магазин должен иметь название и описание продукта на английском и французском языках. Я хочу использовать одну форму для создания нового продукта. Эта форма создания будет включать данные из объекта Product (id; productTranslations; price, productTranslations), а также из объекта ProductTranslation (id; name; description, language, product). Полученная форма продукта продукта имеет следующие поля (Name; Description; Language (EN/FR), Price).

Объект Product и ProductTranslation связан друг с другом посредством двунаправленного отношения «один ко многим». Владельцем сайта является ProductTranslation.

После отправки формы я хочу сохранить данные для обоих объектов (Product and ProductTranslation). Вот где все идет не так. Я не могу сохранить данные.

Thusfar, я попробовал следующее:

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(), 
     )); 
    } 
} 

Ошибка:

Warning: spl_object_hash() expects parameter 1 to be object, string given 
500 Internal Server Error - ContextErrorException 

Я посмотрел поваренную книгу на помощь: http://symfony.com/doc/current/book/forms.html#embedded-forms, однако мне не удалось заставить ее работать.

Update 1

Я не нашел ответа на мой вопрос еще. Следуя приведенным ниже комментариям, я взглянул на ассоциации. Я внедрил корректировки в ProductController, что позволяет мне проверить, правильно ли вставлены данные в базу данных. Данные были вставлены правильно, но я не могу вставить его через форму. Надеюсь, кто-то может мне помочь.

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(), 
    )); 
} 

Я получаю следующее сообщение об ошибке сейчас:

Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\Product#$productTranslations", got "string" instead. 

Update 2

var_dump() из переменной продукта в ProductController newAction перед тем сохраняющиеся данные показывают:

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] 
       ... 
+0

Сначала вы должны исправить сопоставления объектов http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html и настроить правильные методы управления ассоциациями http: // doctrine -orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/working-with-association.html, чем пытаться сохранить продукт с переводами в контроллере, и если он работает, то вы можете перейти к использованию форм. – 1ed

+0

@ 1ed Я думаю, вы ссылались на ошибку, которую я сделал с объектом ProductTranslation. Я забыл добавить аннотацию «много к одному» для продукта. Однако результат все тот же. –

+0

Вы только что внесли изменения в сопоставления? Если это так, вам нужно запустить 'php app/console doctrine: schema: update -force'. В противном случае у меня нет другого предложения. Сообщение об ошибке не отображается много. Что произойдет, если вы используете «app_dev.php», добавленный к вашему URL? –

ответ

2

Я смотрел через контроллер, и я замечаю, что вы упорно ProductTranslation, однако в ProductTranslation лица вам не хватает аннотацию к cascade={"persist"} на отношения к сущности продукта. Он должен быть указан в сущности, которую вы сохраняете, если хотите сохранить связанные объекты.

+0

Я допустил ошибку. В текущей версии у меня есть 'cascade = {" persist "}', но это не изменяет ошибку. –

4

Ошибка поясняется; productTranslations должен быть массивом или массивом. Вместо этого это «строка».

Так в конструкторе продукта:

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

Для сеттер/добытчика вы можете использовать:

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: В YAML с Symfony2.3, Вот отображения конфигурации объекта Я использую (Чтобы подчеркнуть, где нужно сохранить каскад).

//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 

Кроме того, обратите внимание, что вам не нужно setProductTranslation() сеттера в сущности продукта, так и addremove цели, чтобы заменить его.

Edit2:

В Symfony2, вот как я обрабатывать формы с коллекциями:

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() 

        )) 

      ; 

     } 

Я не знаю, почему вы не указываете коллекцию в вашем formType. это новая версия Symfony?

+0

Моя ошибка, я уже заметил это и исправил его. Однако я забыл обновить вопрос. Не могли бы вы взглянуть. –

+0

Я взглянул на ваше редактирование. Вы имеете в виду, что вы получаете ту же ошибку даже после изменений? –

+0

Да, я получаю ту же ошибку. Сообщение об ошибке указывает, что это проблема с массивом, но я не понимаю, почему она преобразуется в строку. Я думаю, что проблема заключается в контроле продукта после проверки. Объект преобразуется в строку. –

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

ИДК, что вы хотите сделать здесь, но я считаю, что вам нужно использовать:

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

$ продукт-> getProductTranslations() возвращает ArrayCollection класса «ProductTranslation» и вы объединяете этот массив со значением типа «Продукт».

Это немного непоследовательно. Если я ошибаюсь, можете ли вы сказать мне, что вы делаете в этом предложении?

Спасибо!

+0

Благодарим вас за ввод. Не беспокойтесь о моем коде. Проблема остается, если я сохраняю данные. Я сделал var_dump моей переменной продукта. Надеюсь, это поможет решить мою проблему. –

Смежные вопросы