I've found a solution for the problem described in the question. Since this seems to be a problem many people have, I describe my solution in this answer.
During my research I've come across a issue on GitHub, which contains a dummy DataTransformer.
After reading the content of that page, I came up with the idea to try it with a custom DataTransformer which converts the array
into an instance of UploadedFile
.
Here is the step-by-step solution:
Create a DataTransformer named
UploadedFileTransformer
with the following source code: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; } }
Update the FormType to use the
UploadedFileTransformer
for afile
type input field (compare with the code in the question):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')); } }
Remove crap from the Controller, in my example the access to the
Request
object to obtain the uploaded file (compare with the code in the question).$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.
And that's it. No fiddling with attributes in the FormType and the validation in the Entity is also working.
I really don't know, why this isn't the default behavior of the Symfony components? Maybe I missed to register something in my startup code?