[Form] Improved FileField to store files in a temporary location in case validation fails
This commit is contained in:
parent
f2f0d044c3
commit
e0aa3f30a8
@ -1,5 +1,8 @@
|
||||
<input type="file"
|
||||
id="<?php echo $field->getId() ?>"
|
||||
name="<?php echo $field->getName() ?>"
|
||||
<?php if ($field->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
/>
|
||||
id="<?php echo $field['file']->getId() ?>"
|
||||
name="<?php echo $field['file']->getName() ?>"
|
||||
<?php if ($field['file']->isDisabled()): ?>disabled="disabled"<?php endif ?>
|
||||
/>
|
||||
|
||||
<?php echo $view['form']->render($field['token']) ?>
|
||||
<?php echo $view['form']->render($field['original_name']) ?>
|
@ -135,5 +135,9 @@
|
||||
{% endblock percent_field %}
|
||||
|
||||
{% block file_field %}
|
||||
{% set group = field %}
|
||||
{% set field = group.file %}
|
||||
<input type="file" {% display field_attributes %} />
|
||||
{{ group.token|render }}
|
||||
{{ group.original_name|render }}
|
||||
{% endblock file_field %}
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Symfony\Component\Form;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony framework.
|
||||
*
|
||||
@ -14,8 +16,103 @@ namespace Symfony\Component\Form;
|
||||
/**
|
||||
* A file field to upload files.
|
||||
*/
|
||||
class FileField extends Field
|
||||
class FileField extends FieldGroup
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addRequiredOption('secret');
|
||||
$this->addOption('tmp_dir', sys_get_temp_dir());
|
||||
|
||||
parent::configure();
|
||||
|
||||
$this->add(new Field('file'));
|
||||
$this->add(new HiddenField('token'));
|
||||
$this->add(new HiddenField('original_name'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the file to a temporary location to prevent its deletion when
|
||||
* the PHP process dies
|
||||
*
|
||||
* This way the file can survive if the form does not validate and is
|
||||
* resubmitted.
|
||||
*
|
||||
* @see Symfony\Component\Form\FieldGroup::preprocessData()
|
||||
*/
|
||||
protected function preprocessData(array $data)
|
||||
{
|
||||
if ($data['file']) {
|
||||
$data['file']->move($this->getTmpPath($data['token']));
|
||||
$data['original_name'] = $data['file']->getOriginalName();
|
||||
$data['file'] = '';
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a file path into an array of field values
|
||||
*
|
||||
* @see Symfony\Component\Form\Field::normalize()
|
||||
*/
|
||||
protected function normalize($path)
|
||||
{
|
||||
srand(microtime(true));
|
||||
|
||||
return array(
|
||||
'file' => '',
|
||||
'token' => rand(100000, 999999),
|
||||
'original_name' => '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns an array of field values into a file path
|
||||
*
|
||||
* @see Symfony\Component\Form\Field::denormalize()
|
||||
*/
|
||||
protected function denormalize($data)
|
||||
{
|
||||
$path = $this->getTmpPath($data['token']);
|
||||
|
||||
return file_exists($path) ? $path : $this->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute temporary file path for the given token
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
protected function getTmpPath($token)
|
||||
{
|
||||
return realpath($this->getOption('tmp_dir')) . '/' . $this->getTmpName($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the temporary file name for the given token
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
protected function getTmpName($token)
|
||||
{
|
||||
return md5(session_id() . $this->getOption('secret') . $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original name of the uploaded file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getOriginalName()
|
||||
{
|
||||
$data = $this->getNormalizedData();
|
||||
|
||||
return $data['original_name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
125
tests/Symfony/Tests/Component/Form/FileFieldTest.php
Normal file
125
tests/Symfony/Tests/Component/Form/FileFieldTest.php
Normal file
@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Form;
|
||||
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Symfony\Component\Form\FileField;
|
||||
|
||||
class FileFieldTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public static $tmpFiles = array();
|
||||
|
||||
protected static $tmpDir;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
self::$tmpDir = sys_get_temp_dir();
|
||||
|
||||
// we need a session ID
|
||||
@session_start();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
foreach (self::$tmpFiles as $key => $file) {
|
||||
@unlink($file);
|
||||
unset(self::$tmpFiles[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
public function createTmpFile($path)
|
||||
{
|
||||
self::$tmpFiles[] = $path;
|
||||
file_put_contents($path, 'foobar');
|
||||
}
|
||||
|
||||
public function testBindUploadsNewFiles()
|
||||
{
|
||||
$field = new FileField('file', array(
|
||||
'secret' => '$secret$',
|
||||
'tmp_dir' => self::$tmpDir,
|
||||
));
|
||||
|
||||
$tmpPath = self::$tmpDir . '/' . md5(session_id() . '$secret$' . '12345');
|
||||
$that = $this;
|
||||
|
||||
$file = $this->getMock('Symfony\Component\HttpFoundation\File\UploadedFile', array(), array(), '', false);
|
||||
$file->expects($this->once())
|
||||
->method('move')
|
||||
->with($this->equalTo($tmpPath))
|
||||
->will($this->returnCallback(function ($path) use ($that) {
|
||||
$that->createTmpFile($path);
|
||||
}));
|
||||
$file->expects($this->any())
|
||||
->method('getOriginalName')
|
||||
->will($this->returnValue('original_name.jpg'));
|
||||
|
||||
$field->bind(array(
|
||||
'file' => $file,
|
||||
'token' => '12345',
|
||||
'original_name' => '',
|
||||
));
|
||||
|
||||
$this->assertTrue(file_exists($tmpPath));
|
||||
$this->assertEquals(array(
|
||||
'file' => '',
|
||||
'token' => '12345',
|
||||
'original_name' => 'original_name.jpg',
|
||||
), $field->getDisplayedData());
|
||||
$this->assertEquals($tmpPath, $field->getData());
|
||||
}
|
||||
|
||||
public function testBindKeepsUploadedFilesOnErrors()
|
||||
{
|
||||
$field = new FileField('file', array(
|
||||
'secret' => '$secret$',
|
||||
'tmp_dir' => self::$tmpDir,
|
||||
));
|
||||
|
||||
$tmpPath = self::$tmpDir . '/' . md5(session_id() . '$secret$' . '12345');
|
||||
$this->createTmpFile($tmpPath);
|
||||
|
||||
$field->bind(array(
|
||||
'file' => '',
|
||||
'token' => '12345',
|
||||
'original_name' => 'original_name.jpg',
|
||||
));
|
||||
|
||||
$this->assertTrue(file_exists($tmpPath));
|
||||
$this->assertEquals(array(
|
||||
'file' => '',
|
||||
'token' => '12345',
|
||||
'original_name' => 'original_name.jpg',
|
||||
), $field->getDisplayedData());
|
||||
$this->assertEquals($tmpPath, $field->getData());
|
||||
}
|
||||
|
||||
public function testBindKeepsOldFileIfNotOverwritten()
|
||||
{
|
||||
$field = new FileField('file', array(
|
||||
'secret' => '$secret$',
|
||||
'tmp_dir' => self::$tmpDir,
|
||||
));
|
||||
|
||||
$oldPath = tempnam(sys_get_temp_dir(), 'FileFieldTest');
|
||||
$this->createTmpFile($oldPath);
|
||||
|
||||
$field->setData($oldPath);
|
||||
|
||||
$this->assertEquals($oldPath, $field->getData());
|
||||
|
||||
$field->bind(array(
|
||||
'file' => '',
|
||||
'token' => '12345',
|
||||
'original_name' => '',
|
||||
));
|
||||
|
||||
$this->assertTrue(file_exists($oldPath));
|
||||
$this->assertEquals(array(
|
||||
'file' => '',
|
||||
'token' => '12345',
|
||||
'original_name' => '',
|
||||
), $field->getDisplayedData());
|
||||
$this->assertEquals($oldPath, $field->getData());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user