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 正在请求删除

这对我来说似乎是一个非常奇怪的行为;我没有删除该项目,我什至没有 orphanRemovalcascade{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 方法,而您不应该设置方法,而是 addremove

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 集合在提交表单之前没有预先填充现有集合