limoncello-php / app

Quick start JSON API application
MIT License
83 stars 7 forks source link

File upload via JSON:API in drupal #61

Open dreamsbond opened 4 years ago

dreamsbond commented 4 years ago

I found an article mentioned about File Upload via JSON:API at https://www.drupal.org/node/3024331, curious if limoncello-app support such implementation?

neomerx commented 4 years ago

Yes, it does. There is nothing special about file uploads really. A controller handler will receive a $request parameter which would have file attachment. When the file is stored and some ID is assigned it's time to return the JSON API response.

$index       = ...;
$requestUri  = $request->getUri();
$crud        = static::defaultCreateApi($container, static::API_CLASS);
$jsonSchemas = $container->get(JsonSchemasInterface::class);
$encoder     = $container->get(EncoderInterface::class);

return static::defaultCreateResponse($index, $requestUri, $crud, $jsonSchemas, $encoder);

The code is taken from here and here.

dreamsbond commented 4 years ago

Could you provide an example?

neomerx commented 4 years ago

Code for controller

    // NOTE: you need to create a model/schema/routes/auth rules/etc for uploaded files. Just like for users, roles or any other resource.

    /**
     * @inheritdoc
     */
    public static function create(
        array $routeParams,
        ContainerInterface $container,
        ServerRequestInterface $request
    ): ResponseInterface {
        $files = $request->getUploadedFiles();
        if (count($files) !== 1 ||
            (($file = reset($files)) instanceof UploadedFileInterface) === false
        ) {
            return new EmptyResponse(422);
        }

        assert($file instanceof UploadedFileInterface);

        // save the file and assign file ID (this one stores to tmp, you'd better to save to some storage)
        $fileName = tempnam(sys_get_temp_dir(), 'my_prefix');
        if ($fileName === false ||
            file_put_contents($fileName, (string)$file->getStream()) === false
        ) {
            return new EmptyResponse(400);
        }
        $fileId = pathinfo($fileName, PATHINFO_FILENAME);

        // here you need to save the data to the database or maybe leave it as it is. Because on the next step
        // `defaultCreateResponse` will try to read the resource by `$fileId` via API so it should return
        // the resource.

        return static::defaultCreateResponse(
            $fileId,
            $request->getUri(),
            static::defaultCreateApi($container, static::API_CLASS),
            $container->get(SettingsProviderInterface::class),
            $container->get(JsonSchemasInterface::class),
            $container->get(EncoderInterface::class)
        );
    }

Code for a test

    public function testUpload(): void
    {
        $file = new UploadedFile(new Stream(__FILE__), filesize(__FILE__), UPLOAD_ERR_OK);

        $response = $this->call('POST', static::API_URI, [], [], [], [], [$file]);
    }
dreamsbond commented 4 years ago

thanks!