title: Symfony form: Uploaded file - "This value should be of type string"
body: <p><strong>[UPDATED]: 2019/06/24 - 23;28</strong></p>
  
  <p>Uploading a file with a form, I encounter the following error:</p>
  
  <blockquote>
    <p>This value should be of type string</p>
  </blockquote>
  
  <p>The form builder is set to <code>FileType</code> as it should:</p>
  
  <p><kbd><strong>FormType</strong></kbd></p>
  
  <pre><code>class DocumentType extends AbstractType {
      public function buildForm(FormBuilderInterface $builder, array $options) {
          /** @var Document $salle */
          $document=$options['data']; //Unused for now
          $dataRoute=$options['data_route']; //Unused for now
  
          $builder-&gt;add('nom')
                  -&gt;add('description')
                  -&gt;add('fichier', FileType::class, array(
                      //'data_class' is not the problem, tested without it.
                      //see comments if you don't know what it does.
                      'data_class'=&gt;null,
                      'required'=&gt;true,
                  ))
                  -&gt;add('isActif', null, array('required'=&gt;false));
      }
  
      public function configureOptions(OptionsResolver $resolver) {
          $resolver-&gt;setDefaults([
              'data_class'=&gt;Document::class,
              'data_route'=&gt;null,
          ]);
      }
  }
  </code></pre>
  
  <p>And my getter and setter have no type hint to make sure that <code>UploadedFile::__toString()</code> won't be invoked:</p>
  
  <p><kbd><strong>Entity</strong></kbd></p>
  
  <pre><code>class Document {
      /**
       * @ORM\Id()
       * @ORM\GeneratedValue()
       * @ORM\Column(type="integer")
       */
      private $id;
      /**
       * @ORM\Column(type="string", length=100)
       */
      private $nom;
      /**
       * @ORM\Column(type="string", length=40)
       */
      private $fichier;
      /**
       * @ORM\Column(type="boolean")
       */
      private $isActif;
      /**
       * @ORM\ManyToOne(targetEntity="App\Entity\Salle", inversedBy="documents")
       * @ORM\JoinColumn(onDelete="CASCADE")
       */
      private $salle;
      /**
       * @ORM\ManyToOne(targetEntity="App\Entity\Stand", inversedBy="documents")
       * @ORM\JoinColumn(onDelete="CASCADE")
       */
      private $stand;
  
      public function __construct() {
          $this-&gt;isActif=true;
      }
  
      public function __toString() {
          return $this-&gt;getNom();
      }
  
      public function getId(): ?int {
          return $this-&gt;id;
      }
  
      public function getNom(): ?string {
          return $this-&gt;nom;
      }
  
      public function setNom(string $nom): self {
          $this-&gt;nom=$nom;
  
          return $this;
      }
  
      public function getFichier()/*Removed type hint*/ {
          return $this-&gt;fichier;
      }
  
      public function setFichier(/*Removed type hint*/$fichier): self {
          $this-&gt;fichier=$fichier;
  
          return $this;
      }
  
      public function getIsActif(): ?bool {
          return $this-&gt;isActif;
      }
  
      public function setIsActif(bool $isActif): self {
          $this-&gt;isActif=$isActif;
  
          return $this;
      }
  
      public function getSalle(): ?Salle {
          return $this-&gt;salle;
      }
  
      public function setSalle(?Salle $salle): self {
          $this-&gt;salle=$salle;
  
          return $this;
      }
  
      public function getStand(): ?Stand {
          return $this-&gt;stand;
      }
  
      public function setStand(?Stand $stand): self {
          $this-&gt;stand=$stand;
  
          return $this;
      }
  }
  </code></pre>
  
  <p>Yet, form validator is still expecting a <code>string</code> and not an <code>UploadedFile</code> object.</p>
  
  <p><kbd><strong>Controller</strong></kbd></p>
  
  <pre><code>/**
   * @Route("/dashboard/documents/new", name="document_new", methods={"POST"})
   * @Route("/dashboard/hall-{id}/documents/new", name="hall_document_new", methods={"POST"})
   * @Route("/dashboard/stand-{id}/documents/new", name="stand_document_new", methods={"POST"})
   * @param Router $router
   * @param Request $request
   * @param FileUploader $fileUploader
   * @param SalleRepository $salleRepository
   * @param Salle|null $salle
   * @param Stand|null $stand
   * @return JsonResponse
   * @throws Exception
   */
  public function new(Router $router, Request $request, FileUploader $fileUploader, SalleRepository $salleRepository, Salle $salle=null, Stand $stand=null) {
      if($this-&gt;isGranted('ROLE_ORGANISATEUR')) {
          $route=$router-&gt;match($request-&gt;getPathInfo())['_route'];
          if(($route == 'hall_document_new' &amp;&amp; !$salle) || ($route == 'stand_document_new' &amp;&amp; !$stand)) {
              //ToDo [SP] set message
              return $this-&gt;json(array(
                  'messageInfo'=&gt;array(
                      array(
                          'message'=&gt;'',
                          'type'=&gt;'error',
                          'length'=&gt;'',
                      )
                  )
              ));
          }
  
          $document=new Document();
          if($route == 'hall_document_new') {
              $action=$this-&gt;generateUrl($route, array('id'=&gt;$salle-&gt;getId()));
          } elseif($route == 'stand_document_new') {
              $action=$this-&gt;generateUrl($route, array('id'=&gt;$stand-&gt;getId()));
          } else {
              $action=$this-&gt;generateUrl($route);
          }
          $form=$this-&gt;createForm(DocumentType::class, $document, array(
              'action'=&gt;$action,
              'method'=&gt;'POST',
              'data_route'=&gt;$route,
          ));
  
          $form-&gt;handleRequest($request);
          if($form-&gt;isSubmitted()) {
              //Fail here, excepting a string value (shouldn't), got UploadedFile object
              if($form-&gt;isValid()) {
                  if($route == 'hall_document_new') {
                      $document-&gt;setSalle($salle);
                  } elseif($route == 'stand_document_new') {
                      $document-&gt;setStand($stand);
                  } else {
                      $accueil=$salleRepository-&gt;findOneBy(array('isAccueil'=&gt;true));
                      if($accueil) {
                          $document-&gt;setSalle($accueil);
                      } else {
                          //ToDo [SP] set message
                          return $this-&gt;json(array(
                              'messageInfo'=&gt;array(
                                  array(
                                      'message'=&gt;'',
                                      'type'=&gt;'',
                                      'length'=&gt;'',
                                  )
                              )
                          ));
                      }
                  }
  
                  /** @noinspection PhpParamsInspection */
                  $filename=$fileUploader-&gt;uploadDocument($document-&gt;getFichier());
                  if($filename) {
                      $document-&gt;setFichier($filename);
                  } else {
                      //ToDo [SP] set message
                      return $this-&gt;json(array(
                          'messageInfo'=&gt;array(
                              array(
                                  'message'=&gt;'',
                                  'type'=&gt;'error',
                                  'length'=&gt;'',
                              )
                          )
                      ));
                  }
  
                  $entityManager=$this-&gt;getDoctrine()-&gt;getManager();
                  $entityManager-&gt;persist($document);
                  $entityManager-&gt;flush();
  
                  return $this-&gt;json(array(
                      'modal'=&gt;array(
                          'action'=&gt;'unload',
                          'modal'=&gt;'mdcDialog',
                          'content'=&gt;null,
                      )
                  ));
              } else {
                  //ToDo [SP] Hide error message
                  return $this-&gt;json($form-&gt;getErrors(true, true));
                  // return $this-&gt;json(false);
              }
          }
  
          return $this-&gt;json(array(
              'modal'=&gt;array(
                  'action'=&gt;'load',
                  'modal'=&gt;'mdcDialog',
                  'content'=&gt;$this-&gt;renderView('salon/dashboard/document/new.html.twig', array(
                      'salle'=&gt;$salle,
                      'stand'=&gt;$stand,
                      'document'=&gt;$document,
                      'form'=&gt;$form-&gt;createView(),
                  )),
              )
          ));
      } else {
          return $this-&gt;json(false);
      }
  }
  </code></pre>
  
  <p><kbd><strong>services.yaml</strong></kbd></p>
  
  <pre><code>parameters:
      locale: 'en'
      app_locales: en|fr
      ul_document_path: '%kernel.root_dir%/../public/upload/document/'
  
  services:
      _defaults:
          autowire: true
          autoconfigure: true
          bind:
              $locales: '%app_locales%'
              $defaultLocale: '%locale%'
              $router: '@router'
  
      App\:
          resource: '../src/*'
          exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
  
      App\Controller\:
          resource: '../src/Controller'
          tags: ['controller.service_arguments']
  
      App\Listener\kernelListener:
          tags:
              - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
              - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
              - { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
  
      App\Service\FileUploader:
          arguments:
              $ulDocumentPath: '%ul_document_path%'
  </code></pre>