Symfony form: Uploaded file - “This value should be of type string”

折月煮酒 提交于 2019-12-23 09:30:02

问题


[UPDATED]: 2019/06/24 - 23;28

Uploading a file with a form, I encounter the following error:

This value should be of type string

The form builder is set to FileType as it should:

FormType

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->add('nom')
                ->add('description')
                ->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'=>null,
                    'required'=>true,
                ))
                ->add('isActif', null, array('required'=>false));
    }

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults([
            'data_class'=>Document::class,
            'data_route'=>null,
        ]);
    }
}

And my getter and setter have no type hint to make sure that UploadedFile::__toString() won't be invoked:

Entity

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->isActif=true;
    }

    public function __toString() {
        return $this->getNom();
    }

    public function getId(): ?int {
        return $this->id;
    }

    public function getNom(): ?string {
        return $this->nom;
    }

    public function setNom(string $nom): self {
        $this->nom=$nom;

        return $this;
    }

    public function getFichier()/*Removed type hint*/ {
        return $this->fichier;
    }

    public function setFichier(/*Removed type hint*/$fichier): self {
        $this->fichier=$fichier;

        return $this;
    }

    public function getIsActif(): ?bool {
        return $this->isActif;
    }

    public function setIsActif(bool $isActif): self {
        $this->isActif=$isActif;

        return $this;
    }

    public function getSalle(): ?Salle {
        return $this->salle;
    }

    public function setSalle(?Salle $salle): self {
        $this->salle=$salle;

        return $this;
    }

    public function getStand(): ?Stand {
        return $this->stand;
    }

    public function setStand(?Stand $stand): self {
        $this->stand=$stand;

        return $this;
    }
}

Yet, form validator is still expecting a string and not an UploadedFile object.

Controller

/**
 * @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->isGranted('ROLE_ORGANISATEUR')) {
        $route=$router->match($request->getPathInfo())['_route'];
        if(($route == 'hall_document_new' && !$salle) || ($route == 'stand_document_new' && !$stand)) {
            //ToDo [SP] set message
            return $this->json(array(
                'messageInfo'=>array(
                    array(
                        'message'=>'',
                        'type'=>'error',
                        'length'=>'',
                    )
                )
            ));
        }

        $document=new Document();
        if($route == 'hall_document_new') {
            $action=$this->generateUrl($route, array('id'=>$salle->getId()));
        } elseif($route == 'stand_document_new') {
            $action=$this->generateUrl($route, array('id'=>$stand->getId()));
        } else {
            $action=$this->generateUrl($route);
        }
        $form=$this->createForm(DocumentType::class, $document, array(
            'action'=>$action,
            'method'=>'POST',
            'data_route'=>$route,
        ));

        $form->handleRequest($request);
        if($form->isSubmitted()) {
            //Fail here, excepting a string value (shouldn't), got UploadedFile object
            if($form->isValid()) {
                if($route == 'hall_document_new') {
                    $document->setSalle($salle);
                } elseif($route == 'stand_document_new') {
                    $document->setStand($stand);
                } else {
                    $accueil=$salleRepository->findOneBy(array('isAccueil'=>true));
                    if($accueil) {
                        $document->setSalle($accueil);
                    } else {
                        //ToDo [SP] set message
                        return $this->json(array(
                            'messageInfo'=>array(
                                array(
                                    'message'=>'',
                                    'type'=>'',
                                    'length'=>'',
                                )
                            )
                        ));
                    }
                }

                /** @noinspection PhpParamsInspection */
                $filename=$fileUploader->uploadDocument($document->getFichier());
                if($filename) {
                    $document->setFichier($filename);
                } else {
                    //ToDo [SP] set message
                    return $this->json(array(
                        'messageInfo'=>array(
                            array(
                                'message'=>'',
                                'type'=>'error',
                                'length'=>'',
                            )
                        )
                    ));
                }

                $entityManager=$this->getDoctrine()->getManager();
                $entityManager->persist($document);
                $entityManager->flush();

                return $this->json(array(
                    'modal'=>array(
                        'action'=>'unload',
                        'modal'=>'mdcDialog',
                        'content'=>null,
                    )
                ));
            } else {
                //ToDo [SP] Hide error message
                return $this->json($form->getErrors(true, true));
                // return $this->json(false);
            }
        }

        return $this->json(array(
            'modal'=>array(
                'action'=>'load',
                'modal'=>'mdcDialog',
                'content'=>$this->renderView('salon/dashboard/document/new.html.twig', array(
                    'salle'=>$salle,
                    'stand'=>$stand,
                    'document'=>$document,
                    'form'=>$form->createView(),
                )),
            )
        ));
    } else {
        return $this->json(false);
    }
}

services.yaml

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%'

回答1:


In config/packages/validator.yaml comment out these lines if they exist:

framework:
    validation:
        # Enables validator auto-mapping support.
        # For instance, basic validation constraints will be inferred from Doctrine's metadata.
        #auto_mapping:
        #  App\Entity\: []

See Symfony 4.3 issue [Validation] Activate auto-mapped validation via an annotation #32070.




回答2:


In your form builder, you set data_class to null:

->add('fichier', FileType::class, array(
    'data_class'=>null,
    'required'=>true,
))

But FileType actually expects some data class to be defined internally. It has some logic to define class dynamically: it is either Symfony\Component\HttpFoundation\File\File for single file upload or null for multiple files.

So, you're effectively force your file control to be multifile, but target field type is string. Symfony does some type-guessing and chooses controls accordingly (e.g. boolean entity field will be represented by checkbox) -- if you don't specify explicit control type and options.

So, I think you should remove data_class from your options, and that will solve the problem.

Here is a link to specific place to make it behave like I have described: https://github.com/symfony/form/blob/master/Extension/Core/Type/FileType.php#L114

As you can see, it decides on data_class value and some other values, then does setDefaults(), i.e. these correct values are there -- unless you override them. A bit fragile architecture, I'd say, but that's what we have to work with.



来源:https://stackoverflow.com/questions/56690952/symfony-form-uploaded-file-this-value-should-be-of-type-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!