Symfony2 嵌入式表单不会将数据持久化到数据库



我希望你能帮助我。我正在使用Symfony 2.x和Doctrine 2.x,我想创建一个由两个实体组成的表单。通过填写这一表格,我想将数据保存到两个学说实体。

为了简单起见,我举了一个例子。多语言网店需要有英文和法文的名称和产品描述。我想用一个表单创建一个新产品。此创建表单将包括来自"产品"实体(id;productTranslations;price,ProductTranslation)以及来自"产品翻译"实体(id;name;description;language,Product)的数据。生成的创建产品表单包含以下字段(名称;描述;语言(EN/FR);价格)。

Product和ProductTranslation实体通过双向的一对多关系相互关联。关系的所有者网站是ProductTranslation。

表单提交后,我希望将数据持久化到两个实体(Product和ProductTranslation)。这就是出错的地方。我无法保存这些数据。

Thusfar,我已经尝试了以下内容:

产品实体

<?php
namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
use DoctrineCommonCollectionsArrayCollection;
use SymfonyComponentValidatorConstraints as Assert;
/**
* Product
*
* @ORMTable(name="product")
* @ORMEntity(repositoryClass="AppBundleRepositoryProductRepository")
*/
class Product
{   
/**
* @var int
*
* @ORMColumn(name="id", type="integer")
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORMColumn(name="price", type="decimal", precision=10, scale=0)
*/
private $price;
/**
* @ORMOneToMany(targetEntity="AppBundleEntityProductTranslation", 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 AppBundleEntityProductTranslation $productTranslation
*
* @return Product
*/
public function addProductTranslation(AppBundleEntityProductTranslation $productTranslation)
{
$this->productTranslations[] = $productTranslation;
return $this;
}
/**
* Remove productTranslation
*
* @param AppBundleEntityProductTranslation $productTranslation
*/
public function removeProductTranslation(AppBundleEntityProductTranslation $productTranslation)
{
$this->productTranslations->removeElement($productTranslation);
}
}

产品翻译实体

<?php
namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
/**
* ProductTranslation
*
* @ORMTable(name="product_translation")
* @ORMEntity(repositoryClass="AppBundleRepositoryProductTranslationRepository")
*/
class ProductTranslation
{
/**
* @var int
*
* @ORMColumn(name="id", type="integer")
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORMColumn(name="name", type="string", length=255)
*/
private $name;
/**
* @var string
*
* @ORMColumn(name="description", type="text")
*/
private $description;
/**
* @var string
*
* @ORMColumn(name="language", type="string", length=5)
*/
private $language;
/**
* @ORMManyToOne(targetEntity="AppBundleEntityProduct", inversedBy="productTranslations",cascade={"persist"})
* @ORMJoinColumn(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 AppBundleEntityProduct $product
*
* @return ProductTranslation
*/
public function setProduct(AppBundleEntityProduct $product = null)
{
$this->product = $product;
return $this;
}
/**
* Get product
*
* @return AppBundleEntityProduct
*/
public function getProduct()
{
return $this->product;
}
}

产品类型

<?php
namespace AppBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyComponentFormExtensionCoreTypeMoneyType;
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' => 'AppBundleEntityProduct'
));
}
}

产品翻译类型

<?php
namespace AppBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyComponentFormExtensionCoreTypeTextType;
use SymfonyComponentFormExtensionCoreTypeTextareaType;
use SymfonyComponentFormExtensionCoreTypeChoiceType;
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' => 'AppBundleEntityProductTranslation'
));
}
}

产品控制器

<?php
namespace AppBundleController;
use SymfonyComponentHttpFoundationRequest;
use SymfonyBundleFrameworkBundleControllerController;
use AppBundleEntityProduct;
use AppBundleFormProductType;
use AppBundleEntityProductTranslation;
/**
* 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('AppBundleFormProductType', $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-表格,但我一直无法让它发挥作用。

更新1

我还没有找到问题的答案。根据下面的评论,我看了一下这些协会。我已经对ProductController进行了调整,它使我能够测试数据是否以正确的方式插入数据库。数据插入正确,但我无法通过表格插入。希望有人能帮助我。

产品控制器

<?php
namespace AppBundleController;
use SymfonyComponentHttpFoundationRequest;
use SymfonyBundleFrameworkBundleControllerController;
use AppBundleEntityProduct;
use AppBundleFormProductType;
/**
* 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('AppBundleFormProductType', $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 "DoctrineCommonCollectionsCollection|array" for association field "AppBundleEntityProduct#$productTranslations", got "string" instead. 

更新2

在持久化数据之前,ProductController newAction中变量乘积的var_dump()显示:

object(AppBundleEntityProduct)[493]
private 'id' => null
private 'price' => float 3
private 'productTranslations' => 
object(DoctrineCommonCollectionsArrayCollection)[494]
private 'elements' => 
array (size=4)
'name' => string 'abc' (length=45)
'description' => string 'alphabet' (length=35)
'language' => string 'en' (length=2)
0 => 
object(AppBundleEntityProductTranslation)[495]
...

错误是不言自明的;productTranslations必须是Array或arrayCollection。它是一个"字符串"。

因此,在产品的构造函数中:

public function __construct()
{
$this->activityTranslations = new ArrayCollection();
$this->productTranslations = new DoctrineCommonCollectionsArrayCollection();
}  

对于setter/getter,您可以使用:

public function addProductTranslation(AppBundleEntityProductTranslation $pt)
{
$this->productTranslations[] = $pt;
$pt->setProduct($this);
return $this;
}

public function removeProductTranslation(AppBundleEntityProductTranslation  $pt)
{
$this->productTranslations->removeElement($pt);
}
public function getProductTranslations()
{
return $this->productTranslations;
}

编辑:在带有Symfony2.3的YAML中,这是我正在使用的对象映射配置(强调应该添加级联持久化的地方)。

//Product entity 
oneToMany:
productTranslations:
mappedBy: product
targetEntity: AppBundle...BundleEntityProductTranslation
cascade:      [persist]
// ProductTranslation entity
manyToOne:
product:
targetEntity: AppBundle..BundleEntityProduct
inversedBy: productTranslations
joinColumn:
name: product_id
type: integer
referencedColumnName: id
onDelete: cascade

另外,请注意,在Product实体中不需要setProductTranslation()setter,因为addremove旨在替换它

第2版:

在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吗?

我查看了您的控制器,注意到您正在持久化ProductTranslation,但是在ProductTranslations实体中,您在与Product实体的关系中缺少cascade={"persist"}的注释。如果要保存相关实体,则需要在要持久化的实体上指定它。

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

我知道你想在这里做什么,但我相信你需要使用:

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

$product->getProductTranslations()返回类"ProductTranslation"的ArrayCollection,并且您正在将该数组与类型为"product"的值合并。

这有点前后矛盾,如果我错了,你能告诉我你在那句话里做什么吗?

谢谢!

相关内容

  • 没有找到相关文章

最新更新