【问题标题】:File Upload doesn't create UploadedFile文件上传不会创建 UploadedFile
【发布时间】:2014-03-02 15:40:29
【问题描述】:

我目前正在开发一个 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 类和一个 Controller,我能够持久化 Entity 通过“Twig”模板查看。所以整个堆栈工作正常。

然后我在我的Entity 中添加了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。我注意到,其他人也有这个问题:

目前我在 Controller 中使用以下代码,但我想使用自动绑定功能(因为目前我无法在 中验证上传的文件实体)。

$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):这是Entity的相关代码:

<?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): 为我的回答做准备的简化源代码。

【问题讨论】:

  • 您的表单是否具有正确的 enctype 属性?
  • 是的,表单生成如下:
  • 我做到了:请参见上文。

标签: php forms symfony symfony-2.4


【解决方案1】:

我找到了问题中描述的问题的解决方案。由于这似乎是许多人遇到的问题,因此我在此答案中描述了我的解决方案。

在我的研究过程中,我遇到了一个 issue on GitHub,其中包含一个虚拟 DataTransformer。

在阅读了该页面的内容后,我想到了尝试使用自定义 DataTransformer 将array 转换为UploadedFile 实例的想法。

这里是一步一步的解决方案:

  1. 使用以下源代码创建一个名为UploadedFileTransformer 的DataTransformer:

    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;
        }
    }
    
  2. 更新 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'));
        }
    }
    
  3. 从 Controller 中删除废话,在我的示例中访问 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 组件的默认行为?也许我错过了在我的启动代码中注册一些东西?

【讨论】:

    【解决方案2】:

    只需将多部分添加到表单中,它应该可以工作

    <form action="#" method="post" enctype="multipart/form-data">
    
    </form>
    

    【讨论】:

      猜你喜欢
      • 2022-09-28
      • 1970-01-01
      • 1970-01-01
      • 2015-01-11
      • 2017-11-25
      • 1970-01-01
      • 2015-10-30
      • 2015-11-11
      • 1970-01-01
      相关资源
      最近更新 更多