Skip to content

Latest commit

 

History

History
119 lines (95 loc) · 3.39 KB

response-constructor.md

File metadata and controls

119 lines (95 loc) · 3.39 KB

Response constructor examples

Interface

interface ResponseConstructorInterface
{
    /** @param mixed $result */
    public function constructResponse($result, Request $request): Response;
}

See position in process

Serializer JSON response constructor

⭐ This response constructor is supplied with the bundle.

Most of the time the result will be an object or array and be converted into JSON through the SerializerJsonResponseConstructor. Obviously it needs the custom normalizers for the values objects to be able to do so.

final readonly class SerializerJsonResponseConstructor implements ResponseConstructorInterface
{
    public function __construct(
        private SerializerInterface $serializer,
    ) {
    }

    public function constructResponse($data, Request $request): JsonResponse
    {
        return new JsonResponse(
            $this->serializer->serialize($data),
            200,
            [],
            true
        );
    }
}

File response constructor

A file should be returned as a binary and not as JSON, so we would need a custom response constructor like the following:

final readonly class FileResponseConstructor implements ResponseConstructorInterface
{
    /** @param File $data */
    public function constructResponse($data, Request $request): Response
    {
        return FileManagementHelper::binaryResponse(
            $data->fileContent,
            $data->fileExtension
        );
    }
}

Streamed response

⭐ This response constructor is supplied with the bundle.

There are cases where it's not feasible to return the full response at once. For example when loading a lot of files from an external storage provider and wrapping it into a zip file before sending it to the client. In such a case, the query handler would return a callable like this:

final readonly class GetAllFilesInDirectoryAsDownloadQueryHandler implements QueryHandlerInterface
{
    public function __construct(
        private FileManagement $fileManagement,
        private UserRepository $userRepository,
        private DirectoryRepository $directoryRepository,
    ) {
    }

    /** @param GetAllFilesInDirectoryAsDownloadQuery $query */
    public function handle(Query $query): callable
    {
        $this->requestingUserMustBeAdmin($query);

        $directory = $this->getDirectory($query);

        return function () use ($directory) {
            $this->downloadDirectoryAsZip($directory);
        };
    }

    ...

    private function downloadDirectoryAsZip(Directory $directory): void
    {
        $options = new Archive();
        $options->setContentType('application/octet-stream');
        // This is needed to prevent issues with truncated zip files
        $options->setZeroHeader(true);

        // Initialise zip stream with output zip filename.
        $zip = new ZipStream(
            sprintf('%s.zip', $directory->name),
            $options
        );

        $this->addDirectoryRecursiveToZip($directory, $directory->name, $zip);
        $zip->finish();
    }
}

Such a callable would be sent to a simple streamed response constructor:

final readonly class StreamedResponseConstructor implements ResponseConstructorInterface
{
    /** @param callable $data */
    public function constructResponse($data, Request $request): Response
    {
        return new StreamedResponse($data);
    }
}