rescue true

6 mayo, 2010

Buscador RESTful en Rails

Filed under: REST,Ruby on Rails — xurdekio @ 11:20 am
Tags: , , , , , ,

En este post trataré de explicar la forma mas limpia y rápida de construir un buscador RESTful en nuestra aplicación Rails.
El action encargado de devolver los resultados del buscador será el index y el verbo será GET puesto que es la convención para devolver información de listas de objetos. En nuestro ejemplo vamos a trabajar sobre un buscador de noticias, que buscará por texto (en los campos titulo, entradilla, y texto) y entre fechas en el campo created_at.
El partial del buscador quedará tal que así

	<%form_tag({:controller => :noticias, :action => :index}, :method => :get) do%>
                <div>
                     <%= label_tag "busqueda_texto", "Texto "%>
                     <%= text_field_tag "busqueda[texto]" %>
                </div>
                <div>
                     <%= label_tag "busqueda_desde", "Fecha desde "%>
                     <%= date_select "busqueda[desde]" %>
                </div>
                <div>
                     <%= label_tag "busqueda_hasta", "Fecha hasta "%>
                     <%= date_select "busqueda[hasta]" %>
                </div>
		<%=submit_tag("Buscar")%>
	<%end%>

Esto nos dejara las opciones de búsqueda en el hash params[:busqueda].

Mi intención es dejar el controlador lo más limpio posible, y que de la lógica de búsqueda se encargue el modelo, de esta forma podemos reaprovechar el buscador siempre que nos haga falta desde otras actions u otros controladores.

  PER_PAGE = 10
  def index
    #Le pedimos al modelo que nos transforme los parametros de busqueda en condiciones sql
    conditions = Noticia.conditions_from_params(params[:busqueda])
    #Cargamos las noticias paginadas con will_paginate
    @noticias = Noticia.paginate(:all, :conditions => conditions, :page => params[:page], :per_page => PER_PAGE)

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @noticias }
    end
  end

Y por último, el construir las conditions en el modelo

  def self.conditions_from_params(params)
    #Si llegan en blanco, nil, no hay condiciones de búsqueda
    return nil if params.blank?
    
    #columnas para buscar por texto 
    text_search_cols = ["titulo", "entradilla","texto"]
    
    conditions = [] #incializamos las conditions
    #Creamos las condiciones de búsqueda por texto
    if text_search_cols.any? and !params[:text].blank?
      text = "%#{params[:text]}%" #Texto con comodines
      #Importante usar Noticia.sanitize_sql_array para protegernos del sql injection
      conditions << text_search_cols.map{|x| Noticia.sanitize_sql_array(["#{x} like ?", text])}.join(" OR ") # col1 like '%text%' or col2 like '%text%'...
    end
    
    #condiciones de búsqueda por fecha
    conditions << Noticia.sanitize_sql_array(["created_at >= ?", Date.new(params["desde(1i)"].to_i,params["desde(2i)"].to_i,params["desde(3i)"].to_i)]) unless params[:desde].blank?
    conditions << Noticia.sanitize_sql_array(["created_at <= ?", Date.new(params["hasta(1i)"].to_i,params["hasta(2i)"].to_i,params["hasta(3i)"].to_i)]) unless params[:hasta].blank?

    #Por ultimo juntamos las condiciones para que queden de la forma (sql_query_cond1) AND (sql_query_cond2)...
    conditions.map{|x| "(#{x})"}.join(" AND ")  
  end

Como podeis ver añadir mas opciones de búsqueda al buscador es ahora extremadamente fácil, solo necesitais añadir el input al partial y crear la condición sql oportuna en el modelo.
Una ventaja añadida es mantener la aplicación DRY, puesto que no repetiremos constantemente el sql necesario para filtrar noticias, por ejemplo para contar las noticias de un día:

Noticia.count(:conditions => Noticia.conditions_from_params(:desde => dia, :hasta => dia))

Pues hala, lo dicho, a experimentar con vuestros propios buscadores!

Dejar un comentario »

Aún no hay comentarios.

RSS feed for comments on this post. TrackBack URI

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Crea un blog o un sitio web gratuitos con WordPress.com.

A %d blogueros les gusta esto: