Monday, September 28, 2009

Modificación de filtros en Symfony para un modulo de administración. Ejemplo sobre el módulo sfGuardUser del sfDoctrineGuardPlugin(Doctrine)

Dada la poca documentación al respecto, decidí publicar un ejemplo sobre modificación de los filtros para un módulo de administración en una aplicación de symfony. El ejemplo en concreto es sobre el plugin sfDoctrineGuardPlugin y su módulo sf_guard_user.

El detalle importante en este caso es que de los filtros que el módulo genera por default, probablemente sólo los de username, permissions y fechas son útiles, pero dado que el resto de campos que completan el perfil de un usuario van en otra tabla (Customize sfDoctrineGuardPlugin), se hace necesario crear nuevos filtros para los atributos del perfil que sean necesarios.


Ejemplo de schema del perfil:

Perfil:
  columns:
    codigo:
      type: string(10)
      primary: true
      notnull: true
    sf_guard_user_id:
      type: integer
    apellidos:
      type: string(40)
      notnull: true
    nombres:
      type: string(40)
      notnull: true
    sexo:
      type: string(1)
      notnull: true
    email:
      type: string(45)

    #La relación sfGuardUser fue creada manualmente para acoplar esta tabla al
    #plugin de manejo de usuarios sfDoctrineGuardPlugin.
  relations:
    sfGuardUser:
      local: sf_guard_user_id
      foreign: id
      foreignType: one
      onDelete: CASCADE



Agreguemos entonces un filtro para los nombres de los usuarios:

Debemos ubicar la clase sfGuardUserFormFilter.class.php dentro del directorio /lib/filter/doctrine/sfDoctrineGuardPlugin. Ahí agregaremos lo siguiente dentro de la función configure:

public function configure()
{
  //...

  //Widget para el filtro de nombres
  $this->widgetSchema['nombres'] = new sfWidgetFormFilterInput(array(
        'with_empty' => false
  ));
  $this->validatorSchema['nombres'] = new sfValidatorPass(array(
        'required' => false
  ));
}


Con esto hemos creado un widget para el atributo 'nombres' con su respectivo validator. El widget es un simple campo de entrada (sfWidgetFormFilterInput), en donde hemos desactivado con "'with_empty' => false", el checkbox 'is_empty'.

Posteriormente agregaremos la función para consultar sobre la base de datos los atributos que deben ser cargado desde la tabla perfil.

public function addNombresColumnQuery(Doctrine_Query $query, $field, $values)
{
  //Se comprueba que no sea nulo el valor del campo del filtro
  if ($values['text'] != '') {
    $query->from('sfGuardUser u')
      ->innerJoin('u.Perfil us')
      ->andWhere("us.nombres LIKE ?", '%'.$values['text'].'%');
  }

}


El nombre de la función debe ser addCampocualquieraColumnQuery.

En este caso se hace un Join interno entre las tablas sfGuardUser y perfil, agregando un WHERE para filtrar los registros que coincidan con el valor introducido en el campo de input. El valor es $values['text'] y los porcentajes a los lados son sintaxis de doctrine para permitir que la búsqueda no requiera la palabra exacta, y permita devolver resultados con sólo una parte de ella (Documentación doctrine)

Finalmente, se añade el nuevo atributo de filtro a la función getFields, que devuelve todos los campos por los que es posible filtrar:

public function getFields()
{
  return parent::getFields() + array(
      'nombres' => 'Text'

);


Se añade al array ya existente desde parent::getFields() en la clase autogenerada BasesfGuardUserFormFilter.class.php, el nuevo campo de tipo text.


Con esto sólo resta activar el nuevo campo de filtro en el archivo generator.yml del módulo sfGuardUser:

filter:
  display: [username, permissions_list, nombres]


Luego:

$ ./symfony cc

El filtro debería funcionar

10 comments:

jpromerobx said...

Muchas gracias José por el post, muy claro y de mucha ayuda para construir filtros personalizados.

Javier García said...

Gracias por el tutorial! me ha funcionado!

De todos modos tengo un par de dudas:

¿Por que usastes la funcion configure(){}?

¿La construcción del nombre de la función add+Campo+ColumnQuery es una convención?No encontre nada sobre ello...

JoseA said...

Hola jpromerobx gracias por sus comentarios.

tirengarfio, Use la función configure porque es allí donde se editan los widgets para un filtro o para un form. Así se maneja symfony en ese aspecto.

Sobre la función add+Campo+ColumnQuery imagino se trata de una convención. No hay documentación al respecto. Decidí hacer este tutorial porque entre pedazos de código de los grupos y foros logré que funcionara. Sin embargo, revisando en archivos del core, parece serlo.

Zacarías said...

José,

Muchísimas gracias por la aportación, muy interesante.

He utilizado tu idea pero he hecho algún cambio para permitir que el filtro de nombre se pueda añadir a otros filtros que ya existan al objeto, así es como queda:

public function addNombresColumnQuery(Doctrine_Query $query, $field, $values)
{
//Se comprueba que no sea nulo el valor del campo del filtro
if ($values['text'] != '') {
$a = $query->getRootAlias();
$query->innerJoin($a'.Perfil us')
->andWhere("us.nombres LIKE ?", '%'.$values['text'].'%');
}
}

De esta forma si el formulario de filtro tiene otro campo, por ejemplo estado, te permite lanzar una query con las dos condiciones, estado y nombre.

Saludos y gracias una vez más.

Unknown said...

Jose, muchas gracias por el aporte, de verdad que muy util y me ha quitado un quebradero de cabeza :)

Anonymous said...

Hola a todos.
lo primero, gracias por el aporte, hay muy poca documentación sobre esto y la explicación es bastante clara.
¿Alguien sabría decirme como hacer esto mismo pero con Propel?
Quiero crear un filtro "fecha" personalizado.

Gracias

Anonymous said...

Hola queria saber si se puede modificar el texto que dice is_empty

Marcos Jose said...

Una pregunta se puede cambiar el idioma de los botones, go, save, save and add, en donde puedo modificar esto?

Guillermo said...

Excelente justo lo que necesitaba, me sirvio para saber como manejar cualquier filtro.

Gracias

Harold Henriquez said...

JOse me puedes ayudar porfavor mira tengo este problema Parse error: syntax error, unexpected T_PUBLIC in C:\xampp\htdocs\tesis\lib\filter\doctrine\ClienteFormFilter.class.php on line 23

con esta linea

addNombresColumnQuery(Doctrine_Query $query, $field, $values)

necesito hacer un filtro de 2 tablas y no puedooo !


HELP ME

ocupo sf 1.4