28
Abr

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:

  1. Instalar el plugin sfThumbnailPlugin
    symfony plugin:install sfThumbnailPlugin
  2. Tener configurada la BD, y el modelo implementado previamente con al menos un campo para guardar imágenes varchar(50).
  3. 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

, , , ,

33 Responses to “sfThumbnailPlugin y Admin Generator: redimensionar imágenes”

  • Omar

    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.

      • omar

        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.

  • omar

    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

  • ChikiCadiz

    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.

  • David

    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…)

  • Mimi

    Hola me da el siguiente error
    Fatal error: Class ‘sfResizedFile’ not found in /usr/share/pear/symfony/validator/sfValidatorFile.class.php on line 167

  • Mimi

    Bueno el error anterior se resolvio con esto $this->validatorSchema['imagen1_delete'] = new sfValidatorBoolean();

    imagen1 es el campo

  • jesus

    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

    • jesus

      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

        • jesus

          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)

  • Juanan

    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!

    • Juanan

      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

  • Agustin Gutierrez

    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

  • Agustin Gutierrez

    Ejecute
    symfony project:permissions y me tira el siguiente error:
    SF_ROOT_DIR/web/uploads/images

  • Agustin Gutierrez

    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

  • ALIT07

    Gracias David, Sos un ´pro!

  • morper

    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);
    }

  • Juanan

    Otra pregunta…..

    Como se puede hacer para que se guarde el fichero que se sube con su nombre original?

    Gracias

Add reply