Symfony3:ArrayCollection只添加了最后一个项



我有一个实体Product。我的产品可以有不同语言的多个名称。一个法语名字,一个英语名字,等等。我不想用自动翻译。

用户必须在产品表单中填写名称并选择相应的语言。多亏了"添加"按钮,他可以添加任意多的名字。

所有语言都由管理员用户创建(以另一种形式)。因此,语言也是一个实体,它有一个名称(例如:英语)和一个代码(例如:EN)。

因此,ProductType是我的主形式ProductNameType是我的"集合"形式

例如,当用户创建了一个有两个名称的新产品(一个是法语,另一个是英语)时,该产品会保存在我的数据库中,并且还会创建两个名称并保存在另一个表中。

在这一点上,一切都很顺利。我的addAction()很好,产品和相应的名称都保存在数据库中。

但是,当我显示预先填写的表格时,我的editAction()出现问题我的集合数组中只存在最后添加的产品名称

我不明白我做错了什么。这些名字在我的数据库中,产品也在,那么为什么我在ArrayCollection中只得到姓氏呢?

实体Product.php

namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
use DoctrineCommonCollectionsArrayCollection;
use SymfonyComponentValidatorConstraints as Assert;
use SymfonyBridgeDoctrineValidatorConstraintsUniqueEntity;
/**
 * @ORMTable(name="modele")
 * @ORMEntity(repositoryClass="ProductRepository")
 * @UniqueEntity(fields="code", message="Product code already exists")
 */
class Product
{
    /**
     * @ORMColumn(name="Modele_Code", type="string", length=15)
     * @ORMId
     * @AssertNotBlank()
     * @AssertLength(max=15, maxMessage="The code cannot be longer than {{ limit }} characters")
     */
    private $code;
    /**
     * @ORMOneToMany(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 AppBundleEntity;
use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;
use SymfonyBridgeDoctrineValidatorConstraintsUniqueEntity;
/**
 * @ORMTable(name="modele_lib")
 * @ORMEntity(repositoryClass="ModelTextsRepository")
 * @UniqueEntity(fields={"code","language"}, message="A name in this language already exists for this product")
 */
class ProductNames
{
    /**
     * @ORMColumn(name="Modele_Code", type="string", length=15)
     * @ORMId
     */
    private $code;
    /**
     * @ORMManyToOne(targetEntity="Product", inversedBy="names")
     * @ORMJoinColumn(name="Modele_Code", referencedColumnName="Modele_Code")
     */
    private $product;
    /**
     * @ORMColumn(name="Langue_Code", type="string", length=2)
     */
    private $language;
    /**
     * @ORMColumn(name="Modele_Libelle", type="string", length=50)
     * @AssertNotBlank()
     */
    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 AppBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyComponentFormExtensionCoreTypeTextType;
use SymfonyComponentFormExtensionCoreTypeCollectionType;
use SymfonyComponentFormExtensionCoreTypeSubmitType;
// use DoctrineORMEntityRepository;
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' => 'AppBundleEntityProduct',
            'languages'  => null
        ));
    }
}

表单产品名称Type.php

namespace AppBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
use SymfonyComponentFormExtensionCoreTypeTextType;
use SymfonyComponentFormExtensionCoreTypeChoiceType;
use IvoryCKEditorBundleFormTypeCKEditorType;
use DoctrineORMEntityRepository;
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' => 'AppBundleEntityProductNames',
            'languages'  => null,
            'edit'       => false
        ));
    }
}

ProductController.php(请参阅editAction查找我的问题)。

如果我在表单提交后在addAction()中打印$form->getData()$product->getNames(),我会得到我所有的数据,一切对我来说都很好。

namespace AppBundleController;
use AppBundleFormProductType;
use AppBundleEntityProduct;
use AppBundleEntityProductNames;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SymfonyBundleFrameworkBundleControllerController;
use SymfonyComponentHttpFoundationRequest;
use DoctrineCommonCollectionsArrayCollection;
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
      ));
   }
}

问题可能出在ProductNames实体上。您已将code标记为主键(使用@ORMId),一个产品将具有多个ProductNames,并且它们都不能将code作为主键,因为主键需要唯一。我建议通过在langauge中添加@ORMId注释来使用复合主键。

class ProductNames
{
    /**
     * @ORMColumn(name="Modele_Code", type="string", length=15)
     * @ORMId
     */
    private $code;
    /**
     * @ORMColumn(name="Langue_Code", type="string", length=2)
     * @ORMId
     */
    private $language;
    // ...
}

您必须更新/重新创建数据库,复合密钥才能生效。

希望这能有所帮助。

相关内容

最新更新