Sonata 3:删除 OneToMany 关系中的实体不适用于 'by_reference' => false
Posted
技术标签:
【中文标题】Sonata 3:删除 OneToMany 关系中的实体不适用于 \'by_reference\' => false【英文标题】:Sonata 3: Deleting entities in OneToMany relationship doesn't work with 'by_reference' => falseSonata 3:删除 OneToMany 关系中的实体不适用于 'by_reference' => false 【发布时间】:2018-02-01 09:36:24 【问题描述】:我一定错过了什么。我的相关实体是:
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
(它们是EducationalModule
的子代)旁边都有“删除”复选框。选中复选框并更新我的EducationalModule
后,EducationalUnit
被删除。
有趣的是,当我将我的EducationalModuleAdmin
'by_reference' => false
更改为true
然后删除作品(但还有另一个问题 - 创建的孩子没有参考父母)。
我尝试非常彻底地调试它,我可以确认实体在进入 Sonata 的 ModelManager
中的 ->persist()
和 ->flush()
之前确实被修改了,但由于某种原因,孩子数量的变化不会持续存在最后。
【问题讨论】:
【参考方案1】:尝试删除setUnits
方法。如果设置了by_reference => false
,则奏鸣曲应该找到add/remove
方法。目前只使用 setUnits
方法,并且在此方法中您只设置与条目的关系,而不是解除绑定不存在的条目。
您还应该将方法重命名为 addUnit
和 removeUnit
。
【讨论】:
删除setUnits
方法会导致 NoSuchPropertyException
在删除时显示消息:Could not determine access type for property "units".
。 :(
有什么办法可以“解除绑定”在父对象中删除的对象?
好的,感谢您的帮助,我找到了解决方案,这是不同但相似的东西,而且非常荒谬。 addUnits
和 removeUnits
方法应该单独命名。没错,所以它们应该是addUnit
和removeUnit
,因为Symfony 在它的自动魔法中实际上是在寻找我的复数属性的单数名称。我不确定我是更生气还是对此感到惊讶......以上是关于Sonata 3:删除 OneToMany 关系中的实体不适用于 'by_reference' => false的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Sonata Admin 中正确配置“sonata_type_collection”字段
Symfony 2.1 Sonata 管理包 OneToMany