Los formularios de Symfony 1.4

2.7. Subiendo archivos

La gestión de los archivos subidos con PHP, al igual que en otros lenguajes de programación orientados a la web, implica el uso de código HTML y de programación en el servidor. En esta sección se muestran las herramientas ofrecidas por el framework para simplificar el trabajo del programador y también se muestra cómo evitar los errores habituales.

A continuación se modifica el formulario de contacto para permitir adjuntar archivos al mensaje. Para ello, se añade un campo llamado archivo, tal y como muestra el listado 2-17.

Listado 2-17 - Añadiendo un campo archivo en el formulario ContactoForm

// lib/form/ContactoForm.class.php
class ContactoForm extends sfForm
{
  protected static $asuntos = array('Asunto A', 'Asunto B', 'Asunto C');

  public function configure()
  {
    $this->setWidgets(array(
      'nombre'  => new sfWidgetFormInput(),
      'email'   => new sfWidgetFormInput(),
      'asunto'  => new sfWidgetFormSelect(array('choices' => self::$asuntos)),
      'mensaje' => new sfWidgetFormTextarea(),
      'archivo' => new sfWidgetFormInputFile(),
    ));
    $this->widgetSchema->setNameFormat('contacto[%s]');

    $this->setValidators(array(
      'nombre'  => new sfValidatorString(array('required' => false)),
      'email'   => new sfValidatorEmail(),
      'asunto'  => new sfValidatorChoice(array('choices' => array_keys(self::$asuntos))),
      'mensaje' => new sfValidatorString(array('min_length' => 4)),
      'archivo' => new sfValidatorFile(),
    ));
  }
}

Cuando se utiliza un widget de tipo sfWidgetFormInputFile para permitir la subida de archivos, se debe añadir el atributo enctype en la etiqueta <form>, tal y como se muestra en el listado 2-18.

Listado 2-18 - Modificando la plantilla para tener en cuenta el campo archivo

<form action="<?php echo url_for('contacto/index') ?>" method="POST" enctype="multipart/form-data">
  <table>
    <?php echo $formulario ?>
    <tr>
      <td colspan="2">
        <input type="submit" />
      </td>
    </tr>
  </table>
</form>

Nota Si generas de forma dinámica las plantillas asociadas a los formularios, puedes emplear el método isMultipart() del objeto del formulario, ya que devuelve true si el formulario requiere el atributo enctype.

La información de los archivos subidos no se almacena junto con el resto de información enviada. Por eso, es necesario modificar la llamada al método bind() para pasar esa información como segundo parámetro, tal y como se muestra en el listado 2-19.

Listado 2-19 - Pasando los archivos subidos al método bind()

class contactoActions extends sfActions
{
  public function executeIndex($request)
  {
    $this->formulario = new ContactoForm();

    if ($request->isMethod('post'))
    {
      $this->formulario->bind($request->getParameter('contacto'), $request->getFiles('contacto'));
      if ($this->formulario->isValid())
      {
        $valores = $this->formulario->getValues();
        // hacer cosas con los valores

        // ...
      }
    }
  }

  public function executeGracias()
  {
  }
}

Aunque el formulario ya es completamente funcional, es necesario modificar la acción para almacenar el archivo subido en un directorio. Como se explica al principio de este capítulo, el validador sfValidatorFile convierte toda la información relacionada con el archivo subido en un objeto de tipo sfValidatedFile. El listado 2-20 muestra cómo utilizar este objeto para almacenar el archivo subido en el directorio web/uploads.

Listado 2-20 - Utilizando el objeto sfValidatedFile

if ($this->formulario->isValid())
{
  $archivo = $this->formulario->getValue('archivo');

  $nombreArchivo = 'subido_'.sha1($archivo->getOriginalName());
  $extension = $archivo->getExtension($archivo->getOriginalExtension());
  $archivo->save(sfConfig::get('sf_upload_dir').'/'.$nombreArchivo.$extension);

  // ...
}

La siguiente tabla muestra todos los métodos del objeto sfValidatedFile:

Método Descripción
save() Guarda el archivo subido
isSaved() Devuelve true si el archivo se ha guardado
getSavedName() Devuelve el nombre del archivo guardado
getExtension() Devuelve la extensión del archivo, en función de su tipo MIME
getOriginalName() Devuelve el nombre del archivo subido
getOriginalExtension() Devuelve la extensión del nombre del archivo subido
getTempName() Devuelve la ruta del archivo temporal
getType() Devuelve el tipo MIME del archivo
getSize() Devuelve el tamaño del archivo

Nota El tipo MIME que proporciona el navegador por cada archivo subido no es fiable. Para asegurar la máxima seguridad, durante la validación del archivo se ejecutan de forma secuencial las funciones finfo_open y mime_content_type y la herramienta file. Si ninguna de esas funciones es capaz de determinar el tipo MIME del archivo y si el sistema tampoco es capaz de proporcionarlo, se utiliza como último recurso el tipo MIME ofrecido por el navegador. Para añadir o modificar las funciones que tratan de averiguar el tipo MIME, se utiliza la opción mime_type_guessers en el constructor de sfValidatorFile.