Загрузка файла не создает UploadedFile
В настоящее время я работаю над проектом PHP v5.5.9, в котором я использую некоторые автономные компоненты (я не использую фреймворк с полным стеком), в настоящее время следующее:
psr/log 1.0.0 Common interface for logging libraries
sonata-project/intl-bundle 2.2.1 Symfony SonataIntlBundle
symfony/debug v2.4.2 Symfony Debug Component
symfony/dependency-injection v2.4.2 Symfony DependencyInjection Component
symfony/event-dispatcher v2.4.2 Symfony EventDispatcher Component
symfony/form v2.4.2 Symfony Form Component
symfony/http-foundation v2.4.2 Symfony HttpFoundation Component
symfony/http-kernel v2.4.2 Symfony HttpKernel Component
symfony/icu v1.2.0 Contains an excerpt of the ICU data and classes to load it.
symfony/intl v2.4.2 A PHP replacement layer for the C intl extension that includes additional data from the ICU library.
symfony/locale v2.4.2 Symfony Locale Component
symfony/options-resolver v2.4.2 Symfony OptionsResolver Component
symfony/property-access v2.4.2 Symfony PropertyAccess Component
symfony/security-core v2.4.2 Symfony Security Component - Core Library
symfony/security-csrf v2.4.2 Symfony Security Component - CSRF Library
symfony/templating v2.4.2 Symfony Templating Component
symfony/translation v2.4.2 Symfony Translation Component
symfony/twig-bridge v2.4.2 Symfony Twig Bridge
symfony/validator v2.4.2 Symfony Validator Component
twig/extensions v1.0.1 Common additional features for Twig that do not directly belong in core
twig/twig v1.15.1 Twig, the flexible, fast, and secure template language for PHP
У меня есть один класс Entity, один класс FormType и один контроллер, и я могу сохранить Entity с помощью шаблона "Twig". Таким образом, полный стек работает правильно.
Затем я добавил File
приписать мою сущность, чтобы добавить файл изображения, как описано в официальной документации] 1. Я также расширил свой FormType следующим кодом:
$builder->add('pictureFile', 'file', array('label' => 'Image File'));
После запуска обновленного кода (с выбранным файлом изображения) отображается следующая ошибка:
Catchable fatal error: Argument 1 passed to MyEntity::setPictureFile() must be an instance of Symfony\Component\HttpFoundation\File\File, array given, called in [.]\vendor\symfony\property-access\Symfony\Component\PropertyAccess\PropertyAccessor.php on line [.] and defined in MyEntity.php on line [.]
Таким образом, Symfony не создает автоматически UploadedFile
из формы, но связывает $_FILE
данные к array
, Я заметил, что другие люди также имеют эту проблему:
- как заставить объект запроса symfony2 передавать объект UploadedFile в класс сущности вместо строки
- Symfony2 загрузка нескольких файлов
На данный момент я использую следующий код в моем контроллере, но я хотел бы использовать функцию автоматического связывания (потому что в настоящее время я не могу проверить загруженный файл в сущности).
$formType = new MyType;
$entity = new MyEntity;
$form = $this->formFactory->create($formType, $entity);
$form->handleRequest();
if (true === $form->isSubmitted()) {
if (true === $form->isValid()) {
// TODO Replace ugly workaround to partially solve the problem of the file upload.
$request = Request::createFromGlobals();
$file = $request->files->get('myentity')['pictureFile'];
// Persist entity, upload file to server and redirect.
}
}
// Load View template.
Изменить (2014-03-03): Вот соответствующий код сущности:
<?php
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Mapping\ClassMetadata;
class MyEntity
{
private $id;
private $name;
private $picture;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
public function getPictureFile()
{
return $this->pictureFile;
}
public function setPictureFile(File $pictureFile = null)
{
$this->pictureFile = $pictureFile;
}
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint(
'name',
new Assert\NotBlank
);
$metadata->addPropertyConstraint(
'name',
new Assert\Type('string'));
$metadata->addPropertyConstraint(
'pictureFile',
new Assert\Image(
array(
'maxSize' => '2048k',
'minWidth' => 640,
'maxWidth' => 4096,
'minHeight' => 480,
'maxHeight' => 4096
)
)
);
}
}
И исходный код FormType:
<?php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class MyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', 'text')
->add(
'pictureFile',
'file',
array('label' => 'Image File')
)
->add('submit', 'submit', array('label' => 'Save'));
}
public function getName()
{
return 'myentity';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => __NAMESPACE__ . '\MyEntity'));
}
}
Любые предложения, как получить эту работу?
Изменить (2014-03-07): Упрощенный исходный код при подготовке к моему ответу.
2 ответа
Я нашел решение проблемы, описанной в вопросе. Поскольку это, похоже, проблема многих людей, я опишу свое решение в этом ответе.
Во время моего исследования я столкнулся с проблемой на GitHub, который содержит фиктивный DataTransformer.
Прочитав содержимое этой страницы, я решил попробовать его с помощью специального DataTransformer, который преобразует array
в случае UploadedFile
,
Вот пошаговое решение:
Создать DataTransformer с именем
UploadedFileTransformer
со следующим исходным кодом:use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Exception\TransformationFailedException; use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; use Symfony\Component\HttpFoundation\File\UploadedFile; class UploadedFileTransformer implements DataTransformerInterface { /** * {@inheritdoc} * * @param array $data The array to transform to an uploaded file. * * @return \Symfony\Component\HttpFoundation\File\UploadedFile|null The * uploaded file or `null` if no file has been uploaded. */ public function reverseTransform($data) { if (!$data) { return null; } $path = $data['tmp_name']; $pathinfo = pathinfo($path); $basename = $pathinfo['basename']; try { $uploadedFile = new UploadedFile( $path, $basename, $data['type'], $data['size'], $data['error'] ); } catch (FileNotFoundException $ex) { throw new TransformationFailedException($ex->getMessage()); } return $uploadedFile; } /** * {@inheritdoc} * * @param \Symfony\Component\HttpFoundation\File\UploadedFile|null $file The * uploaded file to transform to an `array`. * * @return \Symfony\Component\HttpFoundation\File\UploadedFile|null The * argument `$file`. */ public function transform($file) { return $file; } }
Обновите FormType, чтобы использовать
UploadedFileTransformer
дляfile
введите поле ввода (сравните с кодом в вопросе):use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class MyType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('name', 'text') ->add( $builder->create( 'pictureFile', 'file', array('label' => 'Image File') )->addModelTransformer(new \UploadedFileTransformer) ) ->add('submit', 'submit', array('label' => 'Save')); } public function getName() { return 'myentity'; } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array('data_class' => __NAMESPACE__ . '\MyEntity')); } }
Удалить дерьмо из контроллера, в моем примере доступ к
Request
Объект для получения загруженного файла (сравните с кодом в вопросе).$formType = new MyType; $entity = new MyEntity; $form = $this->formFactory->create($formType, $entity); $form->handleRequest(); if (true === $form->isSubmitted()) { if (true === $form->isValid()) { $file = $entity->getPictureFile(); // Persist entity, upload file to server and redirect. } } // Load View template.
И это все. Не нужно возиться с атрибутами в FormType, и проверка в Entity также работает.
Я действительно не знаю, почему это не стандартное поведение компонентов Symfony? Может быть, я пропустил что-то зарегистрировать в моем коде запуска?
Просто добавьте multipart к форме, и она должна работать
<form action="#" method="post" enctype="multipart/form-data">
</form>