2016-05-09 15 views
0

私はエンティティプロダクトを持っています。私の製品は、異なる言語で複数の名前を持つことができます。フランス語の名前、英語の名前など。私は自動翻訳を使いたくない。Symfony3:ArrayCollectionに最後の項目のみが追加されました

ユーザーは、製品フォームに名前を入力し、対応する言語を選択する必要があります。彼はAddボタンのおかげでたくさんの名前を追加できます。

すべての言語は管理者によって(別の形式で)作成されます。したがって、言語は名前(例:英語)とコード(例:EN)を持つエンティティでもあります。

だから、ProductTypeは私の主要フォームあり、そしては私"コレクション" フォームです。

ユーザーがたとえば2つの名前(フランス語と英語)で新しい製品を作成すると、製品がデータベースに保存され、2つの名前も作成されて別のテーブルに保存されます。

この時点で、すべて正常に動作します。私の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

ProductNamesエンティティに問題がある可能性があります。 codeをプライマリキー(@ORM\Id)とマークした場合、製品は複数のProductNamesとなり、プライマリキーは一意である必要があるため、プライマリキーとしてcodeを持つことはできません。私は、複合主キーを使用して、@ORM\Idアノテーションを追加することを提案します。

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; 

    // ... 
} 

複合キーを有効にするには、データベースを更新/再作成する必要があります。

これが役に立ちます。

+0

あなたはそうです!実際には、既存のデータベースを使用するSymfonyとのインタフェースを作成する必要があります。このデータベースでは、テーブル "modele_lib"(= ProductNames)には、製品コード+言語コードの2つの主キーがあります。 私は注釈でそれを書くのを忘れました、ありがとう、私はそれが私のArrayCollectionの問題を修正するかどうかをテストするつもりです。 – Felurian

+0

ありがとう、あなたの天才!今すぐ動作します;-) – Felurian

関連する問題