I’m still very new to Symfony 1.2 and its form framework. Reading through documentation, discussion forums and blog posts, I couldn’t find a tutorial which had everything I needed in an image upload. Having finished an image upload for the first time, I decided to write such a tutorial.
This tutorial requires Symfony 1.2, the Doctrine ORM plugin and the PHP GD library. You should already have all of these elements installed, and be familiar with them.
We’ll start at the beginning. I’m going to make an uploader for a background image to attach to a Website model. Make sure you have a field in your model to store the image file name:
Website:
columns:
background_image: string(255)
And rebuild your model:
symfony doctrine:build-all-reload
Now open the form class generated from your schema.
You’re going to add a widget and a validator for the image upload. The widget will also allow the user to view the image after it has been uploaded, and delete it if necessary. The validator will make sure the image upload is not required (it is required by default). There is also a second validator, sfValidatorPass, because the widget we are using adds a checkbox widget giving the user the option to delete the file.
You’ll also override the doSave() method and updateObject() methods. The doSave() method will handle the upload (if there is one), create a new file name for the image, and save the image both to the file system and the background_image field automatically. Since the background_image field is automatically set with the full path of the file, you will use updateObject() to remove the path and just store the file name. If you move your project to another system, the file path will change and so we want to store just the file name.
Also in doSave(), you will handle the case where the user has checked off the “delete image” checkbox, which is automatically named background_image_delete.
Here is the code so far under lib/form/doctrine/WebsiteForm.class.php:
class WebsiteForm extends BaseWebsiteForm
{
public function configure()
{
$this->widgetSchema['background_image'] = new sfWidgetFormInputFileEditable(array(
'file_src' => '/'.basename(sfConfig::get('sf_upload_dir')).'/'.$this->getObject()->getBackgroundImage(),
'is_image' => true,
'edit_mode' => strlen($this->getObject()->getBackgroundImage()) > 0,
'template' => '
<div>%file%
%input%
%delete% %delete_label%</div>
'
));
$this->validatorSchema['background_image'] = new sfValidatorFile(array(
'required' => false,
'mime_types' => 'web_images'
));
$this->validatorSchema['background_image_delete'] = new sfValidatorPass();
}
protected function doSave ( $con = null )
{
$upload = $this->getValue('background_image');
if ( $upload )
{
$filename = sha1($upload->getOriginalName().microtime().rand()).$upload->getExtension($upload->getOriginalExtension());
$filepath = sfConfig::get('sf_upload_dir').'/'.$filename;
$oldfilepath = sfConfig::get('sf_upload_dir').'/'.$this->getObject()->getBackgroundImage();
if ( file_exists($oldfilepath) )
{
unlink($oldfilepath);
}
$upload->save($filepath);
}
$delete = $this->getValue('background_image_delete');
if ( $delete )
{
$filename = $this->getObject()->getBackgroundImage();
$filepath = sfConfig::get('sf_upload_dir').'/'.$filename;
@unlink($filepath);
$this->getObject()->setBackgroundImage(null);
}
return parent::doSave($con);
}
public function updateObject($values = null)
{
$object = parent::updateObject($values);
$object->setBackgroundImage(str_replace(sfConfig::get('sf_upload_dir').'/', '', $object->getBackgroundImage()));
return $object;
}
}
This should work well for the most part. But what if the image is 2000 pixels in width? It will be very disruptive to display such a large image.
That’s where sfThumbnailPlugin comes in.
Install the plugin if you haven’t already done so:
$ symfony plugin:install sfThumbnailPlugin
$ php symfony cc
Now create a new directory to store your thumbnails:
$ mkdir /web/uploads/thumbnails
You’ll probably want to use this directory in other modules or applications in your project. Add a new setting called sf_thumbnail_dir in your project configuration:
config/ProjectConfiguration.class.php
...
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
...
sfConfig::set('sf_thumbnail_dir', sfConfig::get('sf_upload_dir').'/thumbnails');
...
}
}
Now all that’s left to do is create and store the thumbnail from the doSave() method of your form class, and make sure that your form displays the thumbnail and not the source image in your configure() method.
Modify the widget settings so that it displays the thumbnail. Note that I’ve …’d most of the parts that don’t change:
lib/form/doctrine/WebsiteForm.class.php
class WebsiteForm extends BaseWebsiteForm
{
public function configure()
{
...
$this->widgetSchema['background_image'] = new sfWidgetFormInputFileEditable(array(
'file_src' => '/'.basename(sfConfig::get('sf_upload_dir')).'/'.basename(sfConfig::get('sf_thumbnail_dir')).'/'.$this->getObject()->getBackgroundImage(),
'is_image' => true,
'edit_mode' => strlen($this->getObject()->getBackgroundImage()) > 0,
'template' => '
<div>%file%
%input%
%delete% %delete_label%</div>
'
));
...
}
protected function doSave ( $con = null )
{
$upload = $this->getValue('background_image');
if ( $upload )
{
...
$thumbnailpath = sfConfig::get('sf_thumbnail_dir').'/'.$filename;
...
$oldthumbnailpath = sfConfig::get('sf_thumbnail_dir').'/'.$this->getObject()->getBackgroundImage();
if ( file_exists($oldthumbnailpath) )
{
unlink($oldthumbnailpath);
}
...
$thumbnail = new sfThumbnail(150, 150, true, true, 75, 'sfGDAdapter');
$thumbnail->loadFile($filepath);
$thumbnail->save($thumbnailpath);
}
$delete = $this->getValue('background_image_delete');
if ( $delete )
{
...
$thumbnailpath = sfConfig::get('sf_thumbnail_dir').'/'.$filename;
@unlink($thumbnailpath);
...
}
}
...
}
And that’s all there is to it. Note that there is no template file here because the form framework handles the view of the form inputs.
Questions? Complaints? Leave a comment.
Here are some related resources:
Here are some other posts you might be interested in: