Skip to main content

File uploads with the Sylius Settings plugin

· 3 min read
Stephan Hochdörfer
Head of IT Business Operations

In one of our Sylius projects, we are using the Sylius Settings plugin by our friends from Monsieur Biz to manage settings efficiently.

This plugin has been instrumental in configuring "global" variables that merchants can modify without needing to rebuild or restart the Sylius application.

A recent feature request by the merchant required us to implement a document upload and download feature. The merchant needed to upload a document that customers could then download. However, the Settings plugin does not support file uploads by default. Given that the plugin leverages Symfony Forms under the hood, we explored the possibility of integrating into the Symfony Form flow to upload files to a specific location.

Initially, we attempted to use the PRE_SUBMIT and POST_SUBMIT events to develop the requested feature. Unfortunately, this approach did not yield the desired results. The main logic within the Settings entity is designed to work with objects implementing DateTimeInterface or JsonSerializable, but not with file objects directly.

After careful consideration, we discovered a straightforward solution: Using a Data Transformer for the file upload process. A Data Transformer acts as a "translator" between the application's logic and the user's view, converting data from one format to another. Although this might not be the most convenient approach, it effectively addresses our specific use case.

In our DocumentSettingsType class, we configured the file upload field as follows:

<?php

declare(strict_types=1);

namespace App\Form\Type\Settings;

use App\Form\Transformer\UploadedSettingsFileToFilenameTransformer;
use MonsieurBiz\SyliusSettingsPlugin\Form\AbstractSettingsType;
use MonsieurBiz\SyliusSettingsPlugin\Form\SettingsTypeInterface;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;

class DocumentsSettingsType extends AbstractSettingsType implements SettingsTypeInterface
{
public function __construct(private string $kernelProjectDir)
{
}

public function buildForm(FormBuilderInterface $builder, array $options): void
{
$this->addWithDefaultCheckbox($builder, 'file_template', FileType::class, [
'label' => 'admin.ui.form.settings.file_template',
'required' => false,
'data_class' => null,
]);

$builder->get('file_template')->addModelTransformer(
new UploadedSettingsFileToFilenameTransformer($this->kernelProjectDir),
);
}
}

For the file_template form field, we register the UploadedSettingsFileToFilenameTransformer to take care of the file upload logic. The implementation of the UploadedSettingsFileToFilenameTransformer class looks like this:

<?php

declare(strict_types=1);

namespace App\Form\Transformer;

use App\Controller\DocumentsController;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;

final readonly class UploadedSettingsFileToFilenameTransformer implements DataTransformerInterface
{
public function __construct(private string $kernelProjectDir)
{
}

public function transform($value): mixed
{
return null;
}

public function reverseTransform($value): mixed
{
if (!$value instanceof UploadedFile) {
return $value;
}

$filename = uniqid() . '.' . $value->guessExtension();
$value->move($this->kernelProjectDir . DocumentsController::STORAGE_DIR, $filename);

return $filename;
}
}

In our Data Transformer implementation, the transform() method returns null because we don't need to convert the file name back to an UploadedFile object. The reverseTransform() method handles the file upload and returns the filename afterward.