I am writing a bundle that needs to have a file uploaded to server and later served, I am also using Sonata Admin bundle.
First thing I needed to understand is that I will need an additional variable in my entity that will be used for the FileType object and that variable WILL NOT BE MAPPED to doctrine.
<?phpclassProduct
{
protected $id;
...protected $imageName;
protected $file;
...publicfunctiongetAbsolutePath()
{
returnnull=== $this->imageName?null: $this->getUploadRootDir().'/'.$this->imageName;
}
publicfunctiongetWebPath()
{
returnnull=== $this->imageName?null: $this->getUploadDir().'/'.$this->imageName;
}
protectedfunctiongetUploadRootDir($basepath)
{
// the absolute directory path where uploaded documents should be saved
return $basepath.$this->getUploadDir();
}
protectedfunctiongetUploadDir()
{
// get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
return'uploads/products';
}
publicfunctionupload($basepath)
{
// the file property can be empty if the field is not required
if (null=== $this->file) {
return;
}
if (null=== $basepath) {
return;
}
// we use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the target filename to move to
$this->file->move($this->getUploadRootDir($basepath), $this->file->getClientOriginalName());
// set the path property to the filename where you'ved saved the file
$this->setImageName($this->file->getClientOriginalName());
// clean up the file property as you won't need it anymore
$this->file=null;
}
}
As you can see it is pretty straight forward and you can see it in the Manual as well.
I made a few changes to the way path is generated, I did not like the way it was done in the book, so I pass the base dir path to the class, easier for testing, and less hassle if it needs to be implemented elsewhere.
What I did not understand at first is that I can use any field type defined in Symfony2 not only those types defined by the Sonata Admin Bundle.
Once I had an a-ha moment, I just added the file type (line 8, second parameter) and done, I had a nice file upload field displayed.
Sonata admin bundle exposes the pre/post persist and pre/post update calls for us to use.
Because we need the file moved and named BEFORE persisting the entity to the DB we need to use the pre* calls,
prePersist call for the Create and preUpdate call for the Update/Edit.
With that I had a final piece of the puzzle.
I added a function that would take the $product param (which is a Product Entity), so that we do not have code duplication.
The base dir path is retrieved from the request, and passed into the upload function of the Product Entity.
Once you hit save button on the form, the appropriate pre* call will be made, file will be uploaded and moved to your location under the web directory, and the entity will be persisted into the db.