无法删除收集项目。 (使用CollectionType,OneToMany双向)

时间:2016-09-12 05:48:18

标签: php symfony collections doctrine

我对Symfony2.8.10和Doctrine中的集合持久性有疑问。 我写了以下实体类,Article和ArticleAttribute。

/**
 * 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;
    }
....

绑定形成这些类。

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,
            ]);
    }

上面的代码是FormType的一部分。

在页面上删除多个ArticleAttributes。 提交表格。 调用handleRequest和$ em-> flush(); 但删除的项目仍保留在数据库中。

我找到了解决此问题的方法。 我在FormResizeListener类中修改了onSubmit方法。

    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);
    }

通过调用$ em->删除,将已删除项目的删除注册到工作单元。

修改后。已删除的项目正确地从数据库中删除 但我觉得这很讨厌。 请告诉我正确的方法来避免这个问题。 我很感激你的帮助。

1 个答案:

答案 0 :(得分:2)

尝试修改与Article添加ArticleAttributes选项

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

我还建议阅读this article