4

I am using Vich Uploader Bundle in combination with API Platform to store files. I've followed the instructions from the official documentation https://api-platform.com/docs/core/file-upload/#handling-file-upload

Everything works well except I want to include PUT calls to replace existing MediaObject entities. My attempt was as follows:

I have created a custom operationand mapped it to the PUT method in the entity as follows:


<?php
// api/src/Entity/MediaObject.php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Controller\CreateMediaObject;
use App\Controller\EditMediaObject;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * @ORM\Entity
 * @ApiResource(
 * 
 *     ...
 * 
 *     itemOperations={
 *         "get",
 *         "put"={
 *             "controller"=EditMediaObject::class,
 *             "deserialize"=false,
 *             "validation_groups"={"Default", "media_object_create"},
 *             "swagger_context"={
 *                 "consumes"={
 *                     "multipart/form-data",
 *                 },
 *                 "parameters"={
 *                     {
 *                         "in"="formData",
 *                         "name"="file",
 *                         "type"="file",
 *                         "description"="The file to upload",
 *                     },
 *                 },
 *             },
 *         },
 *         "delete"
 *     },
 * )
 * @Vich\Uploadable
 */
class MediaObject
{
    /**
     * @var int|null
     *
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     * @ORM\Id
     */
    protected $id;

    /**
     * @var string|null
     *
     * @ApiProperty(iri="http://schema.org/contentUrl")
     * @Groups({"media_object_read"})
     */
    public $contentUrl;

    /**
     * @var File|null
     *
     * @Assert\NotNull(groups={"media_object_create"})
     * @Vich\UploadableField(mapping="media_object", fileNameProperty="filePath")
     */
    public $file;

    /**
     * @var string|null
     *
     * @ORM\Column(nullable=true)
     */
    public $filePath;

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

The correponding Controller:

<?php
// api/src/Controller/EditMediaObject.php

namespace App\Controller;

use App\Entity\MediaObject;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

final class EditMediaObject
{
    /**
    * @var RequestStack
    */
    private $requestStack;


    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }
    public function __invoke(MediaObject $data): MediaObject
    {
        $request = $this->requestStack->getCurrentRequest();
        $uploadedFile = $request->attributes->get('file');
        if (!$uploadedFile) {
            throw new BadRequestHttpException ('"file" is required');
        }
        $data->file = $uploadedFile;

        return $data;
    }
}

The $data object is susccessfully populated. However I dont have access to the initial request parameters (particularly 'file').

Is this because of the preliminary OPTIONS call? If so, how can I forward / preserve the file to attach it to the MediaObject?

Thanks in advance

Edit:

In case anyone is interested, it is NOT possible to send file parameters over PUT requests in PHP. (source: PHP multipart form data PUT request?)

9lex
  • 131
  • 6

3 Answers3

1

Why are you looking at $request->attributes? You should be looking at $request->files.

See https://symfony.com/doc/current/components/http_foundation.html#accessing-request-data

Tristan
  • 11
  • 1
0

You need to send a POST request with an extra parameter, _method = PUT, It will change the POST request as if it is a PUT request.

Dharman
  • 30,962
  • 25
  • 85
  • 135
user15873
  • 116
  • 2
0

I have recently had a very similar problem, though, I'm not using the entire symfony library, I'm using the HttpFoundation/Request and HttpFoundation/Response objects.

At first I was puzzled by how the request seemed completely empty.

After spending more time than I would have liked looking into it, this is an issue that can be traced back to the internals of PHP.

I managed to find this PECL extension

I'm not really very familiar with pecl, and couldn't seem to get it working using pear. but I'm using CentOS and Remi PHP which has a yum package.

I ran yum install php-pecl-apfd and it literally fixed the issue straight away. I believe there are other packages in various flavours of linux and I'm sure anybody with more knowledge of pear/pecl/general php extensions could get it running on windows or mac with no issue.

By fixed, I mean, my request object was populated as expected (request->all() and request->files for example).

This seems to have worked for both PUT and PATCH requests with and without files (using multipart forms).

If you're using Docker, you'll need to restart your containers.

DazBaldwin
  • 4,125
  • 3
  • 39
  • 43