5

Edit: here's a github with the full code to reproduce the problem

I have the following entity

class Place
{
    use Traits\HasId;

    /**
     * Used for form.
     *
     * @Assert\Image(
     *     mimeTypes = {"image/png", "image/jpeg"},
     *     minWidth = 50,
     *     maxWidth = 1000,
     *     minHeight = 50,
     *     maxHeight = 1000,
     *     maxSize = "1M"
     * )
     */
    private $imageFile = null;

    /**
     * @ORM\OneToOne(targetEntity="MyImage", orphanRemoval=true, cascade={"persist"})
     */
    protected $image;

}

With the following form

class AdminPlaceType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new HasImageTransformer();
        $builder->add('imageFile')->addModelTransformer($transformer);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(['data_class' => 'AppBundle\Entity\Place']);
    }
}

And the following model transformer

class HasImageTransformer implements DataTransformerInterface
{   
    public function transform($hasImage)
    {
        return $hasImage;
    }

    /**
     * reverse transforms.
     */
    public function reverseTransform($hasImage)
    {   
        $file = $hasImage->getImageFile();
        $myImage = new MyImage();
        $myImage->setData(file_get_contents($file->getPathName()))
        $myImage->setMimeType($file->getMimeType());

        $hasImage->setImage($myImage);
    }
}

I can upload a correct image, and the form is correctly saved in database.

However if I submit an incorrect image (for example a .txt file), the form is still saved in database without any error

However if I remove the addModelTransformer from the Form, then I got the correct validation error

This file is not a valid image

as my transformer does not modify the original imageFile field, I'm wondering what could cause this problem.

I'm using php7 and symfony3.3.4

allan.simon
  • 3,886
  • 6
  • 35
  • 60
  • UPDATE: my fault, just noticed you're setting two different properties there. Is ``MyImage`` a successor to ``Symfony\Component\HttpFoundation\File\File``? Symfony's ``ImageValidator`` might not know how to handle your object so it just ignores it. – Michael Hirschler Jul 18 '17 at 06:11
  • Actually "MyImage" is not the one having the validator decorator set, it's the entity in database (for some weird reason, it's the image store in a blob field) , the imageFile field is the one having the validator , and contains a "UploadedFile" object from the submitted form. The transformer create a MyImage object from the UploadedFile – allan.simon Jul 18 '17 at 07:10
  • I tried to reproduce problem, but I had many errors and questions ... try to write all files, becouse those are not sufficient ... -1 – pooler Jul 25 '17 at 14:08
  • @pooler, sorry for this, I will try to setup a github project with a vagrantfile that reproduce the problem – allan.simon Jul 25 '17 at 20:27
  • 1
    `$builder->add('imageFile')->addModelTransformer($transformer);` will apply the transformer to the entire form. You need to add `imageFile` first, then `$builder->get('imageFile')->addModelTransformer($transformer);` - see https://symfony.com/doc/current/form/data_transformers.html – Michel Jul 26 '17 at 02:20
  • @pooler I've added a link to a fully functionnal and minimal repository that reproduce the problem – allan.simon Jul 26 '17 at 20:28
  • @Michel, yes hence why my transformer call the method of the `Place` entity and not the `Image` entity, I understand what you're saying , but the documentation does not say anything about interfering with validation. – allan.simon Jul 26 '17 at 20:29
  • @Michel and if I do so I can't use strong typing anymore, as "imageFile" will contains sometimes actual File sometimes my transformed MyImage – allan.simon Jul 26 '17 at 20:35

3 Answers3

3

The answer was actually pretty stupid

The reason was that I forgot a return in the reverseTransform

/**
 * reverse transforms.
 */
public function reverseTransform($hasImage)
{   
    $file = $hasImage->getImageFile();
    $myImage = new MyImage();
    $myImage->setData(file_get_contents($file->getPathName()))
    $myImage->setMimeType($file->getMimeType());

    $hasImage->setImage($myImage);
    // this was missing :(
    return $hasImage;
}

This was causing the whole entity in the form model to be transformed as "null" BUT the entity itself was not destroyed because I had still reference to it in the controller as it was created through the standard

  public function createAction(Request $request)
  {   
      $place = new Place();

      $form = $this->createForm(AdminPlaceType::class, $place);
      $form->handleRequest($request);
  }

so the $place was containing the correct data, and the form having null it was not triggering validation....

allan.simon
  • 3,886
  • 6
  • 35
  • 60
  • I had a similar thing with a Controller not returning anything...and I forgot the `return` statement. Happens to all of us :) – Jason Roman Jul 27 '17 at 14:42
  • @allan.simon that the code works does not mean it is the best way to do it. You are using a form transformer for a purpose that it was not intended for. A transformer should only convert the input to another different format as an output. A --[Transform]--> B and B --[inverseTransform]--> A Your code is not doing this so adding it to the transform it is not the right place. Your code is doing A --[Trasnform]--> A and A--[inverseTransform]--> A' so if then you have A' --[Trasnform]--> A' This is not how it should work. You can do it, but I just want to let you know that it is not right. – albert Aug 04 '17 at 10:59
2

Your transformer is replacing a UploadFile or File object by MyImage entity. I assume your MyImage is not extending File or UploadFile. This is not the intended use for a transformer. A transformer change one input to another type of input.

I would recommend to remove the transformer and add the code of the transformer into Place->setImageFile. Then you will have the imageFile validation as expected and every time you change the image by another file you will recreate your MyImage even when you are not using the form.

If you really want to do that in the form code and not in the Place entity code you should use the FormEvents https://symfony.com/doc/current/form/events.html FormEvents::POST_SET_DATA

albert
  • 4,290
  • 1
  • 21
  • 42
  • I'm not replacing, i'm transforming uploadfile into a new field , MyImage, I think it's the canon way to do it ? (the example in the documentation take a number to transform it into a an entity ) i.e the UploadFIle/File is the ImageFile property of my entity, and my transformer is setting the Image field part which is of type MyImage, so my entity's initial data hasn't been touched ? – allan.simon Jul 26 '17 at 16:17
  • I've added a link to a github containing a fully functionnal and minimal code that reproduce the problem – allan.simon Jul 26 '17 at 20:27
0

To cause a validation error, throw a TransformationFailedException. But the message you pass to this exception won't be shown to the user. You'll set that message with the invalid_message option (see below). https://symfony.com/doc/current/form/data_transformers.html

helmis.m
  • 234
  • 1
  • 18
  • but i can't throw one because from my transformer point of view everything is fine right ? my understanding is that you will throw such an exception if you have an error as per the transofrmation process (for example if you transform an id intot an entity and it does not exist etc. ) – allan.simon Jul 26 '17 at 16:19