28841


Symfony2.8.10 と Doctrine を使って、コレクションを永続化することについて質問があります。  
以下の２つのクラス Article を ArticleAttribute を書きました。

[code]

    /**
     * Article
     *
     * @ORM\Table(name="article")
     * @ORM\Entity(repositoryClass="AppBundle\Repository\ArticleRepository")
     * @ORM\HasLifecycleCallbacks
     * */
    class Article
    {
        /** @ORM\Id @ORM\Column(type="integer") */
        private $id;
        /** @ORM\Column(type="string") */
        private $title;
    
        /**
         * @var ArticleAttribute[]
         * @ORM\OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"})
         */
        private $attributes;
    
        /**
         * Constructor
         */
        public function __construct()
        {
            $this->attributes = new ArrayCollection();
        }
    
        /**
         * Get attributes
         *
         * @return ArticleAttribute[]
         */
        public function getAttributes()
        {
            return $this->attributes;
        }
    
        /**
         * @param $item ArticleAttribute
         */
        public function addAttribute($item)
        {
            $this->attributes[$item->getAttribute()] = $item;
            $item->setArticle($this);
        }
    
        /**
         * Remove attributes
         *
         * @param ArticleAttribute $attribute
         */
        public function removeAttribute(ArticleAttribute $attribute)
        {
            $this->attributes->removeElement($attribute);
        }
    ...
    
    /**
     * @ORM\Table(name="article_attribute")
     * @ORM\Entity
     */
    class ArticleAttribute
    {
        /**
         * @var Article
         * @ORM\Id
         * @ORM\ManyToOne(targetEntity="Article", inversedBy="attributes")
         * @ORM\JoinColumn(name="article_id", referencedColumnName="id")
         */
        private $article;
    
        /**
         * @ORM\Id
         * @ORM\Column(type="string")
         */
        private $attribute;
    
        /**
         * Get article
         *
         * @return Article
         */
        public function getArticle()
        {
            return $this->article;
        }
    
        /**
         * Set article
         *
         * @param Article $article
         * @return ArticleAttribute
         */
        public function setArticle(Article $article)
        {
            $this->article = $article;
            return $this;
        }
    ....
    
[/code]

これらのクラスをフォームにバインドしました。

[code]

    class ArticleType extends AbstractType
    {
        /**
         * @param FormBuilderInterface $builder
         * @param array $options
         */
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('id')
                ->add('title')
                ->add('attributes', CollectionType::class, [
                    'entry_type' => ArticleAttributeType::class,
                    'allow_add' => true,
                    'allow_delete' => true,
                ]);
        }
    
[/code]

上記コードはフォームタイプの一部です。

  
いくつかの ArticleAttributes をページ上で削除します。  
サブミットします。  
handleRequest と $em->flush(); を呼び出します。  
しかし、削除した項目はデータベースに残ったままです。  
  
私は回避方法を見つけました。  
FormResizeListener クラスの onSubmit メソッドを変更しました。

[code]

        public function onSubmit(FormEvent $event)
        {
            /** @var FormBuilder $form */
            $form = $event->getForm();
            /** @var PersistentCollection $data */
            $data = $event->getData();
    
            // At this point, $data is an array or an array-like object that already contains the
            // new entries, which were added by the data mapper. The data mapper ignores existing
            // entries, so we need to manually unset removed entries in the collection.
    
            if (null === $data) {
                $data = [];
            }
    
            if (!is_array($data) && !($data instanceof \Traversable && $data instanceof \ArrayAccess)) {
                throw new UnexpectedTypeException($data, 'array or (\Traversable and \ArrayAccess)');
            }
    
            if ($this->deleteEmpty) {
                $previousData = $event->getForm()->getData();
                foreach ($form as $name => $child) {
                    $isNew = !isset($previousData[$name]);
    
                    // $isNew can only be true if allowAdd is true, so we don't
                    // need to check allowAdd again
                    if ($child->isEmpty() && ($isNew || $this->allowDelete)) {
                        unset($data[$name]);
                        $form->remove($name);
                    }
                }
            }
    
            // The data mapper only adds, but does not remove items, so do this
            // here
            if ($this->allowDelete) {
                $arr = (array)$data; //added
                /** @var EntityManager $em */ //added
                $em = @$arr["\0Doctrine\\ORM\\PersistentCollection\0em"]; //added
    
                $toDelete = [];
    
                foreach ($data as $name => $child) {
                    if (!$form->has($name)) {
                        $toDelete[] = $name;
                    }
                }
    
                foreach ($toDelete as $name) {
                    if ($em) $em->remove($data[$name]); //added
                    unset($data[$name]);
                }
            }
    
            $event->setData($data);
        }
    
[/code]

変更後は、削除した項目がデータベースから正しく削除されました。  
しかし、これは汚いと考えます。  
この問題を回避する正しい方法をお教えください。  
あなたの助けを大いに評価します。


CC BY-SA 3.0
2016-09-12T02:27:40.547
28828
2016-09-12T13:26:57.587
2016-09-12T13:20:47.753
18755
18755
question
php symfony2 doctrine2
Cannot remove on collection item (using CollectionType, OneToMany bidirectional)


英語サイトでいただいた回答は下記のようなものでした。

Article と ArticleAttributes のリレーションに orphanRemoval オプションを加えてみなさい。

[code]

    /**
     * @var ArticleAttribute[]
     * @ORM\OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, orphanRemoval=true)
    */
    private $attributes;
    
[/code]

また、[この記事](https://labs.madisoft.it/orphan-removal-forms-collection-and-
collections-elements-swapping-pick-your-poison/)を参照することを提案します。


CC BY-SA 3.0
2016-09-12T13:26:57.587
28841
2016-09-12T13:26:57.587
18755
28828
answer