我有一个实体产品。我的产品可以有多个不同语言的名称。法语名称,英语名称等。我不想使用自动翻译。
用户必须在“产品”表单中写下名称并选择相应的语言。由于添加按钮,他可以根据需要添加许多名称。
所有语言都是由管理员用户创建的(另一种形式)。因此,语言也是一个具有名称(例如:英语)和代码(例如:EN)的实体。
因此,ProductType
是我的主要表单,ProductNameType
是我的"集合"形式
当用户创建一个包含两个名称的新产品(一个是法语,另一个是英文)时,产品会保存在我的数据库中,另外两个名称也会创建并保存在另一个表中。
此时,一切运作良好。我的addAction()
很好,产品和相应的名称都保存在数据库中。
但是,当我显示预先填写的表单时,我的 editAction()
出现了问题。 我的收藏品阵列中只有最后添加的产品名称...
我不明白我做错了什么。名称在我的数据库中,也是产品,为什么我只得到ArrayCollection中的姓氏?
实体 Product.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Table(name="modele")
* @ORM\Entity(repositoryClass="ProductRepository")
* @UniqueEntity(fields="code", message="Product code already exists")
*/
class Product
{
/**
* @ORM\Column(name="Modele_Code", type="string", length=15)
* @ORM\Id
* @Assert\NotBlank()
* @Assert\Length(max=15, maxMessage="The code cannot be longer than {{ limit }} characters")
*/
private $code;
/**
* @ORM\OneToMany(targetEntity="ProductNames", mappedBy="product", cascade={"persist", "remove"})
*/
private $names;
/**
* Constructor
*/
public function __construct()
{
$this->names = new ArrayCollection();
}
/**
* Set code
*
* @param string $code
*
* @return Product
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* @return string
*/
public function getCode()
{
return $this->code;
}
/**
* Get names
*
* @return ArrayCollection
*/
public function getNames()
{
return $this->names;
}
/**
* Add names
*
* @param ProductNames $names
*
* @return Product
*/
public function addName(ProductNames $names)
{
$names->setCode($this->getCode());
$names->setProduct($this);
if (!$this->getNames()->contains($names)) {
$this->names->add($names);
}
return $this;
}
/**
* Remove names
*
* @param ProductNames $names
*/
public function removeName(ProductNames $names)
{
$this->names->removeElement($names);
}
}
实体 ProductNames.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* @ORM\Table(name="modele_lib")
* @ORM\Entity(repositoryClass="ModelTextsRepository")
* @UniqueEntity(fields={"code","language"}, message="A name in this language already exists for this product")
*/
class ProductNames
{
/**
* @ORM\Column(name="Modele_Code", type="string", length=15)
* @ORM\Id
*/
private $code;
/**
* @ORM\ManyToOne(targetEntity="Product", inversedBy="names")
* @ORM\JoinColumn(name="Modele_Code", referencedColumnName="Modele_Code")
*/
private $product;
/**
* @ORM\Column(name="Langue_Code", type="string", length=2)
*/
private $language;
/**
* @ORM\Column(name="Modele_Libelle", type="string", length=50)
* @Assert\NotBlank()
*/
private $name;
/**
* Set code
*
* @param string $code
*
* @return ProductNames
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* Get code
*
* @return string
*/
public function getCode()
{
return $this->code;
}
/**
* Set product
*
* @param Product $product
*
* @return ProductNames
*/
public function setProduct(Model $product)
{
$this->product = $product;
return $this;
}
/**
* Get product
*
* @return Product
*/
public function getProduct()
{
return $this->product;
}
/**
* Set language
*
* @param string $language
*
* @return ProductNames
*/
public function setLanguage($language)
{
$this->language = $language;
return $this;
}
/**
* Get language
*
* @return string
*/
public function getLanguage()
{
return $this->language;
}
/**
* Set name
*
* @param string $name
*
* @return ProductNames
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
}
表格 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\TextType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
// use Doctrine\ORM\EntityRepository;
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$recordId = $options['data']->getCode(); // product code
// default options for names
$namesOptions = array(
'entry_type' => ProductNamesType::class,
'entry_options' => array('languages' => $options['languages']),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'label' => false,
'by_reference' => false
);
// case edit product
if (!empty($recordId)) {
$namesOptions['entry_options']['edit'] = true;
}
$builder
->add('code', TextType::class, array(
'attr' => array(
'size' => 15,
'maxlength' => 15,
'placeholder' => 'Ex : LBSKIN'
),
))
->add('names', CollectionType::class, $namesOptions)
->add('save', SubmitType::class, array(
'attr' => array('class' => 'button-link save'),
'label' => 'Validate'
)
);
// Edit case : add delete button
if (!empty($recordId)) {
$builder->add('delete', SubmitType::class, array(
'attr' => array('class' => 'button-link delete'),
'label' => 'Delete'
));
}
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Product',
'languages' => null
));
}
}
表格 ProductNamesType.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\ChoiceType;
use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Doctrine\ORM\EntityRepository;
class ProductNamesType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Language codes list
$choices = array();
foreach ($options['languages'] as $lang) {
$code = $lang->getCode();
$choices[$code] = $code;
}
$builder
->add('name', TextType::class)
->add('language', ChoiceType::class, array(
'label' => 'Language',
'placeholder' => '',
'choices' => $choices
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\ProductNames',
'languages' => null,
'edit' => false
));
}
}
ProductController.php (请参阅editAction
查找我的问题)。
如果我在表单提交后在$form->getData()
中打印$product->getNames()
或addAction()
,我会收到所有数据,一切都对我好。
namespace AppBundle\Controller;
use AppBundle\Form\ProductType;
use AppBundle\Entity\Product;
use AppBundle\Entity\ProductNames;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Doctrine\Common\Collections\ArrayCollection;
class ProductController extends Controller
{
/**
* @Route("/products/add", name="product_add")
*/
public function addAction(Request $request) {
// build the form
$em = $this->getDoctrine()->getManager();
$languages = $em->getRepository('AppBundle:Language')->findAllOrderedByCode();
$product = new Product();
$form = $this->createForm(ProductType::class, $product, array(
'languages' => $languages
));
// handle the submit
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// save the product
$em->persist($product);
foreach($product->getNames() as $names){
$em->persist($names);
}
$em->flush();
/*** here, everything is working ***/
// success message
$this->addFlash('notice', 'Product has been created successfully !');
// redirection
return $this->redirectToRoute('product');
}
// show form
return $this->render('products/form.html.twig', array(
'form' => $form->createView()
));
}
/**
* @Route("/products/edit/{code}", name="product_edit")
*/
public function editAction($code, Request $request) {
// get product from database
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AppBundle:Product')->find($code);
$languages = $em->getRepository('AppBundle:Language')->findAllOrderedByCode();
// product doesn't exist
if (!$product) {
throw $this->createNotFoundException('No product found for code '. $code);
}
$originalNames = new ArrayCollection();
/*** My PROBLEM IS HERE ***/
// $product->getNames() returns only one name : the last added
foreach ($product->getNames() as $names) {
$originalNames->add($names);
}
// My form shows only one "name block" with the last name added when the user created the product.
// build the form with product data
$form = $this->createForm(ProductType::class, $product, array(
'languages' => $languages
));
// form POST
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// ...
}
// show form
return $this->render('products/form.html.twig', array(
'form' => $form->createView(),
'product_code' => $code
));
}
}
答案 0 :(得分:0)
问题可能在于您的ProductNames实体。您已将code
标记为主键(使用@ORM\Id
),产品会将多个 ProductNames 标记为code
作为主键,因为主键需要是唯一的。我建议通过将@ORM\Id
注释添加到langauge来使用复合主键。
class ProductNames
{
/**
* @ORM\Column(name="Modele_Code", type="string", length=15)
* @ORM\Id
*/
private $code;
/**
* @ORM\Column(name="Langue_Code", type="string", length=2)
* @ORM\Id
*/
private $language;
// ...
}
您必须更新/重新创建数据库才能使复合键生效。
希望这有帮助。