2009-10-29 11 views
2

Disons que j'ai un formulaire où les utilisateurs peuvent rechercher des personnes dont le nom commence par une chaîne name particulière, par exemple, «Mi» trouverait «Mike» et «Miguel». Je probablement créer une déclaration de découverte comme ceci:Conditions avec des variables Bind et des paramètres facultatifs

find(:all, :conditions => ['name LIKE ?', "#{name}%"]) 

Disons que la forme a également deux champs facultatifs, hair_color et eye_color qui peut être utilisé pour filtrer davantage les résultats. Ignorant la partie du nom de la requête, une déclaration de découverte pour les personnes qui peuvent prendre un nombre arbitraire de paramètres facultatifs pourrait ressembler à ceci:

find(:all, :conditions => { params[:person] })

qui pour mes deux paramètres facultatifs se comporteraient comme l'équivalent de ce :

find(:all, :conditions => { :hair_color => hair_color, :eye_color => eye_color })

ce que je ne peux pas comprendre comment fusionner ces deux types de requêtes où le champ est obligatoire, « name » est appliqué à la condition « comme » ci-dessus, et l'option hair_color et eye_color paramètre Des ers (et peut-être d'autres) peuvent être ajoutés pour filtrer davantage les résultats.

Je peux certainement créer une chaîne de requête pour ce faire, mais je pense qu'il doit y avoir une «voie de rails» qui soit plus élégante. Comment puis-je fusionner des paramètres de liaison obligatoires avec des paramètres facultatifs?

Répondre

4

Ceci est l'utilisation parfaite d'une portée nommée.

créer un champ nommé dans le modèle:

named_scope :with_name_like, lambda {|name| 
    {:conditions => ['name LIKE ?', "#{name}%"]} 
} 

À ce stade, vous pouvez appeler

Model.with_name_like("Mi").find(:all, :conditions => params[:person]) 

Et Rails fusionnera les requêtes pour vous.

Edit: Code pour Waseem:

Si le nom est en option, vous pouvez soit omettre la portée du nom de votre chaîne de méthode avec une condition if:

unless name.blank? 
    Model.with_name_like("Mi").find(:all, :conditions => params[:person]) 
else 
    Model.find(:all, :conditions => params[:person]) 
end 

ou vous pouvez redéfinir la portée du nom de faire la même chose.

named_scope :with_name_like, lambda {|name| 
    if name.blank? 
    {} 
    else 
    {:conditions => ['name LIKE ?', "#{name}%"]} 
    end 
} 

Mise à jour

Voici les Rails 3 version du dernier extrait de code:

scope :with_name_like, lambda {|name| 
    if not name.blank? 
    where('name LIKE ?', "#{name}%") 
    end 
} 
+0

Juste une curiosité. Comment l'étendriez-vous si la spécification de la chaîne de nom était également facultative? – Waseem

+0

Je savais qu'il devait y avoir une belle façon propre de le faire. Il y a toujours des rails :-) Merci! – SingleShot

+1

@waseem, j'ai modifié la solution pour répondre à votre question. – EmFi

0

Pour répondre également demande Waseem, mais permettant nul à la place vide? (Ce qui est udeful au cas où vous voulez utiliser "@things = Thing.named_like (params [: nom])" directement)

named_scope :named_like, lambda do |*args| 
    if (name=args.first) 
    {:conditions => ["name like ?",name]} 
    else 
    {} 
    end 
end 

# or oneliner version: 

named_scope :named_like, lambda{|*args| (name=args.first ? {:conditions => ["name like ?",name]} : {}) } } 

J'espère que cela aide

Questions connexes