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

Skip to content

Commit 0af674d

Browse files
committed
bug #7560 Fix file field with remote storage (javiereguiluz)
This PR was squashed before being merged into the 5.x branch. Discussion ---------- Fix file field with remote storage Fixes #7558. Creating our own service tag for this was a mistake, so let's rely on the tag applied by Flysystem. Commits ------- 25bf3db Fix file field with remote storage
2 parents ce16587 + 25bf3db commit 0af674d

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)
@@ -230,17 +231,17 @@ private function getFilePath(?string $filePath, ?string $basePath): ?string
230231
*
231232
* @return array<string|null>
232233
*/
233-
private function getFlysystemFilesPaths(?array $files, string $urlPrefix): array
234+
private function getFlysystemFilesPaths(?array $files, FilesystemOperator $filesystem, ?string $urlPrefix, string $propertyName): array
234235
{
235236
$filesPaths = [];
236237
foreach ($files as $file) {
237-
$filesPaths[] = $this->getFlysystemFilePath($file, $urlPrefix);
238+
$filesPaths[] = $this->getFlysystemFilePath($file, $filesystem, $urlPrefix, $propertyName);
238239
}
239240

240241
return $filesPaths;
241242
}
242243

243-
private function getFlysystemFilePath(?string $filePath, string $urlPrefix): ?string
244+
private function getFlysystemFilePath(?string $filePath, FilesystemOperator $filesystem, ?string $urlPrefix, string $propertyName): ?string
244245
{
245246
if (null === $filePath) {
246247
return null;
@@ -251,6 +252,14 @@ private function getFlysystemFilePath(?string $filePath, string $urlPrefix): ?st
251252
return $filePath;
252253
}
253254

254-
return rtrim($urlPrefix, '/').'/'.ltrim($filePath, '/');
255+
if (null !== $urlPrefix) {
256+
return rtrim($urlPrefix, '/').'/'.ltrim($filePath, '/');
257+
}
258+
259+
try {
260+
return $filesystem->publicUrl(ltrim($filePath, '/'));
261+
} catch (UnableToGeneratePublicUrl $e) {
262+
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);
263+
}
255264
}
256265
}

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)
@@ -220,17 +221,17 @@ private function getImagePath(?string $imagePath, ?string $basePath): ?string
220221
*
221222
* @return array<string|null>
222223
*/
223-
private function getFlysystemImagesPaths(?array $images, string $urlPrefix): array
224+
private function getFlysystemImagesPaths(?array $images, FilesystemOperator $filesystem, ?string $urlPrefix, string $propertyName): array
224225
{
225226
$imagesPaths = [];
226227
foreach ($images as $image) {
227-
$imagesPaths[] = $this->getFlysystemImagePath($image, $urlPrefix);
228+
$imagesPaths[] = $this->getFlysystemImagePath($image, $filesystem, $urlPrefix, $propertyName);
228229
}
229230

230231
return $imagesPaths;
231232
}
232233

233-
private function getFlysystemImagePath(?string $imagePath, string $urlPrefix): ?string
234+
private function getFlysystemImagePath(?string $imagePath, FilesystemOperator $filesystem, ?string $urlPrefix, string $propertyName): ?string
234235
{
235236
if (null === $imagePath) {
236237
return null;
@@ -241,6 +242,14 @@ private function getFlysystemImagePath(?string $imagePath, string $urlPrefix): ?
241242
return $imagePath;
242243
}
243244

244-
return rtrim($urlPrefix, '/').'/'.ltrim($imagePath, '/');
245+
if (null !== $urlPrefix) {
246+
return rtrim($urlPrefix, '/').'/'.ltrim($imagePath, '/');
247+
}
248+
249+
try {
250+
return $filesystem->publicUrl(ltrim($imagePath, '/'));
251+
} catch (UnableToGeneratePublicUrl $e) {
252+
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);
253+
}
245254
}
246255
}

src/Field/FileField.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ public function setFlysystemStorage(string $storageName): self
258258
/**
259259
* Sets the URL prefix used to generate public URLs for files stored in Flysystem
260260
* (e.g. 'https://cdn.example.com/uploads').
261+
*
262+
* This is optional. When not set, EasyAdmin derives the public URL from the
263+
* Flysystem storage itself (via the 'public_url' or 'public_url_generator'
264+
* configured for that storage in flysystem-bundle). Use this setter to
265+
* override that default, for example when the admin UI serves files from a
266+
* different host than the one configured in Flysystem.
261267
*/
262268
public function setFlysystemUrlPrefix(string $urlPrefix): self
263269
{

src/Field/ImageField.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ public function setFlysystemStorage(string $storageName): self
261261
/**
262262
* Sets the URL prefix used to generate public URLs for images stored in Flysystem
263263
* (e.g. 'https://cdn.example.com/uploads').
264+
*
265+
* This is optional. When not set, EasyAdmin derives the public URL from the
266+
* Flysystem storage itself (via the 'public_url' or 'public_url_generator'
267+
* configured for that storage in flysystem-bundle). Use this setter to
268+
* override that default, for example when the admin UI serves files from a
269+
* different host than the one configured in Flysystem.
264270
*/
265271
public function setFlysystemUrlPrefix(string $urlPrefix): self
266272
{

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)