【发布时间】:2012-11-30 15:46:56
【问题描述】:
我目前在 SonataAdminBundle、一对多关系和文件上传方面面临挑战。我有一个名为Client 的实体和一个名为ExchangeFile 的实体。一个Client 可以有多个ExchangeFiles,所以我们这里是一对多的关系。我正在使用VichUploaderBundle 进行文件上传。
这是Client 类:
/**
* @ORM\Table(name="client")
* @ORM\Entity()
* @ORM\HasLifecycleCallbacks
*/
class Client extends BaseUser
{
// SNIP
/**
* @ORM\OneToMany(targetEntity="ExchangeFile", mappedBy="client", orphanRemoval=true, cascade={"persist", "remove"})
*/
protected $exchangeFiles;
// SNIP
}
这是ExchangeFile 类:
/**
* @ORM\Table(name="exchange_file")
* @ORM\Entity
* @Vich\Uploadable
*/
class ExchangeFile
{
// SNIP
/**
* @Assert\File(
* maxSize="20M"
* )
* @Vich\UploadableField(mapping="exchange_file", fileNameProperty="fileName")
*/
protected $file;
/**
* @ORM\Column(name="file_name", type="string", nullable=true)
*/
protected $fileName;
/**
* @ORM\ManyToOne(targetEntity="Client", inversedBy="exchangeFiles")
* @ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
protected $client;
// SNIP
}
在我的ClientAdmin 类中,我通过以下方式添加了exchangeFiles 字段:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
// SNIP
->with('Files')
->add('exchangeFiles', 'sonata_type_collection', array('by_reference' => false), array(
'edit' => 'inline',
'inline' => 'table',
))
// SNIP
}
这允许在客户端编辑表单中对各种交换文件进行内联编辑。到目前为止效果很好:。
问题
但是有一个注意事项:当我点击一次绿色的“+”号(添加一个新的交换文件表单行)时,然后在我的文件系统中选择一个文件,然后点击“+”号再次(通过 Ajax 追加一个新的表单行),选择另一个文件,然后点击“更新”(保存当前客户端),那么第一个文件不会持久化。在数据库和文件系统中只能找到第二个文件。
据我所知,这有以下原因:当第二次点击绿色的“+”号时,当前表单被发布到网络服务器,包括当前表单中的数据(客户端和所有交换文件)。创建了一个新表单并将请求绑定到表单中(这发生在位于Sonata\AdminBundle\Admin 的AdminHelper 类中):
public function appendFormFieldElement(AdminInterface $admin, $subject, $elementId)
{
// retrieve the subject
$formBuilder = $admin->getFormBuilder();
$form = $formBuilder->getForm();
$form->setData($subject);
$form->bind($admin->getRequest()); // <-- here
// SNIP
}
所以整个表单被绑定,一个表单行被附加,表单被发送回浏览器,整个表单被新的表单覆盖。但由于出于安全原因无法预先填充文件输入 (<input type="file" />),因此第一个文件会丢失。该文件仅在实体被持久化时存储在文件系统中(我认为VichUploaderBundle 使用 Doctrine 的 prePersist 来实现这一点),但是当附加表单字段行时,这还不会发生。
我的第一个问题是:我该如何解决这个问题,或者我应该往哪个方向发展?我希望以下用例能够工作:我想创建一个新客户端,并且我知道我将上传三个文件。我点击“新建客户端”,输入客户端数据,点击绿色“+”按钮一次,选择第一个文件。然后我再次点击“+”号,然后选择第二个文件。第三个文件也一样。这三个文件都应该被持久化。
第二个问题:当我只想在一对多关系中添加单个表单行时,为什么 Sonata 管理员会发布整个表单?这真的有必要吗?这意味着如果我有文件输入,则每次添加新的表单行时都会上传表单中存在的所有文件。
提前感谢您的帮助。如果您需要任何详细信息,请告诉我。
【问题讨论】:
-
鉴于您已经在使用 SonataAdmin,我建议您使用他们的MediaBundle。这不仅适用于图像 - 您可以将其配置为仅用于文件上传(如果您需要的话)。
-
感谢@RobMasters 的快速回复。我认为使用 SonataMediaBundle 会给我的应用程序增加很多不必要的复杂性。 MediaBundle 将通过在客户端和交换文件之间添加多对多关系来解决该问题。但这意味着 Exchange 文件可能没有所有者而存在。我正在开发一个允许安全交换具有关键内容的文件的应用程序,因此可能有没有关联客户端的文件可能会导致问题。
标签: php symfony symfony-sonata sonata-admin