Esta es una segunda entrega acerca del trabajo con los formularios de filtro en Symfony. He tenido en cuenta los comentarios y sugerencias de los visitantes del blog, así que esta entrega integrará el uso de Doctrine.
Antes de comenzar, aclaro que mucha de la información aquí consignada está basada en el código generado por el generador de administración tanto de Propel como de Doctrine y desde luego en la documentación del API de Symfony 1.2.
Filtros con Doctrine
En la entrada anterior hablé del uso de los formularios con Propel de manera básica y aunque las forma de trabajar los formularios de filtros con Doctrine no es muy diferente, haré algunas aclaraciones importantes.
Al momento de generar el modelo con Doctrine, con la tarea:
php symfony doctrine:build-all
al igual con con Propel, se genera en el directorio lib/filter/doctrine/ el conjunto de clases de formularios de filtro. Tomando como referencia el esquema de datos del tutorial de Jobeet (Día 3 – http://www.symfony-project.org/jobeet/1_2/Doctrine/en/03) los filtros generados son:
BaseFormFilterDoctrine.class.php JobeetAffiliateFormFilter.class.php JobeetCategoryAffiliateFormFilter.class.php JobeetCategoryFormFilter.class.php JobeetJobFormFilter.class.php base/
Estas clases podemos modificarlas según nuestras necesidades, al igual que con los formularios. Ahora, vamos a generar el módulo que permita el CRUD de las ofertas de trabajo (Tabla Jobeet_Job):
php symfony doctrine:generate-module frontend job JobeetJob
Bien, ahora con el módulo generado, vamos a revisar las acciones y hacer los ajustes para el uso de los filtros. En primera forma, debemos modificar la acción index, para que nos presente el formulario de filtros:
class jobActions extends sfActions // apps/frontend/modules/job/actions/actions.class.php { public function executeIndex(sfWebRequest $request) { $this->jobeet_job_list = Doctrine::getTable('JobeetJob') $this->createQuery('a') $this->execute(); $this->filtro = new JobeetJobFormFilter(); } // más código .....
Al igual que con Propel, debemos crear una instancia del filtro, para que sea visualizada en la plantilla:
// apps/frontend/modules/job/templates/indexSuccess.php <h1>Lista de trabajos</h1> <h1>Lista de trabajos</h1> <form action="<?php echo url_for('job/filter'); ?>" method="post"> <input class="ui-state-default ui-corner-all" type="submit" value="Filtrar" /> '_reset', 'method' => 'post')); ?> </form> <!-- más código html ... -->
Creamos nuestra acción encargada de filtrar los resultados (filter):
// apps/frontend/modules/job/actions/actions.class.php public function executeFilter(sfWebRequest $request) { $this->filtro = new JobeetJobFormFilter(); $this->consulta = $this->filtro->buildQuery($request->getParameter('jobeet_job_filters')); $this->jobeet_job_list = $this->consulta->execute(); $this->setTemplate('index'); }
Una de las diferencias importantes con Propel, es el método que se encarga de construir la consulta según el valor de los filtros, que es buildQuery y que recibe una arreglo de valores a partir de los cuales se construirá la consulta. Este método devuelve un objeto de tipo Doctrine_Query, por eso llamamos al método execute en forma posterior.
Con lo anterior tenemos un sistema de filtros muy básico usando Doctrine como ORM.
Adaptando el filtro
Hasta el momento, ya hemos visto como establecer un filtro, tanto si usamos Propel como Doctrine. Sin embargo, la mayoría de las veces los campos por los cuales deseamos filtrar información no corresponden con todos los de la tabla. El filtro por defecto que se genera contempla todos los campos y por ello es necesario que hagamos algunas modificaciones, para filtrar por los campos deseados.
Siguiendo con el ejemplo del tutorial de Jobeet, supongamos que sólo queremos filtrar la lista de trabajos, por categoría y tipo (campos category y type, respectivamente), para ello debemos modificar el método configure de la clase JobeetJobFormFilter:
// lib/form/doctrine/JobeetJobFormFilter.class.php class JobeetJobFormFilter extends BaseJobeetJobFormFilter { public function configure() { $this->setWidgets(array( 'category_id' => new sfWidgetFormDoctrineChoice(array('model' => 'JobeetCategory', 'add_empty' => true)), 'type' => new sfWidgetFormFilterInput() )); $this->setValidators(array( 'category_id' => new sfValidatorDoctrineChoice(array('required' => false, 'model' => 'JobeetCategory', 'column' => 'id')), 'type' => new sfValidatorPass(array('required' => false)) )); $this->widgetSchema->setNameFormat('jobeet_job_filters[%s]'); } }
Cómo se puede observar, un formulario de filtro es muy similar a un formulario, con algunas diferencias en cuanto a los widgets que se usan y algunos de los métodos. Con esta modificación, cuando imprimamos el filtro en la plantilla, ahora sólo los campos de categoría y tipo estarán disponibles. También podemos usar los métodos setLabel y setLabels para cambiar el nombre de las etiquetas que aparecerán al momento de imprimir el formulario de filtros, por ejemplo:
$this->getWidgetSchema()->setLabels(array( 'category_id' => 'Categoria', 'type' => 'Tipo' ));
Uno de los widgets de filtros que considero más útiles es sfWidgetFormFilterDate, ya que con él es posible filtrar información por rangos de fecha, aquí un ejemplo de su uso para el campo de expiración de la oferta de trabajo (campo expires_at):
'expires_at' => new sfWidgetFormFilterDate(array( 'from_date' => new sfWidgetFormDate(), 'to_date' => new sfWidgetFormDate(), 'with_empty' => 0, // Para que no salga el checkbox 'template' => 'desde %from_date% hasta %to_date%' ))
Bien, hasta aquí esta segunda entrega del trabajo con filtros en Symfony 1.2. Espero que sea de utilidad.

Excelente Juan, muchas gracias por tomarte la molestia de adaptar el ejemplo para los que trabajamos con Doctrine… voy a ponerme ahora a elaborar los ejemplos como tal como indicas
De nada Moises. Espero que los ejemplos hayan sido de utilidad
Saludos Juan, gracias por tu artículo está muy útil y bien explicado.
Estuve trabajando con filtros en Doctrine para el SfDoctrineGuardPlugin y recien acabo de publicar un post sobre como agregar nuevos atributos de otra tabla a los filtros (caso del modulo sfGuardUser).
Acá dejo el link por si a alguien le puede servir:
http://jsangil.blogspot.com/2009/09/modificacion-de-filtros-en-symfony-para.html
Gracias!
Muchas gracias por el aporte. Muy bien explicado al igual que el anterior
me fue de mucha ayuda.
María, gracias por tu comentario. Espero publicar pronto algunos tips adicionales.
Hola, muy interesante, pero hay un tema que no encuentro por ningún sitio y quizás sepas responder.
Cómo se puede hacer, para que el campo de un filtro, por defecto tenga un valor prefijado y se realice el filtro sobre ese valor.
Por ejemplo: El campo “Estado”, que puede tomar 2 valores “Abierto” o “Cerrado”, y queremos que por defecto aparezca filtrado por “Abierto”.
Un saludo.
Puedes usar el método setDefault, al igual que si fuera un formulario normal, por ejemplo:
$this->setDefault(‘Estado’,'Abierto’);
Lo cual se traduce en que el filtro, en el campo estado tendrá por defecto el valor Abierto.
Espero sea de utilidad. Realicé la prueba sobre Symfony 1.2.8, aunque supongo debe funcionar en 1.3 y 1.4
Sí pero no funciona. Haciendo un setDefault, preestablece el filtro, pero no lo ejecuta (v. 1.2.8)
Te paso la forma en que lo he resuelto en otras ocasiones, pero no sé si existe una forma más elegante:
Redefinimos el método configure del formulario de filtrado de forma que queda así:
public function configure()
{
// .. Tu código
// Filtramos por el estado
$usr = sfContext::getInstance()->getUser();
$filter = $usr->getAttribute(‘incident.filters’, array(), ‘admin_module’);
if (! isset($filter['status']))
{
$filter['status'] = ‘A’;
$this->setDefault(‘status’, $filter['status']);
$usr->setAttribute(‘incident.filters’, $filter, ‘admin_module’);
}
// Sigue tu código.
}
Un saludo
Muchas gracias por el post Juan Pablo, tengo una consulta, como es que funciona el reset?
No debería interpretarlo en el actions?
Desde ya muchas gracias.
Hola,
en los filtros no me aparece el botón “Filter”…
Alguna idea?
Un saludo
Javi
Me contesto yo mismo:
en vuestro primer articulo
Hola,
seguí vuestro artículo pero tengo problemas cuando uno de los campos de la tabla para que que quiero crear el filtro es una fecha (tipo “date”).
Me sale el siguiente error:
500 | Internal Server Error | Doctrine_Connection_Mysql_Exception
SQLSTATE[HY093]: Invalid parameter number: number of bound variables
does not match number of tokens
stack trace
He expuesto mi problema en la lista de Sf pero nada…En el post que escribí expongo los pasos que doy a través de una clase que solo tiene un campo para hacerlo mas comprensible:
http://groups.google.com/group/symfony-users/browse_thread/thread/6b4ad942a3611a92/40d3e18d55322d2b?lnk=gst&q=tirengarfio#40d3e18d55322d2b
Ojala me podaís echar una mano, porque es una parte crítica de mi proyecto..
Un saludo
Javi
Hola excelente tutorial:
Pero creo que para que funcione con doctrine deberias hacer el action filter asi:
$this->filtro = new JobeetJobFormFilter();
$this->filtro->bind($request->getParameter(‘jobeet_job_filters’));
if ($this->filtro->isValid())
{
$this->consulta = $this->filtro->buildQuery($this->filtro->getValues());
$this->books = $this->consulta->execute();
}
$this->setTemplate(‘index’);
Ya que sino dara errores con modelos Timestampables
Saludos
Opps!!! se me fue la variable book que era de mis pruebas: asi seria
$this->filtro = new JobeetJobFormFilter();
$this->filtro->bind($request->getParameter(‘jobeet_job_filters’));
if ($this->filtro->isValid())
{
$this->consulta = $this->filtro->buildQuery($this->filtro->getValues());
$this->jobeet_job_list = $this->consulta->execute();
}
$this->setTemplate(‘index’);
Ahora si
Saludos
Algo que me ha interesado en los últimos días es intentar agregar nuevos widgets en el filtro, que aunque sus valores no estan alineados con una columna del modelo, hace uso de una o más columnas para tomar un valor. Por ejemplo:
Un status X que toma un valor: ‘Abierto’, ‘En Revisión’, ‘Aceptado’, ‘Rechazado’, etc. dependiendo de los valores de una o más columnas del modelo.
¿Cómo se podría hacer algo así?
Agradezco mucho la información a Juan Pablo, ha sido una gran ayuda.
Tambien al comentario de oyepez003, que con lo que puso, me pudo dar el filtro..
Hola, queria saber si me podes decir como cambiar los labels is_empty que coloca automaticamente symfony. Lo mismo me pasa en la fecha que me crea from y to, quiero cambiarlo por desde y hasta
Gracias
Hola, he seguido el ejemplo y logré implementar el filtro para una clase a la perfeccion! Muchas gracias!
Podrías hacer algún aporte sobre cómo agregar campos de otras tablas al filtro? Es decir, por ejemplo, suponte que tengo una tabla Personas donde guardo ID, Nombre, Sexo, Fecha de Nacimiento y otra tabla Materias donde guardo ID, Nombre, Descripción y quisiera agregar al filtro de Personas un campo Materias.Nombre para poder, por ejemplo, obtener todas las personas del sexo Femenino que cursan Matemáticas o todas las personas mayores de 25 años que cursan Lengua, etc. Espero puedas ayudarme! Esto me ha resultado imposible de implementar!
Agustín y Alsalomon pronto publicaremos un tutorial más detallado sobre filtros personalizados con Symfony. Muchas gracias por sus comentarios
Y como se aplica a resultados paginados…y que continuen con la paginación?????
(Filtros con paginado)
Epale gente la solucion a mi problema…esta aqui:
http://forum.symfony-project.org/viewtopic.php?f=3&t=25728&p=94521&hilit=Using+filter+forms#p94521
Ahora para hacerlo persistente usen esto adaptandolo a Doctrine:
http://www.symfony-project.org/tutorial/1_2/en/whats-new#chapter_3fb3b0c857177a2b6740a5dcbc0fb8c7_filters
que lo aprovechen
Muchas gracias! Me sirvió como ejemplo para comenzar con los filtros en Symfony
Muy agradecido por este post, saludos desde Venezuela!, me ayudo bastante para agilizar el trabajo que tenia atrasado, 20ptos!!
Hola, tengo un problema con la parte de codigo que se agrega al archivo indexSuccess()
…………….
‘_reset’, ‘method’ => ‘post’)); ?>
………………
Me toma la parte del reset como texto plano.
‘_reset’, ‘method’ => ‘post’)); ?>
alguien me podria ayudar con esto?? tengo que agregar el partial en algun lado?, de todos modos no lo encuentro en ninguna parte del proyecto.
Muchas gracias