En este artículo mostraré una implementación del plugin sfThumbnailPlugin para redimensionar imágenes. Idealmente usado para la creación de las previsualizaciones de fotografías en nuestros sitios web.
Lo necesario:
- Instalar el plugin sfThumbnailPlugin
symfony plugin:install sfThumbnailPlugin - Tener configurada la BD, y el modelo implementado previamente con al menos un campo para guardar imágenes varchar(50).
- Tener un módulo creado con el “Admin generator” de Symfony
Procedimiento
Primero, configuraremos el formulario en la clase correspondiente. Tomaré por ejemplo un módulo para subir noticias a un sitio web.
Widget
Usaremos el widget para subir archivo editable sfWidgetFormInputFileEditable, ya que este nos permite mostrar la imagen que se ha subido además de dar la opción de eliminarla cuando existe.
NoticiaForm.class.php
$this->widgetSchema['imagen'] = new sfWidgetFormInputFileEditable(array( 'label' => 'Imagen Principal', 'file_src' => '/uploads/noticias/thumbs/'.$this->getObject()->getImagen(), 'is_image' => true, 'edit_mode' => !$this->isNew(), 'template' => '<div>%file%<br /><label></label>%input%<br /><label></label>%delete% Eliminar imagen actual</div>', ));
Explicación de lo anterior:
- Label nos permite modificar la etiqueta del campo en el formulario.
- File_src provee la ruta a la imagen ya subida, para mostrarla.
- Is_image, si no me equivoco le permite a Symfony conocer que estamos trabajando con una imagen (si alguien sabe su utilidad, comente).
- Edit_mode hace que la imagen sólo se muestre una vez que hayamos creado el registro, por ende cuando se ha subido ya algo.
- Template, es el formato con el que queremos mostrar la previsualización de la imagen.
Validator
El validador es el que finalmente sube y guarda la imagen, por lo tanto es donde debemos usar el plugin sfThumbnailPlugin.
NoticiaForm.class.php
$this->validatorSchema['imagen'] = new sfValidatorFile(array(
'required' => false,
'mime_types' => 'web_images',
'path' => sfConfig::get('sf_upload_dir').'/noticias/original',
'validated_file_class' => 'sfResizedFile',
));
Explicación.
- Required, ya sabemos.
- Mime_types permite decir qué archivos se pueden subir, los que son permitidos, en este caso sólo imágenes.
- Path es usado para indicar la ruta donde queremos que se guarden las imágenes (de tamaño original, o ampliado).
- Validated_file_class es el parámetro importante aquí, nos permite decir que usaremos una clase propia para manejar la imagen subida.
sfResizedFile es una clase nueva, la cual obtuve desde el Foro de Symfony. Créditos para malas.
/lib/sfResizedFile.class.php
/**
* sfResizedFile represents a resized uploaded file.
*
* @package symfony
* @subpackage validator
* @author Malas
* @version 0.1
*/
class sfResizedFile extends sfValidatedFile
{
/**
* Saves the uploaded file.
*
* This method can throw exceptions if there is a problem when saving the file.
*
* If you don't pass a file name, it will be generated by the generateFilename method.
* This will only work if you have passed a path when initializing this instance.
*
* @param string $file The file path to save the file
* @param int $fileMode The octal mode to use for the new file
* @param bool $create Indicates that we should make the directory before moving the file
* @param int $dirMode The octal mode to use when creating the directory
*
* @return string The filename without the $this->path prefix
*
* @throws Exception
*/
public function save($file = null, $fileMode = 0666, $create = true, $dirMode = 0777)
{
if (is_null($file))
{
$file = $this->generateFilename();
}
if ($file[0] != '/' && $file[0] != '\\' && !(strlen($file) > 3 && ctype_alpha($file[0]) && $file[1] == ':' && ($file[2] == '\\' || $file[2] == '/')))
{
if (is_null($this->path))
{
throw new RuntimeException('You must give a "path" when you give a relative file name.');
}
$smallFile = $this->path.DIRECTORY_SEPARATOR.'s_'.$file;
$file = $this->path.DIRECTORY_SEPARATOR.$file;
}
// get our directory path from the destination filename
$directory = dirname($file);
if (!is_readable($directory))
{
if ($create && !mkdir($directory, $dirMode, true))
{
// failed to create the directory
throw new Exception(sprintf('Failed to create file upload directory "%s".', $directory));
}
// chmod the directory since it doesn't seem to work on recursive paths
chmod($directory, $dirMode);
}
if (!is_dir($directory))
{
// the directory path exists but it's not a directory
throw new Exception(sprintf('File upload path "%s" exists, but is not a directory.', $directory));
}
if (!is_writable($directory))
{
// the directory isn't writable
throw new Exception(sprintf('File upload path "%s" is not writable.', $directory));
}
// copy the temp file to the destination file
$thumbnail = new sfThumbnail(100, 100, true, true, 85, 'sfGDAdapterCuttingOff');
$thumbnail->loadFile($this->getTempName());
$thumbnail->save($smallFile, 'image/jpeg');
$thumbnail = new sfThumbnail(400, 400, true, true, 85, 'sfGDAdapter');
$thumbnail->loadFile($this->getTempName());
$thumbnail->save($file, 'image/jpeg');
// chmod our file
chmod($smallFile, $fileMode);
chmod($file, $fileMode);
$this->savedName = $file;
return is_null($this->path) ? $file : str_replace($this->path.DIRECTORY_SEPARATOR, '', $file);
}
}
Para crear nuestros thumbnails, solo sebemos agregar cada uno como se muestra en negrita:
$thumbnail = new sfThumbnail ($maxWidth = null, $maxHeight = null, $scale = true, $inflate = true, $quality = 75, $adapterClass = null, $adapterOptions = array())
Si necesitamos 5 tamaños diferente, debemos agregar 5 veces la creación y guardado de un objeto sfThumbnail.
Finalmente, limpiar la cache del proyecto y probar cómo funciona el redimensionado de imágenes.
NOTA: Si aparece el error de que no existe sfGDAdapterCuttingOff, simplemente eliminar esa declaración. Se debe a que no está completamente instalado GD. En mi caso funcionó sin necesidad de usar ese parámetro.
Visto en Foro de Symfony



Hice todo lo indicado y me apareció el error que mencionas al final de que no existe sfGDAdapterCuttingOff y no se que parámetro es el que debo eliminar. Despúes de eso eliminando algunos parámetros del método del constructor logré que me guardara la imagen original y el thumbnail, pero me lo guardan en una misma carpeta. Y por último no se como mostrar el thumbnail en una página.
Omar,
Para lo del error. La declaracion deberia quedar asi:
$thumbnail = new sfThumbnail(100, 100, true, true, 85);
Respecto a lo de guardarlo en otra carpeta. En la clase sfResizedFile, busca lo siguiente:
$smallFile = $this->path.DIRECTORY_SEPARATOR.’s_’.$file;
$file = $this->path.DIRECTORY_SEPARATOR.$file;
Ahi estas definiendo el path. En este caso, sale en la misma carpeta, pero le agrega un “s_” al nombre. Puedes hacer algo como:
$smallFile = $this->path.DIRECTORY_SEPARATOR.’miniaturas’.DIRECTORY_SEPARATOR.$file;
Y para mostrar la imagen en la pagina:
image_tag(‘/uploads/PATH/miniaturas/’.$objeto->getImagen()
Espero haberte orientado y no confundido mas jejeje.
disculpa pero el problema es que no se donde poner esa linea
$thumbnail = new sfThumbnail(100, 100, true, true, 85);
yo hice todo lo que tu indicas hasta la parte que hay que crear el thumbnail. y no puse la linea
$thumbnail = new sfThumbnail(100, 100, true, true, 85);
en ningun lugar y cuando lo probe que subi la imagen me dio ese error que tu mencionastes, no existe sfGDAdapterCuttingOff, yo fui a la clase donde estaba el error o sea sfThumbnail.class.php linea 61 o sea el constructor
$this->adapter = new $adapterClass($maxWidth, $maxHeight, $scale, $inflate, $quality, $adapterOptions);
y cuando elimine esa linea me salieron 2 errores mas y entonces no se por fin que es lo que tengo que cambiar
disculpa la molestia y gracias de antemano
Tranquilo. Mira.
En el archivo /lib/sfResizedFile.class.php que creamos,
busca esta linea (es lo que esta en este post mas arriba):
// copy the temp file to the destination file
$thumbnail = new sfThumbnail(100, 100, true, true, 85, ‘sfGDAdapterCuttingOff’);
En esa linea, solo quita ‘sfGDAdapterCuttingOff’, si no te funciona, dejalo como sigue:
$thumbnail = new sfThumbnail(100, 100, true, true, 85, ‘sfGDAdapter’);
Si te fijas, solo estamos omitiendo o cambiando el parametro $adapterOptions de la linea en la que PHP encuentra el error, pero que en realidad no es donde se produce.
Saludos.
muchisimas gracias funciono perfectamente. Si no es mucha
molestia tambien necesito ayuda con una funcion para imprimir un prestamo (un modulo de mi aplicacion) y no se como mandarlo a imprimir, me recomendaron el sfTCPDFplugin
pero a lo mejor tu sabes alguna manera de hacerlo o conoces de algun foro donde postear mis dudas.
de nuevo muchisimas gracias hermano
Aun no he tenido que mandar a imprimir. Quiza si debas documentarte respecto al plugin sfTCPDFPlugin.
Todas tus dudas puedes resolverlas en la comunidad en español de symfony, responden muy rapido:
http://groups.google.com/group/symfony-es
Saludos
ok muchas gracias
Hola!! Ante todo gracias por tu aporte que me ha sido muy útil en cierta medida, y digo esto porque realmente lo que necesito es guardar dicha imagen en la base de datos donde residen los datos d emi aplicación. ¿Podrías ayudarme en cómo hacer esto?
Saludos.
Yo utilizo la librería:
http://phpthumb.gxdlabs.com/
que os la recomiendo. Con el plugin que trata me encontré con el problema que sólo podía cargar las imágenes loadFile mediante un argumento de tipo url web (http://www…)
Hola me da el siguiente error
Fatal error: Class ‘sfResizedFile’ not found in /usr/share/pear/symfony/validator/sfValidatorFile.class.php on line 167
Lee bien la entrada, ahí está el código de esa clase, que hay que crear manualmente.
Saludos.
Si funciono pero ahora me sale el siguiente error cuando elimino la imagen con el check
Unexpected extra form field named “imagen1_delete”.
Bueno el error anterior se resolvio con esto $this->validatorSchema['imagen1_delete'] = new sfValidatorBoolean();
imagen1 es el campo
Gracias por la aclaración
Y como borras la imágen del thumbnail?
He puesto lo que comentas y ciertamente borra la imagen, pero no el thumbnail
Gracias
buenas david!!! mira tengo el siguente error:
Fatal error: Class ‘sfResizedFile’ not found in C:\symfony-1.4.3\lib\validator\sfValidatorFile.class.php on line 167
puede ser porke no generé el modulo con el “admin generator”??
que puedo hacer para solucionarlo??
gracias de antemano
ui!! veo ke es el mismo error ke tubo mimi,,, pero no entiendo tu respuesta… ke codigo es el ke hay que introducir manualmente?? disculpa por las molestias!! jejeje
En el post se explica cómo crear los thumbnails con una clase denominada sfResizedFile, la cual no es una clase de Symfony, sino una que hay que crear.
Es decir, el archivo /lib/sfResizedFile.class.php hay que crearlo y pegar el código que está en este mismo post.
Lee completo primero para que entiendas la idea de lo que se está haciendo.
Espero haberte ayudado.
Saludos
gracias eres un crak!!! el problema era ke en el directorio
/lib/sfResizedFile.class.php no me encotraba la clase, he tenido que ponerla en /lib/from/doctrine/sfResizedFile.class.php y ahí si me la encuentra.(no se por ke sera…) pero bueno, problema solucionado… jejeje
gracias por tu ayuda y por tus post!! ya te tengo en mis favoritos!!!
Gracias por tu aporte!!! Me ha sido de mucha ayuda.
Hay algo que me parece interesante agregar. Espero que este bien y que le sirva a alguien…
Luego de agregar una imagen con otros tamaños ademas del original, intentaba eliminar las imagenes creadas, pero solamente se eliminaba la original. Entonces agregue el siguiente método en noticiaForm.class, luego del método config:
public function doSave($con = null)
{
//si elimino la imagen con check
if($this->getValue(‘nombrefoto_delete’))
{
$filename = $this->getObject()->getNombrefoto();
//directorio de la imagen original
$filepath = sfConfig::get(‘sf_upload_dir’).’/fotos/’.$filename;
@unlink($filepath);
//directorio de la imagen con tamaño distinto
$thumbnailpath = sfConfig::get(‘sf_upload_dir’).’/fotos/miniatura/’.$filename;
@unlink($thumbnailpath);
$this->getObject()->setNombrefoto(null);
}
return parent::doSave($con);
}
‘nombreFoto’ es el campo de la tabla que hace referencia a la imagen
De esta manera no solo se elimina la imagen original,sino que también la/s de tamaño distinto.
Hola David, excelente tu Post! te hago una consulta. Soy un principiante de Symfony, pero quería saber si los thumbs no se podrían generar también en el doSave del form…
Que opinas?
Desde ya muchísimas gracias!
Viva Symfony! la calidad y la simplicidad (que son parecidas jeje)
Hola,
Esta solución es válida para la versión 1.4?
No termino de encontrar documentación sobre este tema de subir imágenes con el admin generator.
Gracias y saludos!
Dos cosas…..
1.- Gracias por el aporte, que he ido al grano y te no he dado las gracias. Un post de gran utilidad.
2.- Confirmo que funciona en la versión 1.4.4. No me estaba funcionando porque no me había dado cuenta que al copiar la definición de la clase sfResizedFile en el fichero….había machacado la etiqueta <?php
Saludos y gracias de nuevo
Hola Queria saber como puedo hacer para que me muestre la imagen pequeña creada por el thumbnail. Primero probe poniendo en distintas carpetas, como explicaste en un comentario mas arriba pero me tira estos warning
Warning: imagejpeg() [function.imagejpeg]: Unable to open ‘/home/agustin/web/i4d/web/uploads/images/original/thumbs/s_6e39981024a564bc7d222c1c4d2baff0d9bf7ddf.jpg’ for writing: No such file or directory in /home/agustin/web/i4d/plugins/sfThumbnailPlugin/lib/sfGDAdapter.class.php on line 162
Warning: chmod() [function.chmod]: No such file or directory in /home/agustin/web/i4d/lib/sfResizedFile.class.php on line 82
Warning: Cannot modify header information – headers already sent by (output started at /home/agustin/web/i4d/plugins/sfThumbnailPlugin/lib/sfGDAdapter.class.php:162) in /home/agustin/web/i4d/lib/vendor/symfony/lib/response/sfWebResponse.class.php on line 357
Warning: Cannot modify header information – headers already sent by (output started at /home/agustin/web/i4d/plugins/sfThumbnailPlugin/lib/sfGDAdapter.class.php:162) in /home/agustin/web/i4d/lib/vendor/symfony/lib/response/sfWebResponse.class.php on line 357
Despues de ellos la ejecucion sigue normalmente, pero no me muestra las imagenes, ni las guarda
Y ademas tampoco me anda el link eliminar imagen
Gracias
Ejecute
symfony project:permissions y me tira el siguiente error:
SF_ROOT_DIR/web/uploads/images
Lo solucione guardando las dos imagenes en la misma carpeta, pero yo
querria que se mostrara la imagen reducida, la que tiene el nombre
s_….
y no la de tamaño mas grande. Ademas queria ver como hacer para que el
‘template’ => ‘%file%%input%%delete% Eliminar imagen actual’, funcione ya
que solo me aparece el texto pero no hace nada
Gracias David, Sos un ´pro!
HOla¡
quería saber como poder redimensionar las imagenes, pero que no cada una dependiendo del tamaño de la imagen original, se redimensione de un tamaño u otro, me gustaría que todas las imagenes, sean como sean, se me redimensionen con el tamaño 30×35, es posible???
Muchas gracias me sirvio de mucho!!! solo falto añadirle el validador del delete, en mi caso es el siguiente
$this->validatorSchema['avatarurl_delete'] = new sfValidatorPass();
Para poder eliminar todas las imagenes. Como ya mencionaron edite el metodo doSave en mi caso en la clase del formulario “class PatientExperienceForm extends BasePatientExperienceForm”
public function doSave($con = null)
{
/*Delete*/
if($this->getValue(‘avatar_url_delete’)){
@unlink(sfConfig::get(‘sf_upload_dir’).’/images/patientexperience_preview/mini_’.$this->getObject()->getAvatarUrl());
@unlink(sfConfig::get(‘sf_upload_dir’).’/images/patientexperience_preview/medium_’.$this->getObject()->getAvatarUrl());
}
/* Update */
if($this->getValue(‘avatar_url’)){
@unlink(sfConfig::get(‘sf_upload_dir’).’/images/patientexperience_preview/mini_’.$this->getObject()->getAvatarUrl());
@unlink(sfConfig::get(‘sf_upload_dir’).’/images/patientexperience_preview/medium_’.$this->getObject()->getAvatarUrl());
}
return parent::doSave($con);
}
Otra pregunta…..
Como se puede hacer para que se guarde el fichero que se sube con su nombre original?
Gracias