7

I have a Symfony2 application with a form where a file type field. I need to upload an image of a student there, so I helped this documentation: How to Upload Files

It is my code:

Controller:

public function createAction(Request $request)
{        
    if ($request->isXmlHttpRequest() && !$request->isMethod('POST')) {
    throw new HttpException('XMLHttpRequests/AJAX calls must be POSTed');
    }

    $entity = new Student();
    $form = $this->createCreateForm($entity);
    $form->handleRequest($request);

    if ($form->isValid()) {
       $file = $entity->getPhoto();

       $fileName = md5(uniqid()).'.'.$file->guessExtension();

       $photoDir = $this->container->getParameter('kernel.root_dir').'/../web/uploads/images';

       $file->move($photoDir, $fileName);

       $entity->setPhoto($fileName);

       $em = $this->getDoctrine()->getManager();
       $em->persist($entity);
       $em->flush();

       if ($request->isXmlHttpRequest()) {
            return new JsonResponse(array('message' => 'Success!','success' => true), 200);
        }

        if ($request->isMethod('POST')) {
        return new JsonResponse(array('message' => 'Invalid form','success' => false), 400);
    }

      return $this->redirect($this->generateUrl('student_show', array('id' => $entity->getId())));
    }
    return $this->render('BackendBundle:Student:new.html.twig', array(
        'entity' => $entity,
        'form'   => $form->createView(),
    ));
}

Entity:

   use Doctrine\ORM\Mapping as ORM;
   use Symfony\Component\Validator\Constraints as Assert;

   //...
   /**
   * @var string
   *
   * @ORM\Column(name="photo", type="string", length=255, nullable=true)
   * 
   */
   private $photo;


   public function setPhoto($photo)
   {
    $this->photo = $photo;

    return $this;
   }

   public function getPhoto()
   {
    return $this->photo;
   }

formtype:

   //...

   ->add('photo', 'file', array('required' => false))

   //...

Javascript:

 //...

$('.form_student').on("submit",function(event) {
 event.preventDefault();

 $.ajax({
  type: 'POST',
  url: Routing.generate('student_create'),
  data: $(this).serialize(),
  dataType: 'json',

  success: function(response) {

   alert(response.message);
  },
  error: function (response, desc, err){
      if (response.responseJSON && response.responseJSON.message) {
         alert(response.responseJSON.message);
      }
      else{
         alert(desc);
      }
  }
 });
});

The problem I have now is I have to do it with an Ajax request, but do not know how to send that file field and it can be used then in Symfony controller.

I have seen some FormData(), but do not know how it is used.

Could you help me?

3
  • Can you help me help you? I don't want to go elsewhere to look at your code. Commented Jan 20, 2016 at 17:08
  • Hi @MadPhysicist. Sorry, I've added the code. Commented Jan 20, 2016 at 17:25
  • 1
    @Joseph IIRC you can add an answer and accept it. Commented Jan 23, 2016 at 22:04

3 Answers 3

11

I've solved changing in my code:

  • data: new FormData($(this)[0]) instead of data: $ (this).serialize()

  • Adding the ajax request:

    processData: false, contentType: false, cache: false,

and the file is sent correctly

Sign up to request clarification or add additional context in comments.

1 Comment

The new FormData() HTML5 element breaks compatibility with IE8/9 - the serialize should be no problem. What really fixes the issue are {processData: false, contentType: false}
3

I have solve this in following way, if you want to do it using Ajax.

In your entity declare this :

/** 
 *@ORM\HasLifecycleCallbacks
 */
class Student
{
    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    public $path;

    /**
     * @Assert\File(
     *   maxSize="60000",
     * )
     */
    private $file;

    private $temp;

    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file = $file;
        if (isset($this->path)) {
            // store the old name to delete after the update
            $this->temp = $this->path;
            $this->path = null;
        } else {
            $this->path = 'initial';
        }
    }

    /**
     * Get file.
     *
     * @return UploadedFile
     */
    public function getFile()
    {
        return $this->file;
    }

    public function getAbsolutePath()
    {
        return null === $this->path
            ? null
            : $this->getUploadRootDirPath().'/'.$this->path;
    }

    public function getWebPath()
    {
        return null === $this->path
            ? null
            : $this->getUploadDirPath().'/'.$this->path;
    }

    protected function getUploadRootDirPath()
    {
        // the absolute directory path where uploaded
        // documents should be saved
        return __DIR__.'/../../../../web/'.$this->getUploadDirPath();
    }

    protected function getUploadDirPath()
    {
        // get rid of the __DIR__ so it doesn't screw up
        // when displaying uploaded doc/image in the view.
        return 'uploads/student_photos';
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->getFile()) {
            // do whatever you want to generate a unique name
            $filename = basename($this->getFile()->getClientOriginalName(),'.'.$this->getFile()->getClientOriginalExtension());
            $this->path = $filename.'.'.$this->getFile()->getClientOriginalExtension();
            if(file_exists($this->getUploadRootDirPath().'/'.$this->path)==1)
            {
                $date = date('-d_M_Y_H:i');
                $this->path = $filename.$date.'.'.$this->getFile()->getClientOriginalExtension();
            }
        }
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if (null === $this->getFile()) {
            return;
        }

        $this->getFile()->move($this->getUploadRootDirPath(), $this->path);

        if (isset($this->temp)) {
            // delete the old image
            unlink($this->getUploadRootDirPath().'/'.$this->temp);
            // clear the temp image path
            $this->temp = null;
        }
        $this->file = null;
    }


    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        $file = $this->getAbsolutePath();
        if ($file) {
            unlink($file);
        }
    }
}

In your FormType :

->add('file', null, array('label' => 'Profile Picture', 'required' => false))

In you Controller :

   $entity->setFile($request->files->get('photo')); //here you have get your file field name
   $em->persist($entity);
   $em->flush();

your ajax looks fine, but if this will not work than use

  data:new FormData(this),

instead of

  data: $(this).serialize(),

and add these two parameters in ajax:

      processData: false,
      contentType: false  

You can change save file method as your requirement and change path field to photo.

6 Comments

Hi @herr, thanks for the reply, at the end I just fix my code changing data: $ (this) .serialize () with data: new FormData ($ (this) [0])
Sorry @Joseph i forgot to add in answer these two parameters processData: false, contentType: false. it required to send files in ajax.
Hi @herr, I have a problem, now I can not leave blank the value of the file in my code in the form because it gives me this error: Error: Call to a member function guessExtension() on a non-object in....I added a condition to test the controller that is invalid not the piece of code to upload file to run but still gives me that error, maybe I'm not doing well. I have also tried your code but I get a validation message that already showed me other codes: This form can not have additional fields. Would you know how to fix so you can leave blank file in the form?
Hi @herr, In the end I've solved it by putting the condition if("on"!=$entity->getPhoto()), because when kept before without the code to upload files, the "on" value is kept in the file field in the database. Would you know because that value is saved?
In your code you have to test if($entity->getPhoto() !='') (space or null) In my code you have check if($request->files->get('photo'))
|
0

Update:

I think the problem is that you must deserialize the received in your controller in case you are submitting via ajax

Addentionally you should serialize the form explicitly and not serialize using thisthis

var data= $('form.form_student').serialize();

7 Comments

Hi @Nickolaus. Forgive my ignorance, but what part of my code I can check? I assigned it directly to the variable $ file controller and follows the same error message.
Post the error message... would be helpful to determine whats causing the error
The error message returned is: "The file could not be found" @Nickolaus, I think it's because Ajax have to send the file in another way, I've seen out there, but do not know. Without Ajax request it is saved correctly
Hi @Nickolaus, If you do not select file and leave blank the Ajax request is successful without deserializing in the controller, the problem is when I want to save the file. The value that contains the file serialize fields is "on", then by this fact is important. How do I deselelizo and then use that data in symfony?
Uploaded Files are not visible as values.... to deserialize use json_decode function
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.