A. Martyn
A. Martyn

Reputation: 85

Using VichUploaderBundle to upload a file through an API

So, what I need is a basic file upload. What I have is Symfony 4 and VichUploaderBundle, integrated exactly as described https://symfony.com/doc/current/bundles/EasyAdminBundle/integration/vichuploaderbundle.html

Entity is marked as @Vich\Uploadable, fields are defined correcty:

/**
 * @Vich\UploadableField(mapping="images", fileNameProperty="fileName")
 * @var File
 */
private $file;

/**
 * @ORM\Column(type="string", length=255)
 */
private $fileName;

This is my Type:

class MyType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name', TextType::class)
            ->add('file', VichImageType::class, ['allow_file_upload' => true])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => My::class,
            'allow_extra_fields' => false,
            'http_method' => 'POST'
        ]);

        $resolver->setRequired('http_method');
    }
}

What I have in my controller:

$form = $this->createForm(MyType::class, null);
    $form->submit(array_merge($request->request->all(), $request->files->all()));

    if ($form->isSubmitted() && $form->isValid()) {
        /** @var $my */
        $my = $form->getData();
        $em = $this->getDoctrine()->getManager();
        $em->persist($my);
        $em->flush();

        return $this->json($my);
    }

The outcome of this is a TransformationException:

Compound forms expect an array or NULL on submission.

I've debugged the whole thing with xDebug - it successfully submits name field, and even file field, for the first time. But then, for some reason, it thinks it's a compound form and tries to submit it one more time - which obviously leads to a transformation exception https://github.com/symfony/form/blob/master/Form.php#L571

And, when I try to set 'compound' => false, it doesn't submit the file field at all.

What might be the problem here? I've seen in the docs, that for using it with EasyAdmin it's enough to just specify the field name and type 'vich_image', why it doesn't work outside the EasyAdmin? You could notice, by the way, that I've added extra parameter 'allow_file_upload' => true as it wasn't submitting without it https://github.com/symfony/form/blob/master/Form.php#L534

Upvotes: 1

Views: 1228

Answers (2)

Jej
Jej

Reputation: 1

I try this solution and it worked :

/**
 *
 * @Route("/create", name="image_create", methods={"POST"})
 * @throws JsonException
 * @throws \Doctrine\DBAL\Exception
 * @throws Exception
 */
public function create(Request $request)
{
    //Call api in form-data with key image which have image file
    $file = $request->files->all()["image"];

    $image = new Image();
    $image->setName(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME))
        ->setMime($file->getClientMimeType())
        ->setCreatedAt(new \Datetime('now'))
        ->setTargetImageFile($file);
    $this->entityManager->persist($image);
    $this->entityManager->flush();

   //response whatever you want
}

Here my entity :

<?php

namespace App\Entity;

use App\Repository\ImageRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * @ORM\Entity(repositoryClass=ImageRepository::class)
 * @ORM\Table(name="img_image")
 * @Vich\Uploadable
 */
class Image
{
/**
 * @ORM\Id
 * @ORM\GeneratedValue
 * @ORM\Column(type="integer", name="img_image_id")
 */
private $id;

/**
 * @ORM\Column(type="string", length=255, name="img_image_name")
 */
private $name;

/**
 * @ORM\Column(type="string", length=100, name="img_image_mime")
 */
private $mime;

/**
 * @Vich\UploadableField(mapping="image", fileNameProperty="target_image")
 *
 * @var File
 */
private $target_image_file;

/**
 * @ORM\Column(type="string", length=255, name="img_target_image")
 *
 * @var string
 */
private $target_image;

/**
 * @ORM\Column(type="datetime", name="img_image_created_at")
 */
private $created_at;

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

public function getName(): ?string
{
    return $this->name;
}

public function setName(string $name): self
{
    $this->name = $name;

    return $this;
}

public function getMime(): ?string
{
    return $this->mime;
}

public function setMime(string $mime): self
{
    $this->mime = $mime;

    return $this;
}

public function getCreatedAt(): ?\DateTimeInterface
{
    return $this->created_at;
}

public function setCreatedAt(\DateTimeInterface $created_at): self
{
    $this->created_at = $created_at;

    return $this;
}

/**
 * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
 * of 'UploadedFile' is injected into this setter to trigger the update. If this
 * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
 * must be able to accept an instance of 'File' as the bundle will inject one here
 * during Doctrine hydration.
 *
 * @param File|UploadedFile|null $target_image_file
 */
public function setTargetImageFile(?File $target_image_file = null): void
{
    $this->target_image_file = $target_image_file;

    if (null !== $target_image_file) {
        // It is required that at least one field changes if you are using doctrine
        // otherwise the event listeners won't be called and the file is lost
        $this->updatedAt = new \DateTimeImmutable();
    }
}

public function getTargetImageFile(): ?File
{
    return $this->target_image_file;
}

public function setTargetImage(?string $target_image): void
{
    $this->target_image = $target_image;
}

public function getTargetImage(): ?string
{
    return $this->target_image;
}

}

Upvotes: 0

A. Martyn
A. Martyn

Reputation: 85

So, a bit of further research has lead me to this answer https://github.com/dustin10/VichUploaderBundle/issues/769#issuecomment-477173161

And the solution is to submit the form like this:

$form->submit(array_merge(
    $request->request->all(),
    ['file' => $request->files->all()]
));

Upvotes: 0

Related Questions