2010-01-28 10 views
0

J'ai une méthode qui récupère les groupes présents dans certaines zones. Les groupes sont donnés par country_id, region_id et city_id.Optimisation de l'ignorance des variables non définies lors de la création de conditions de recherche dans Rails

L'interface utilisateur donne trois zones de sélection pour choisir un pays, une région de ce pays, puis une ville de cette région. Pour trouver tous les groupes dans une ville, j'ai ce code:

@groups = Group.find(:all, :conditions => {:city_id => params[:city_id]}) 

Tout cela fonctionne très bien, mais je veux aussi trouver tous les groupes dans une zone où n'est pas spécifié les critères inférieurs. Par exemple, si un pays et une région sont donnés, mais pas une ville, j'aimerais le trouver par région.

Ce que je fais est la suivante:

if !params[:city_id].nil? 
    @groups = Group.find(:all, :conditions => {:city_id => params[:city_id]}) 
else 
    if !params[:region_id].nil? 
     @groups = Group.find(:all, :conditions => {:region_id => params[:region_id]}) 
    else 
     @groups = Group.find(:all, :conditions => {:country_id => params[:country_id]}) 
    end 
end 

Cela fonctionne parfaitement bien, mais il semble que c'est un peu inefficace. Est-ce que je le fais de la meilleure façon ou je peux rationaliser un peu? Une idée que j'avais était d'avoir une seule recherche vérifiant tous les paramètres, mais je ne pouvais pas savoir comment ignorer efficacement les paramètres qui étaient nuls - mon idée principale était de vérifier ceux qui n'étaient pas définis et de les définir à quelque chose comme '*' ou 'true', mais ce n'est pas ainsi que SQL joue le jeu.

Répondre

1

Sonne comme un emploi pour les étendues nommées:

class Group < ActiveRecord::Base 
    named_scope :in_city, lambda { |city_id| { 
    :conditions => { :city_id => city_id } 
    }} 

    named_scope :in_region, lambda { |region_id | { 
    :conditions => { :region_id => region_id } 
    }} 

    named_scope :in_country, lambda { |country_id | { 
    :conditions => { :country_id => country_id } 
    }} 
end 

Cela crée des champs d'application simples pour limiter les enregistrements du groupe. Vraisemblablement, vous avez indexé correctement votre base de données, donc ils sont rapides à résoudre.

Le contrôleur est beaucoup plus facile à mettre en œuvre alors:

def index 
    @group_scope = Group 

    if (!params[:city_id].blank?) 
    @group_scope = @group_scope.in_city(params[:city_id]) 
    elsif (!params[:region_id].blank?) 
    @group_scope = @group_scope.in_region(params[:region_id]) 
    elsif (!params[:country_id].blank?) 
    @group_scope = @group_scope.in_country(params[:country_id]) 
    end 

    @groups = @group_scope.all 
end 

En général, vous devriez tester pour .blank? au lieu de .nil? car certains éléments de formulaire peuvent envoyer des résultats vides, comme un select avec quelque chose comme "All" par défaut.

+0

Je n'avais pas rencontré named_scopes dans mon court temps avec des rails - ceux-ci correspondent parfaitement à la facture, dans ce problème et dans d'autres domaines. Merci beaucoup. – SaucyK

1

Si chaque valeur params est un candidat pour :conditions vous pouvez simplement faire ceci:

@groups = Group.all(:conditions => params.reject { |idx, val| val.nil? }) 

Ce juste jette les valeurs nulles de params et utilise les valeurs restantes pour les conditions.

Si vous ne souhaitez pas utiliser toutes les valeurs de params, vous avez deux options. Vous pouvez simplement se débarrasser d'un tas de redondance dans votre code d'origine:

conditions = if !params[:city_id].nil? 
       { :city_id => params[:city_id] } 
       elsif !params[:region_id].nil? 
       { :region_id => params[:region_id] } 
       else 
       { :country_id => params[:country_id] } 
       end 

@groups = Group.all(:conditions => conditions) 

Vous pouvez frapper quelques autres lignes comme celle-ci, mais il sacrifie un peu de lisibilité OMI:

conditions = if !params[:city_id].nil? then { :city_id => params[:city_id] } 
       elsif !params[:region_id].nil? then { :region_id => params[:region_id] } 
       else { :country_id => params[:country_id] } 
       end 

Ou vous pouvez faire quelque chose comme ceci:

conditions = [:city_id, :region_id, :country_id].inject({}) do |hsh, sym| 
    hsh[sym] = params[sym] unless params[sym].nil? 
    hsh 
end 

@groups = Group.all(:conditions => conditions) 

Cela a l'avantage que vous n'avez pas besoin d'ajouter une autre condition pour chaque symbole.

+0

Merci d'avoir pris le temps de fournir la réponse perspicace. Je n'ai pas utilisé votre suggestion dans ce cas, mais je peux voir votre suggestion pour .inject() m'être utile ailleurs dans mon projet. Merci beaucoup! – SaucyK

1

Vous pourriez utiliser certains idiomes Ruby pour obtenir quelque chose d'un peu plus succinct.

Essayez quelque chose comme ceci: (! Code non testé)

def index 
    @groups = Group.find :all, :conditions => [:city_id, :region_id, :country_id].inject {} do |conditions, name| 
     conditions[name] = params[name] unless params[name].blank? 
     conditions 
    end 
end 
+0

Merci d'avoir pris le temps de répondre à ma question. Je n'avais jamais rencontré le .inject() avant et je peux les voir utiles dans le futur. Merci beaucoup! – SaucyK