Symfony Form + File Collection + oTm-mTo = 保存的文件总是在所有者更新时删除
Symfony Form + File Collection + oTm-mTo = saved files always deleted on owner update
我有一个实体 Item
和一个实体 File
当我用 files
保存我的 item
表格时,一切正常,但后来,当我尝试向我的 Item
添加一个新的 File
时,所有相关的文件从我的 table 中删除,我只得到新添加的项目。
删除发生在 $form->handleRequest($request);
我之前和之后尝试转储数据,可以确认 100% 删除发生在那里。
然后我的 $itemEntity
会预先填充现有文件,但会在 $form
中的 handleRequest
之后消失
我也在使用 add
方法而不是 set
并且在我的实体中有一个 arrayCollection
我查看了探查器,可以看到一个原则SQL 正在请求删除
这对我来说似乎是一个非常奇怪的行为;我没有删除该项目,我什至没有 orphanRemoval
或 cascade{delete}
。
最后我不得不在数据库中手动重新创建旧文件的记录,但这似乎不合适,我确定我遗漏了一些东西。
百万美元的问题?
控制器
$itemEntity = $em->getRepository(Item::class)->findOneBy([
'uid' => $uid,
]);
// create form
$form = $this->createForm(NewItemType::class, $itemEntity);
// if I dump Item Entity here I can see my old files
$form->handleRequest($request);
// if I dump Item Entity here they are gone and replaced by the new ones
// .... later but never reached
$item->addFile($fileInstance);
项目实体:
/**
* @ORM\OneToMany(targetEntity="App\Entity\File", mappedBy="item", cascade={"persist"})
*/
private $file;
public function __construct()
{
$this->file = new ArrayCollection();
}
文件实体:
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $path;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Item", inversedBy="file")
* @ORM\JoinColumn(nullable=false)
*/
private $item;
/**
* @ORM\Column(type="string", length=255)
*/
private $uid;
/**
* @ORM\Column(type="integer")
*/
private $type;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $description;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $position;
/**
* @var boolean
* @ORM\Column(type="boolean", nullable=true)
*/
private $main;
/**
* @var UploadedFile|null
*/
protected $file;
public function getFile(): ?UploadedFile
{
return $this->file;
}
public function setFile(UploadedFile $file): void
{
$this->file = $file;
}
public function getId(): ?int
{
return $this->id;
}
public function getPath(): ?string
{
return $this->path;
}
public function setPath(string $path): self
{
$this->path = $path;
return $this;
}
public function getUid(): ?string
{
return $this->uid;
}
public function setUid(string $uid): self
{
$this->uid = $uid;
return $this;
}
public function getItem(): ?Item
{
return $this->item;
}
public function setBike(?Item $item): self
{
$this->item = $item;
return $this;
}
public function getType(): ?int
{
return $this->type;
}
public function setType(int $type): self
{
$this->type = $type;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getPosition(): ?int
{
return $this->position;
}
public function setPosition(?int $position): self
{
$this->position = $position;
return $this;
}
public function getMain(): ?bool
{
return $this->main;
}
public function setMain(?bool $main): self
{
$this->main = $main;
return $this;
}
表单集合类型
->add('item', CollectionType::class, array(
'label' => false,
'entry_type' => ItemFileType::class,
'error_bubbling' => false,
'entry_options' => [ 'required' => false, 'error_bubbling' => true ],
'allow_add' => true,
'allow_delete' => true
))
项目文件类型
->add('file', FileType::class, [
'label' => false,
'required' => false
])
->add('description')
->add('main', CheckboxType::class,[
'label' => 'Make this one the main picture',
'required' => false,
]);
这是 ArrayCollection
的常见情况。首先,您在 Item
实体
中有 $file
变量
/**
* @ORM\OneToMany(targetEntity="App\Entity\File", mappedBy="item", cascade={"persist"})
*/
private $file;
事实上,最好将此变量称为 $files
,只要可以将几个或多个文件附加到单个项目即可。
并且您的代码中还应该包含以下行
public function __construct() {
$this->files = new ArrayCollection();
}
这里的问题是,当你调用 handleRequest
时,它会完全重写你的
$files
属性。正如我所想,发生这种情况是因为您的 Item
实体中只有 set
方法,而您不应该设置方法,而是 add
和 remove
public function addFile(File $file)
{
if (!$this->files->contains($file)) {
$file->setItem($this);
$this->files->add($file);
}
return $this;
}
public function removeFile(File $file)
{
$this->files->removeElement($file);
return $this;
}
重点不是完全重写ArrayCollection
,而是扩展它。如果您从集合中删除某些项目,Doctrine 会将其从数据库中删除。
仅供参考 这也可能发生,因为 $files
集合在提交表单之前没有预先填充现有集合
我有一个实体 Item
和一个实体 File
当我用 files
保存我的 item
表格时,一切正常,但后来,当我尝试向我的 Item
添加一个新的 File
时,所有相关的文件从我的 table 中删除,我只得到新添加的项目。
删除发生在 $form->handleRequest($request);
我之前和之后尝试转储数据,可以确认 100% 删除发生在那里。
然后我的 $itemEntity
会预先填充现有文件,但会在 $form
handleRequest
之后消失
我也在使用 add
方法而不是 set
并且在我的实体中有一个 arrayCollection
我查看了探查器,可以看到一个原则SQL 正在请求删除
这对我来说似乎是一个非常奇怪的行为;我没有删除该项目,我什至没有 orphanRemoval
或 cascade{delete}
。
最后我不得不在数据库中手动重新创建旧文件的记录,但这似乎不合适,我确定我遗漏了一些东西。
百万美元的问题?
控制器
$itemEntity = $em->getRepository(Item::class)->findOneBy([
'uid' => $uid,
]);
// create form
$form = $this->createForm(NewItemType::class, $itemEntity);
// if I dump Item Entity here I can see my old files
$form->handleRequest($request);
// if I dump Item Entity here they are gone and replaced by the new ones
// .... later but never reached
$item->addFile($fileInstance);
项目实体:
/**
* @ORM\OneToMany(targetEntity="App\Entity\File", mappedBy="item", cascade={"persist"})
*/
private $file;
public function __construct()
{
$this->file = new ArrayCollection();
}
文件实体:
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $path;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Item", inversedBy="file")
* @ORM\JoinColumn(nullable=false)
*/
private $item;
/**
* @ORM\Column(type="string", length=255)
*/
private $uid;
/**
* @ORM\Column(type="integer")
*/
private $type;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $description;
/**
* @ORM\Column(type="integer", nullable=true)
*/
private $position;
/**
* @var boolean
* @ORM\Column(type="boolean", nullable=true)
*/
private $main;
/**
* @var UploadedFile|null
*/
protected $file;
public function getFile(): ?UploadedFile
{
return $this->file;
}
public function setFile(UploadedFile $file): void
{
$this->file = $file;
}
public function getId(): ?int
{
return $this->id;
}
public function getPath(): ?string
{
return $this->path;
}
public function setPath(string $path): self
{
$this->path = $path;
return $this;
}
public function getUid(): ?string
{
return $this->uid;
}
public function setUid(string $uid): self
{
$this->uid = $uid;
return $this;
}
public function getItem(): ?Item
{
return $this->item;
}
public function setBike(?Item $item): self
{
$this->item = $item;
return $this;
}
public function getType(): ?int
{
return $this->type;
}
public function setType(int $type): self
{
$this->type = $type;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
public function getPosition(): ?int
{
return $this->position;
}
public function setPosition(?int $position): self
{
$this->position = $position;
return $this;
}
public function getMain(): ?bool
{
return $this->main;
}
public function setMain(?bool $main): self
{
$this->main = $main;
return $this;
}
表单集合类型
->add('item', CollectionType::class, array(
'label' => false,
'entry_type' => ItemFileType::class,
'error_bubbling' => false,
'entry_options' => [ 'required' => false, 'error_bubbling' => true ],
'allow_add' => true,
'allow_delete' => true
))
项目文件类型
->add('file', FileType::class, [
'label' => false,
'required' => false
])
->add('description')
->add('main', CheckboxType::class,[
'label' => 'Make this one the main picture',
'required' => false,
]);
这是 ArrayCollection
的常见情况。首先,您在 Item
实体
$file
变量
/**
* @ORM\OneToMany(targetEntity="App\Entity\File", mappedBy="item", cascade={"persist"})
*/
private $file;
事实上,最好将此变量称为 $files
,只要可以将几个或多个文件附加到单个项目即可。
并且您的代码中还应该包含以下行
public function __construct() {
$this->files = new ArrayCollection();
}
这里的问题是,当你调用 handleRequest
时,它会完全重写你的
$files
属性。正如我所想,发生这种情况是因为您的 Item
实体中只有 set
方法,而您不应该设置方法,而是 add
和 remove
public function addFile(File $file)
{
if (!$this->files->contains($file)) {
$file->setItem($this);
$this->files->add($file);
}
return $this;
}
public function removeFile(File $file)
{
$this->files->removeElement($file);
return $this;
}
重点不是完全重写ArrayCollection
,而是扩展它。如果您从集合中删除某些项目,Doctrine 会将其从数据库中删除。
仅供参考 这也可能发生,因为 $files
集合在提交表单之前没有预先填充现有集合