Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 25bf3db

Browse files
committed
Fix file field with remote storage
1 parent dc85429 commit 25bf3db

12 files changed

Lines changed: 153 additions & 48 deletions

File tree

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"symfony/process": "^6.4.33|^7.0|^8.0",
6262
"symfony/web-link": "^6.4.32|^7.0|^8.0",
6363
"vincentlanglet/twig-cs-fixer": "^3.10",
64-
"league/flysystem": "^3.0",
64+
"league/flysystem": "^3.10",
6565
"zenstruck/foundry": "^2.3"
6666
},
6767
"config": {

config/services.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,11 +408,11 @@
408408

409409
->set(FileConfigurator::class)
410410
->arg(0, param('kernel.project_dir'))
411-
->arg(1, tagged_locator(EasyAdminExtension::TAG_FLYSYSTEM_STORAGE))
411+
->arg(1, tagged_locator('flysystem.storage', 'storage'))
412412

413413
->set(ImageConfigurator::class)
414414
->arg(0, param('kernel.project_dir'))
415-
->arg(1, tagged_locator(EasyAdminExtension::TAG_FLYSYSTEM_STORAGE))
415+
->arg(1, tagged_locator('flysystem.storage', 'storage'))
416416

417417
->set(IntegerConfigurator::class)
418418

doc/fields/FileField.rst

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ configuration (e.g. ``default.storage``)::
251251

252252
yield FileField::new('attachment')
253253
->setFlysystemStorage('default.storage')
254-
->setFlysystemUrlPrefix('https://cdn.example.com/uploads')
255254
->setUploadDir('files/')
256255
->setUploadedFileNamePattern('[uuid].[extension]');
257256

@@ -272,18 +271,24 @@ storage (not as a local directory).
272271
setFlysystemUrlPrefix
273272
~~~~~~~~~~~~~~~~~~~~~
274273

275-
Sets the URL prefix used to build the public URLs for files stored in Flysystem.
276-
This is typically a CDN URL or a public URL pointing to your storage bucket::
274+
**This method is optional.** By default, EasyAdmin generates the public URL of
275+
each file from the Flysystem storage itself (via the ``public_url`` or
276+
``public_url_generator`` option configured for that storage). Use this method
277+
only to override that default; for example when the admin UI needs to serve
278+
files from a different host than the one configured in Flysystem, or when your
279+
Flysystem storage has no public URL generator configured::
277280

278281
yield FileField::new('...')->setFlysystemUrlPrefix('https://cdn.example.com/uploads');
279282

280-
This prefix is combined with the file path to generate the full URL shown in the
281-
``index`` and ``detail`` pages.
283+
When set, this prefix is combined with the file path to generate the full URL
284+
shown in the ``index`` and ``detail`` pages, and takes precedence over the
285+
Flysystem ``public_url`` configuration.
282286

283287
.. note::
284288

285-
When using Flysystem, the ``setBasePath()`` option is ignored. Use
286-
``setFlysystemUrlPrefix()`` instead.
289+
When using Flysystem, the ``setBasePath()`` option is ignored. Configure
290+
``public_url`` in your Flysystem storage, or call ``setFlysystemUrlPrefix()``
291+
to override it.
287292

288293
How It Works
289294
~~~~~~~~~~~~

doc/fields/ImageField.rst

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,6 @@ configuration (e.g. ``default.storage``)::
216216

217217
yield ImageField::new('photo')
218218
->setFlysystemStorage('default.storage')
219-
->setFlysystemUrlPrefix('https://cdn.example.com/uploads')
220219
->setUploadDir('images/')
221220
->setUploadedFileNamePattern('[uuid].[extension]');
222221

@@ -237,18 +236,24 @@ storage (not as a local directory).
237236
setFlysystemUrlPrefix
238237
~~~~~~~~~~~~~~~~~~~~~
239238

240-
Sets the URL prefix used to build the public URLs for images stored in Flysystem.
241-
This is typically a CDN URL or a public URL pointing to your storage bucket::
239+
**This method is optional.** By default, EasyAdmin generates the public URL of
240+
each image from the Flysystem storage itself (via the ``public_url`` or
241+
``public_url_generator`` option configured for that storage). Use this method
242+
only to override that default; for example when the admin UI needs to serve
243+
images from a different host than the one configured in Flysystem, or when your
244+
Flysystem storage has no public URL generator configured::
242245

243246
yield ImageField::new('...')->setFlysystemUrlPrefix('https://cdn.example.com/uploads');
244247

245-
This prefix is combined with the image path to generate the full URL shown in the
246-
``index`` and ``detail`` pages.
248+
When set, this prefix is combined with the image path to generate the full URL
249+
shown in the ``index`` and ``detail`` pages, and takes precedence over the
250+
Flysystem ``public_url`` configuration.
247251

248252
.. note::
249253

250-
When using Flysystem, the ``setBasePath()`` option is ignored. Use
251-
``setFlysystemUrlPrefix()`` instead.
254+
When using Flysystem, the ``setBasePath()`` option is ignored. Configure
255+
``public_url`` in your Flysystem storage, or call ``setFlysystemUrlPrefix()``
256+
to override it.
252257

253258
All existing options (``setUploadedFileNamePattern()``, ``setFileConstraints()``,
254259
``mimeTypes()``, ``maxSize()``, replaced file behaviors, ``isDeletable()``) continue

src/DependencyInjection/EasyAdminExtension.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ class EasyAdminExtension extends Extension implements PrependExtensionInterface
2626
public const TAG_FIELD_CONFIGURATOR = 'ea.field_configurator';
2727
public const TAG_FILTER_CONFIGURATOR = 'ea.filter_configurator';
2828
public const TAG_ACTIONS_EXTENSION = 'ea.actions_extension';
29-
public const TAG_FLYSYSTEM_STORAGE = 'ea.flysystem_storage';
3029

3130
public function load(array $configs, ContainerBuilder $container): void
3231
{
@@ -51,11 +50,6 @@ static function (Definition $definition, AdminRoute $attribute, \ReflectionClass
5150
$container->registerForAutoconfiguration(ActionsExtensionInterface::class)
5251
->addTag(self::TAG_ACTIONS_EXTENSION);
5352

54-
if (interface_exists('League\Flysystem\FilesystemOperator')) {
55-
$container->registerForAutoconfiguration(\League\Flysystem\FilesystemOperator::class)
56-
->addTag(self::TAG_FLYSYSTEM_STORAGE);
57-
}
58-
5953
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../../config'));
6054
$loader->load('services.php');
6155
}

src/Field/Configurator/FileConfigurator.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use EasyCorp\Bundle\EasyAdminBundle\Field\FileField;
1111
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Model\FlysystemFile;
1212
use League\Flysystem\FilesystemOperator;
13+
use League\Flysystem\UnableToGeneratePublicUrl;
1314
use Psr\Container\ContainerInterface;
1415
use Symfony\Component\HttpFoundation\File\File;
1516
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -45,10 +46,10 @@ public function configure(FieldDto $field, EntityDto $entityDto, AdminContext $c
4546

4647
$configuredBasePath = $field->getCustomOption(FileField::OPTION_BASE_PATH);
4748

48-
if (null !== $filesystem && null !== $flysystemUrlPrefix) {
49+
if (null !== $filesystem) {
4950
$formattedValue = \is_array($field->getValue())
50-
? $this->getFlysystemFilesPaths($field->getValue(), $flysystemUrlPrefix)
51-
: $this->getFlysystemFilePath($field->getValue(), $flysystemUrlPrefix);
51+
? $this->getFlysystemFilesPaths($field->getValue(), $filesystem, $flysystemUrlPrefix, $field->getProperty())
52+
: $this->getFlysystemFilePath($field->getValue(), $filesystem, $flysystemUrlPrefix, $field->getProperty());
5253
} else {
5354
$formattedValue = \is_array($field->getValue())
5455
? $this->getFilesPaths($field->getValue(), $configuredBasePath)
@@ -224,17 +225,17 @@ private function getFilePath(?string $filePath, ?string $basePath): ?string
224225
*
225226
* @return array<string|null>
226227
*/
227-
private function getFlysystemFilesPaths(?array $files, string $urlPrefix): array
228+
private function getFlysystemFilesPaths(?array $files, FilesystemOperator $filesystem, ?string $urlPrefix, string $propertyName): array
228229
{
229230
$filesPaths = [];
230231
foreach ($files as $file) {
231-
$filesPaths[] = $this->getFlysystemFilePath($file, $urlPrefix);
232+
$filesPaths[] = $this->getFlysystemFilePath($file, $filesystem, $urlPrefix, $propertyName);
232233
}
233234

234235
return $filesPaths;
235236
}
236237

237-
private function getFlysystemFilePath(?string $filePath, string $urlPrefix): ?string
238+
private function getFlysystemFilePath(?string $filePath, FilesystemOperator $filesystem, ?string $urlPrefix, string $propertyName): ?string
238239
{
239240
if (null === $filePath) {
240241
return null;
@@ -245,6 +246,14 @@ private function getFlysystemFilePath(?string $filePath, string $urlPrefix): ?st
245246
return $filePath;
246247
}
247248

248-
return rtrim($urlPrefix, '/').'/'.ltrim($filePath, '/');
249+
if (null !== $urlPrefix) {
250+
return rtrim($urlPrefix, '/').'/'.ltrim($filePath, '/');
251+
}
252+
253+
try {
254+
return $filesystem->publicUrl(ltrim($filePath, '/'));
255+
} catch (UnableToGeneratePublicUrl $e) {
256+
throw new \LogicException(sprintf('Unable to generate the public URL for the file stored in Flysystem for the "%s" field. Either configure the "public_url" option for this storage in your Flysystem configuration, or call setFlysystemUrlPrefix() on this field.', $propertyName), 0, $e);
257+
}
249258
}
250259
}

src/Field/Configurator/ImageConfigurator.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
1111
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Model\FlysystemFile;
1212
use League\Flysystem\FilesystemOperator;
13+
use League\Flysystem\UnableToGeneratePublicUrl;
1314
use Psr\Container\ContainerInterface;
1415
use Symfony\Component\HttpFoundation\File\File;
1516
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -45,10 +46,10 @@ public function configure(FieldDto $field, EntityDto $entityDto, AdminContext $c
4546

4647
$configuredBasePath = $field->getCustomOption(ImageField::OPTION_BASE_PATH);
4748

48-
if (null !== $filesystem && null !== $flysystemUrlPrefix) {
49+
if (null !== $filesystem) {
4950
$formattedValue = \is_array($field->getValue())
50-
? $this->getFlysystemImagesPaths($field->getValue(), $flysystemUrlPrefix)
51-
: $this->getFlysystemImagePath($field->getValue(), $flysystemUrlPrefix);
51+
? $this->getFlysystemImagesPaths($field->getValue(), $filesystem, $flysystemUrlPrefix, $field->getProperty())
52+
: $this->getFlysystemImagePath($field->getValue(), $filesystem, $flysystemUrlPrefix, $field->getProperty());
5253
} else {
5354
$formattedValue = \is_array($field->getValue())
5455
? $this->getImagesPaths($field->getValue(), $configuredBasePath)
@@ -214,17 +215,17 @@ private function getImagePath(?string $imagePath, ?string $basePath): ?string
214215
*
215216
* @return array<string|null>
216217
*/
217-
private function getFlysystemImagesPaths(?array $images, string $urlPrefix): array
218+
private function getFlysystemImagesPaths(?array $images, FilesystemOperator $filesystem, ?string $urlPrefix, string $propertyName): array
218219
{
219220
$imagesPaths = [];
220221
foreach ($images as $image) {
221-
$imagesPaths[] = $this->getFlysystemImagePath($image, $urlPrefix);
222+
$imagesPaths[] = $this->getFlysystemImagePath($image, $filesystem, $urlPrefix, $propertyName);
222223
}
223224

224225
return $imagesPaths;
225226
}
226227

227-
private function getFlysystemImagePath(?string $imagePath, string $urlPrefix): ?string
228+
private function getFlysystemImagePath(?string $imagePath, FilesystemOperator $filesystem, ?string $urlPrefix, string $propertyName): ?string
228229
{
229230
if (null === $imagePath) {
230231
return null;
@@ -235,6 +236,14 @@ private function getFlysystemImagePath(?string $imagePath, string $urlPrefix): ?
235236
return $imagePath;
236237
}
237238

238-
return rtrim($urlPrefix, '/').'/'.ltrim($imagePath, '/');
239+
if (null !== $urlPrefix) {
240+
return rtrim($urlPrefix, '/').'/'.ltrim($imagePath, '/');
241+
}
242+
243+
try {
244+
return $filesystem->publicUrl(ltrim($imagePath, '/'));
245+
} catch (UnableToGeneratePublicUrl $e) {
246+
throw new \LogicException(sprintf('Unable to generate the public URL for the image stored in Flysystem for the "%s" field. Either configure the "public_url" option for this storage in your Flysystem configuration, or call setFlysystemUrlPrefix() on this field.', $propertyName), 0, $e);
247+
}
239248
}
240249
}

src/Field/FileField.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,12 @@ public function setFlysystemStorage(string $storageName): self
248248
/**
249249
* Sets the URL prefix used to generate public URLs for files stored in Flysystem
250250
* (e.g. 'https://cdn.example.com/uploads').
251+
*
252+
* This is optional. When not set, EasyAdmin derives the public URL from the
253+
* Flysystem storage itself (via the 'public_url' or 'public_url_generator'
254+
* configured for that storage in flysystem-bundle). Use this setter to
255+
* override that default, for example when the admin UI serves files from a
256+
* different host than the one configured in Flysystem.
251257
*/
252258
public function setFlysystemUrlPrefix(string $urlPrefix): self
253259
{

src/Field/ImageField.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,12 @@ public function setFlysystemStorage(string $storageName): self
251251
/**
252252
* Sets the URL prefix used to generate public URLs for images stored in Flysystem
253253
* (e.g. 'https://cdn.example.com/uploads').
254+
*
255+
* This is optional. When not set, EasyAdmin derives the public URL from the
256+
* Flysystem storage itself (via the 'public_url' or 'public_url_generator'
257+
* configured for that storage in flysystem-bundle). Use this setter to
258+
* override that default, for example when the admin UI serves files from a
259+
* different host than the one configured in Flysystem.
254260
*/
255261
public function setFlysystemUrlPrefix(string $urlPrefix): self
256262
{

src/Form/Type/FileUploadType.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Model\FileUploadState;
88
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Model\FlysystemFile;
99
use League\Flysystem\FilesystemOperator;
10+
use League\Flysystem\UnableToGeneratePublicUrl;
1011
use Symfony\Component\Filesystem\Filesystem;
1112
use Symfony\Component\Form\AbstractType;
1213
use Symfony\Component\Form\DataMapperInterface;
@@ -102,27 +103,50 @@ public function buildView(FormView $view, FormInterface $form, array $options):
102103
}
103104

104105
$uploadDir = $options['upload_dir'];
106+
$flysystemStorage = $options['flysystem_storage'];
105107
$flysystemUrlPrefix = $options['flysystem_url_prefix'];
106108
$currentFileNames = [];
109+
$currentFileUrls = [];
107110
foreach ($currentFiles as $file) {
108111
if ($file instanceof FlysystemFile) {
109-
$currentFileNames[] = $file->getPathname();
112+
$fileName = $file->getPathname();
113+
$currentFileNames[] = $fileName;
114+
$currentFileUrls[] = $this->resolveFlysystemFileUrl($flysystemStorage, $flysystemUrlPrefix, $fileName);
110115
} elseif ($file instanceof File) {
111-
$currentFileNames[] = str_starts_with($file->getPathname(), $uploadDir)
116+
$fileName = str_starts_with($file->getPathname(), $uploadDir)
112117
? mb_substr($file->getPathname(), mb_strlen($uploadDir))
113118
: $file->getFilename();
119+
$currentFileNames[] = $fileName;
120+
$currentFileUrls[] = null;
114121
}
115122
}
116123

117124
$view->vars['currentFiles'] = $currentFiles;
118125
$view->vars['currentFileNames'] = $currentFileNames;
126+
$view->vars['currentFileUrls'] = $currentFileUrls;
119127
$view->vars['multiple'] = $options['multiple'];
120128
$view->vars['allow_add'] = $options['allow_add'];
121129
$view->vars['allow_delete'] = $options['allow_delete'];
122130
$view->vars['allow_view'] = $options['allow_view'];
123131
$view->vars['allow_download'] = $options['allow_download'];
124132
$view->vars['download_path'] = $options['download_path'];
125-
$view->vars['flysystem_url_prefix'] = $flysystemUrlPrefix;
133+
}
134+
135+
private function resolveFlysystemFileUrl(?FilesystemOperator $filesystem, ?string $urlPrefix, string $fileName): ?string
136+
{
137+
if (null !== $urlPrefix) {
138+
return rtrim($urlPrefix, '/').'/'.ltrim($fileName, '/');
139+
}
140+
141+
if (null === $filesystem) {
142+
return null;
143+
}
144+
145+
try {
146+
return $filesystem->publicUrl(ltrim($fileName, '/'));
147+
} catch (UnableToGeneratePublicUrl) {
148+
return null;
149+
}
126150
}
127151

128152
public function configureOptions(OptionsResolver $resolver): void

0 commit comments

Comments
 (0)