Skip to content
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

ImageField: bug with Symfony Image constraint #5227

Open
ajie62 opened this issue May 13, 2022 · 11 comments · May be fixed by #6258
Open

ImageField: bug with Symfony Image constraint #5227

ajie62 opened this issue May 13, 2022 · 11 comments · May be fixed by #6258
Labels
Milestone

Comments

@ajie62
Copy link

ajie62 commented May 13, 2022

Describe the bug
Hi, I'm using EasyAdmin on a side project, and I decided to use an ImageField on a form to upload... well, an image. That being done, I tried to use it and it was working just fine, until I tried to put a constraint on the ImageField in the CrudController. Once you put on constraint on it, to prevent users to from uploading other types of files, you get the error 'The file could not be found.'. It is even impossible to create the entity.

To Reproduce

  • Create an entity with an Image property (string) and make a CRUD controller for it.
  • In the CrudController, try to use the Image constraint from Symfony.

(OPTIONAL) Additional context
This is what I've done:

ImageField::new('image')
                ->setBasePath('uploads/post-images')
                ->setUploadDir('public/uploads/post-images')
                ->setUploadedFileNamePattern('[slug]-[timestamp].[extension]')
                ->setFormTypeOptions([
                          'required' => false,
                          'constraints' => [
                                    new Image(),
                          ],
                ]),

I think using the ImageField should be enough. We shouldn't have to add an Image constraint, should we?

@bocharsky-bw
Copy link
Contributor

Well explained! Based on this, I think this issue has 2 sub-issues:

  1. ImageField should allow uploading only images out of the box
  2. ImageField should allow adding Image constraint to add more strict limitations for specific cases

@Digi92
Copy link

Digi92 commented Jun 27, 2022

Hi,
in case someone else tries to add a mime validation to the field type "ImageField".
The main problem is that the field type "ImageField" only passes the file name as a value to the validator instead of the object.
Therefore I created my own constraint which first loads the object out of the context and then passes it to the Symfony validator.
With this it works for me now.

Versions:
Symfony: 6.1
Eeasyadmin: 4.3.2

CrudController

ImageField::new('image')
             ->setBasePath('uploads/post-images')
             ->setUploadDir('public/uploads/post-images')
             ->setUploadedFileNamePattern('[slug]-[timestamp].[extension]')
            ->setFormTypeOption(
                'constraints',
                [
                    new \App\Validator\Constraints\EasyAdminFile([
                        'mimeTypes' => [ // We want to let upload only jpeg or png
                            'image/jpeg',
                            'image/png',
                        ],
                    ])
                ]
            );

src/Validator/Constraint/EasyAdminFile.php

<?php

namespace App\Validator\Constraints;

/**
 * @Annotation
 * @Target({"PROPERTY", "METHOD", "ANNOTATION"})
 *
 * @property int $maxSize
 */
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class EasyAdminFile extends \Symfony\Component\Validator\Constraints\File
{
}

src/Validator/Constraint/EasyAdminFileValidator.php

<?php

namespace App\Validator\Constraints;

use EasyCorp\Bundle\EasyAdminBundle\Form\Type\Model\FileUploadState;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

/**
 * This constraint is created for adding validation support to the easyAdmin field type "ImageField".
 * The Symfony constraint file validation is expecting an object of type "UploadedFile" or "FileObject" for
 * handling the validation but EasyAdmin only returned the filename.
 * Therefore, we have to load the object first before calling the symfony file validator.
 * Created for versions:
 * symfony: 6.1
 * easycorp/easyadmin-bundle: 4.3.2
 *
 * Class EasyAdminFileValidator
 * @package App\Validator\Constraints
 */
class EasyAdminFileValidator extends \Symfony\Component\Validator\Constraints\FileValidator
{
    /**
     * @param mixed $value
     * @param \Symfony\Component\Validator\Constraint $constraint
     * @return void
     */
    public function validate(mixed $value, Constraint $constraint)
    {
        if (!$constraint instanceof EasyAdminFile) {
            throw new UnexpectedTypeException($constraint, EasyAdminFile::class);
        }

        if ($value !== null &&
            $this->context->getObject() instanceof Form &&
            $this->context->getObject()->getConfig() instanceof FormBuilder
        ) {
            $config = $this->context->getObject()->getConfig();

            /** @var FileUploadState $state */
            $state = $config->getAttribute('state');

            if (!$state instanceof FileUploadState ||
                !$state->isModified()
            ) {
                return;
            }

            // On the upload field we can set the option for multiple uploads, so we need to take care of this
            foreach ($state->getUploadedFiles() as $index => $file) {
                parent::validate($file, $constraint);
            }
        }
    }
}

@bocharsky-bw
Copy link
Contributor

I still think it should work out-of-the-box in EA, but your workaround is valid, thanks for sharing it!

@Snowbaha
Copy link

@javiereguiluz It will be great to add the validation of Image with ImageField :)

@bakhtiyor
Copy link

@Digi92 thanks for your implementation. I am getting following error message on using it, do you have any idea what is wrong?

image

@abouross
Copy link

abouross commented Aug 12, 2022

In fact currently it sends two values including the file name first then the file second, just ignore the validation if it is not an instance of FileUploaded:

src/Validator/EasyadminImage.php :

<?php


namespace App\Validator;


use Symfony\Component\Validator\Constraints\Image;

/**
 * @Annotation
 *
 * @Target({"PROPERTY", "METHOD", "ANNOTATION"})
 */
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class EasyadminImage extends Image
{

}

src/Validator/EasyadminImageValidator.php :

<?php


namespace App\Validator;


use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\ImageValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

class EasyadminImageValidator extends ImageValidator
{
   public function validate($value, Constraint $constraint)
   {
       if (!$constraint instanceof EasyadminImage) {
           throw new UnexpectedTypeException($constraint, EasyadminImage::class);
       }
       if ($value instanceof UploadedFile)
           parent::validate($value, $constraint);
   }

}

usage example :

ImageField::new('image', 'Icon')
                    ->setColumns(4)
                    ->setUploadDir('public' . DIRECTORY_SEPARATOR . 'uploaded' . DIRECTORY_SEPARATOR . 'images')
                    ->setBasePath('uploaded' . DIRECTORY_SEPARATOR . 'images')
                    ->setUploadedFileNamePattern('[slug]-[contenthash].[extension]')
                    ->setHelp('Image .png, .jpg et jpeg uniquement au format 100 x 100 px')
                    ->setFormTypeOption('constraints', [new EasyadminImage(null, null, null, ['image/png', 'image/jpg', 'image/jpeg'],
                        100, 300, 100, 300, 1, 1)])

@COil
Copy link
Contributor

COil commented Jan 18, 2023

I came to the same conclusion @abouross.

@javiereguiluz javiereguiluz added this to the 4.x milestone Feb 8, 2023
@luismisanchez
Copy link

#5227 (comment)

Just to say thanks to @Digi92 . Working solution on Symfony 5.4.22 and Easyadmin 4.6.1.

@psihius
Copy link
Contributor

psihius commented Feb 7, 2024

@javiereguiluz do you have any ideas on how this one could be fixed so we do not need a workaround? I plan to try and solve this issue, so looking for some feedback if there's one before I dive into this.

@Seb33300
Copy link
Contributor

In fact currently it sends two values including the file name first then the file second, just ignore the validation if it is not an instance of FileUploaded:

This is because the same constraints are passed to the ImageField form type (which is a string saved in the entity) and to the underlying FileType.

In order to properly fix this issue, we should create a new custom option, for instance file_constraints that should be applied to the FileType only.

@Seb33300 Seb33300 linked a pull request Apr 13, 2024 that will close this issue
@Seb33300
Copy link
Contributor

Seb33300 commented Apr 13, 2024

I just created a PR to fix this issue: #6258

If you can, please try it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.