-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix upload file to subdir #6218
base: 4.x
Are you sure you want to change the base?
Conversation
Also need fix while edit entity without changing image - it changes field in db to basename of file |
Workaround for second fix is cumbersome but also possible //...
use App\EasyAdmin\Form\Type\FileUploadType;
//...
ImageField::new('image')->setFormType(FileUploadType::class)
//.. <?php
namespace App\EasyAdmin\Form\Type;
use App\EasyAdmin\Form\DataTransformer\StringToFileTransformer;
use EasyCorp\Bundle\EasyAdminBundle\Form\Type\FileUploadType as BaseType;
use Psr\Container\ContainerInterface;
use Symfony\Component\Form\FormBuilderInterface;
class FileUploadType extends BaseType
{
public function __construct(ContainerInterface $parameterBag)
{
parent::__construct($parameterBag->get('kernel.project_dir'));
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
parent::buildForm($builder, $options);
$uploadDir = $options['upload_dir'];
$uploadFilename = $options['upload_filename'];
$uploadValidate = $options['upload_validate'];
$builder->resetModelTransformers();
$builder->addModelTransformer(new StringToFileTransformer($uploadDir, $uploadFilename, $uploadValidate, $options['multiple']));
}
} <?php
namespace App\EasyAdmin\Form\DataTransformer;
use EasyCorp\Bundle\EasyAdminBundle\Form\DataTransformer\StringToFileTransformer as BaseTransformer;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class StringToFileTransformer extends BaseTransformer
{
private string $uploadDir;
private $uploadFilename;
private $uploadValidate;
private bool $multiple;
public function __construct(string $uploadDir, callable $uploadFilename, callable $uploadValidate, bool $multiple)
{
parent::__construct($uploadDir, $uploadFilename, $uploadValidate, $multiple);
$this->uploadDir = $uploadDir;
$this->uploadFilename = $uploadFilename;
$this->uploadValidate = $uploadValidate;
$this->multiple = $multiple;
}
public function reverseTransform($value): mixed
{
if (null === $value || [] === $value) {
return null;
}
if (!$this->multiple) {
return $this->doReverseTransform($value);
}
if (!\is_array($value)) {
throw new TransformationFailedException('Expected an array or null.');
}
return array_map([$this, 'doReverseTransform'], $value);
}
private function doReverseTransform($value): ?string
{
if (null === $value) {
return null;
}
if ($value instanceof UploadedFile) {
if (!$value->isValid()) {
throw new TransformationFailedException($value->getErrorMessage());
}
$filename = ($this->uploadFilename)($value);
return ($this->uploadValidate)($filename);
}
if ($value instanceof File) {
return str_replace($this->uploadDir, '', $value->getPathname());
}
throw new TransformationFailedException('Expected an instance of File or null.');
}
} |
Probably will be better extend Something like <?php
namespace App\EasyAdmin\Form;
use Symfony\Component\HttpFoundation\File\File;
class DownloadableFile extends File
{
private string $downloadPath;
public function __construct(string $basePath, string $downloadPath)
{
parent::__construct($basePath.$downloadPath);
$this->downloadPath = $downloadPath;
}
public function getDownloadPath()
{
return $this->downloadPath;
}
} and then in //... in the doTransform
if (is_file($this->uploadDir.$value)) {
return new DownloadableFile($this->uploadDir, $value);
}
//...
//... in the doReverseTransform
if ($value instanceof DownloadableFile) {
return $value->getDownloadPath();
}
//... In such case PS. If you are interested, I can make another PR with it and close this one. |
Example widget for edit with preview. {% block app_imageupload_widget %}
{% if not multiple %}
{% set firstImg = currentFiles|first %}
{% if firstImg %}
{% set html_id = 'ea-lightbox-' ~ ea_vars.field.uniqueId %}
{% set image = download_path ~ firstImg.downloadPath %}
<a href="javascript:;" class="ea-lightbox-thumbnail" data-ea-lightbox-content-selector="#{{ html_id }}">
<img src="{{ asset(image) }}" class="img-fluid">
</a>
<div id="{{ html_id }}" class="ea-lightbox">
<img src="{{ asset(image) }}">
</div>
{% else %}
<img src="{{ asset('img/no-image.png') }}" class="img-fluid">
{% endif %}
{% endif %}
{{ block('ea_fileupload_widget') }}
{% endblock %} .app-image-field .form-widget {
width: 200px;
border: 1px solid var(--form-input-border-color);
border-radius: var(--border-radius);
overflow: hidden;
}
.app-image-field .img-fluid {
width: 100%;
height: 150px;
object-fit: cover;
}
.app-image-field .custom-file-label,
.app-image-field .ea-fileupload .input-group-text {
border-color: transparent;
} |
In docs (https://symfony.com/bundles/EasyAdminBundle/current/fields/ImageField.html) and annotation of
setUploadedFileNamePattern
method, noticed that you can combine uploaded file name like[year]/[month]/[day]/[slug]-[contenthash].[extension]
But in such case you get that name with subdirs in database field, but not in filesystem.
UploadedFile::move
works in such way (get basename of second parameter) because ofhttps://github.com/symfony/symfony/blob/d90416ff001b3af6197df477d19eff4f101dac96/src/Symfony/Component/HttpFoundation/File/UploadedFile.php#L185
https://github.com/symfony/symfony/blob/d90416ff001b3af6197df477d19eff4f101dac96/src/Symfony/Component/HttpFoundation/File/File.php#L125
and
https://github.com/symfony/symfony/blob/d90416ff001b3af6197df477d19eff4f101dac96/src/Symfony/Component/HttpFoundation/File/File.php#L133-L139
As workaround you can add form type option
upload_new
for now, like