奏鸣曲 3:删除 OneToMany 关系中的实体不适用于 'by_reference' => false

Sonata 3: Deleting entities in OneToMany relationship doesn't work with 'by_reference' => false

我一定是漏掉了什么。我的相关实体是:

EducationalModule

namespace AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Table(name="educational_module")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\CourseUnitRepository")
 *
 * Class EducationalUnit
 */
class EducationalModule
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\EducationalUnitCategory", inversedBy="educationalUnits")
     * @ORM\JoinColumn(name="category", referencedColumnName="course")
     */
    private $category;

    /**
     * @var string
     *
     * @ORM\Column(type="string", nullable=false)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Course", mappedBy="module")
     */
    private $courses;

    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\EducationalUnit", mappedBy="module", cascade={"persist","remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"position" = "ASC"})
     * @Assert\Count(min="1", minMessage="Module has to have at least one unit.")
     * @Assert\Valid()
     */
    private $units;


    public function __construct()
    {
        $this->units = new ArrayCollection();
    }

    /**
     * @return mixed
     */
    public function getCategory()
    {
        return $this->category;
    }

    /**
     * @param mixed $category
     */
    public function setCategory($category)
    {
        $this->category = $category;
    }

    /**
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     */
    public function getName() : ?string
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName(string $name)
    {
        $this->name = $name;
    }

    /**
     * @return mixed
     */
    public function getCourses()
    {
        return $this->courses;
    }

    /**
     * @return Collection
     */
    public function getUnits()
    {
        return $this->units;
    }


    public function setUnits(Collection $units)
    {
        $this->units = new ArrayCollection();

        foreach ($units as $unit) {
            $this->addUnits($unit);
        }

        return $this;
    }


    public function addUnits(EducationalUnit $unit)
    {
        $unit->setModule($this);
        $this->units->add($unit);
    }

    /**
     * @param $unit string
     *
     * @return $this
     */
    public function removeUnits($unit)
    {
        $this->units->removeElement($unit);

        return $this;
    }
}

EducationalUnit

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Table(name="educational_unit")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\CourseUnitRepository")
 *
 * Class EducationalUnit
 */
class EducationalUnit
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\EducationalModule", inversedBy="units")
     */
    private $module;

    /**
     * @ORM\Column(type="string", length=255, nullable=false)
     */
    private $name;

    /**
     * @ORM\Column(type="integer", options={"default" : 0})
     */
    private $position = 0;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\EducationalUnitCategory", inversedBy="educationalUnits")
     * @ORM\JoinColumn(name="category", referencedColumnName="course")
     */
    private $category;

    /**
     * @var EducationalFile
     *
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\EducationalFile", cascade={"persist","remove"}, orphanRemoval=true)
     * @Assert\NotBlank()
     * @Assert\Valid()
     */
    private $file;

    /**
     * @return mixed
     */
    public function getCategory()
    {
        return $this->category;
    }

    /**
     * @param mixed $category
     */
    public function setCategory($category)
    {
        $this->category = $category;
    }

    /**
     * @return mixed
     */
    public function getModule()
    {
        return $this->module;
    }


    public function setModule($module)
    {
        $this->module = $module;
    }

    /**
     * @return mixed
     */
    public function getPosition()
    {
        return $this->position;
    }

    /**
     * @param mixed $position
     */
    public function setPosition($position)
    {
        $this->position = $position;
    }

    /**
     * @return integer
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName(string $name)
    {
        $this->name = $name;
    }

    /**
     * @return EducationalFile
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * @param EducationalFile $file
     */
    public function setFile($file)
    {
        $this->file = $file;
    }
}

我的管理实体:

EducationalModuleAdmin

namespace AppBundle\Admin;

use AppBundle\Entity\EducationalFile;
use AppBundle\Entity\EducationalModule;
use AppBundle\Entity\EducationalUnit;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;

/**
 * Class EducationalModuleAdmin
 */
class EducationalModuleAdmin extends AbstractAdmin
{
    protected function configureFormFields(FormMapper $formMapper)
    {

        $formMapper->add('name', 'text');
        $formMapper->add('units', 'sonata_type_collection', [
            'required' => true,
            'by_reference' => false, // Has to be false. Thanks to that, our children entities will receive a reference to the parent.
            'btn_add' => 'Add unit',
        ], [
            'edit' => 'inline',
            'inline' => 'table',
            'sortable' => 'position',
            'multiple' => true,
        ]);

    }

    protected function configureDatagridFilters(DatagridMapper $datagridMapper)
    {
        $datagridMapper->add('id');
        $datagridMapper->add('name');
    }

    protected function configureListFields(ListMapper $listMapper)
    {
        $listMapper->add('id');
        $listMapper->add('name');
        $listMapper->add('_action', 'actions', [
            'actions' => [
                'show' => [],
                'edit' => [],
                'delete' => [],
            ],
        ]);
    }
}

EducationalUnitAdmin

namespace AppBundle\Admin;

use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;

/**
 * Class EducationalUnitAdmin
 */
class EducationalUnitAdmin extends AbstractAdmin
{
    protected $parentAssociationMapping = 'module';

    protected function configureFormFields(FormMapper $formMapper)
    {
        $is_creation = !$this->id($this->getSubject());

        $formMapper->add('module', 'sonata_type_model_hidden', [
            'attr' => ['hidden' => true],
        ]);

        $formMapper->add('id', 'integer', [
            'disabled' => true,
        ]);

        $formMapper->add('name', 'text');


        $formMapper->add('file', 'sonata_type_admin', [
            'required' => $is_creation,
            'by_reference' => true,
        ], [
            'edit' => 'inline',
            //'inline' => 'table',
        ]);

        $formMapper->add('position', 'hidden', [
            'attr' => ['hidden' => true],
        ]);
    }

    protected function configureDatagridFilters(DatagridMapper $datagridMapper)
    {
        $datagridMapper->add('id');
    }

    protected function configureListFields(ListMapper $listMapper)
    {
        $listMapper->add('id');
    }
}

我正在编辑我的 EducationalModule 实体,而且我在每个 EducationalUnit 旁边都有 "delete" 复选框(它们是 EducationalModule 的 children ).选择复选框并更新我的 EducationalModule 后,没有 EducationalUnit 被删除。

有趣的是,当我将 EducationalModuleAdmin 'by_reference' => false 更改为 true 然后删除作品时(但还有另一个问题 - 创建 children 不参考parent).

我尝试非常彻底地调试它,在进入 Sonata 的 ModelManager 中的 ->persist()->flush() 之前,我可以确认实体确实被修改了,但由于某种原因,改变中数children没有坚持到底

尝试删除 setUnits 方法。如果设置了 by_reference => false,奏鸣曲应该找到 add/remove 方法。现在只有 setUnits 方法在使用,在这个方法中你只设置与条目的关系,而不是解除绑定不存在的条目。

您还应该将方法重命名为 addUnitremoveUnit