Comment utiliser une variable en attribut de find?

Salut á tous,
J’ai une requete find qui prend une variable en argument comme ca:

@catlist = “#{([“category_id = ?”] * subcats.size).join(” OR “)}”
@values = @searchkeywords.join(",")

@ads = Ad.find(:all , :conditions => [
@catlist,"#{@values}"],:order=>“updated_on”)

avec @values=val1,val2,val3 etc

Le probléme c’est que rails voit @values comme une seul variable au
lieu de plusieurs. Est-ce qu’il y a un moyen que rails reconnaissent
@values comme plusieurs attributs?

merci d’avance

Pat

Patrick A. wrote the following on 28.08.2006 00:02 :

Le probléme c’est que rails voit @values comme une seul variable au
lieu de plusieurs. Est-ce qu’il y a un moyen que rails reconnaissent
@values comme plusieurs attributs?

J’ai l’impression que tu te compliques la vie en cherchant à utiliser
des OR au lieu de IN, le code suivant ne conviendrait-il pas ?

@ads = @searchkeywords.empty? ? [] : Ad.find(:all, :conditions => [
‘category_id IN ?’, @searchkeywords ], :order=>“updated_on”)

Note : le @searchkeywords.empty? est nécessaire car ActiveRecord injecte
automatiquement le contenu de @searchkeywords en faisant un join(’,’),
ce qui ne donne une requête SQL valide que si il y a au moins un élément
dans @searchkeywords. Dans le cas ou @searchkeywords est vide, il n’y a
pas de résultat de toute façon -> []

Je fais ça à pas mal d’endroits dans mon propre code, ce qui me fait
penser que je vais devoir enrober ce comportement dans une librairie…

Lionel.

Le 28 août 06 à 00:02, Patrick A. a écrit :

Salut á tous,

Salut

Le probléme c’est que rails voit @values comme une seul variable au
lieu de plusieurs. Est-ce qu’il y a un moyen que rails reconnaissent
@values comme plusieurs attributs?

Puis-ce qu’en effet c’est une seule variable de type chaîne … Il
faut que values soit un tableau et non une chaîne. Utilises
directement @searchkeywords qui semble être un tableau plutôt que de
concaténer l’ensemble des valeurs du tableau dans une chaine.

Bon amusement.

Nicolas C.

2006/8/28, Lionel B. :

J’ai l’impression que tu te compliques la vie en cherchant à utiliser
des OR au lieu de IN, le code suivant ne conviendrait-il pas ?

Je suis d’accord une requête SQL avec IN est bien plus simple.

@ads = @searchkeywords.empty? ? [] : Ad.find(:all, :conditions => [
‘category_id IN ?’, @searchkeywords ], :order=>“updated_on”)

Note : le @searchkeywords.empty? est nécessaire car ActiveRecord
injecte automatiquement le contenu de @searchkeywords en faisant
un join(’,’), ce qui ne donne une requête SQL valide que si il y a au
moins un élément dans @searchkeywords. Dans le cas ou
@searchkeywords est vide, il n’y a pas de résultat de toute façon -> []

C’est plus robuste avec des parenthèses : ‘foo IN (?)’
Cela dit, si @searchkeywords est vide, il est effectivement inutile
d’effectuer une requête SQL dont on sait pertinemment qu’elle renverra
[].

Autre remarque : find résiste à ça : :conditions => [‘foo IN (?)’, nil]

Je fais ça à pas mal d’endroits dans mon propre code, ce qui me fait
penser que je vais devoir enrober ce comportement dans une librairie…

avec un block/yield ? :slight_smile:

-- Jean-François.

Salut Patrick,

J’ai une requete find qui prend une variable en argument comme ca:

@catlist = “#{([“category_id = ?”] * subcats.size).join(” OR “)}”

Première remarque, ton écriture est plutôt compliqué et gagnerait Ã
être simplifié :

  • String#join retourne une chaîne de caractères donc tu ne gagnes
    à rien de créer une autre chaîne dans lequel tu ne fais uniquement
    une substitution de chaîne. Si str est une chaîne, il y a peu
    d’intérêt à faire “#{str}”.

Donc ça s’écrit : @catlist = ([“category_id = ?”] * subcats.size).join("
OR ")

Deuxièmement : tu utilises * et tu fais un join juste après, ce qui
t’obliges
de surcroît à utiliser des parenthèses, autant faire simple
et écrire :

@catlist = [“category_id = ?”] * subcats.size * " OR "

C’est plus lisible non ?

@values = @searchkeywords.join(",")

Ici, @values (mais au fait, as tu vraiment besoin d’avoir values
comme variable d’instance, tu t’en sers pour construire le find,
mais t’en as besoin dans ta vue ?) est une chaîne de caractères,
puisque tu fais un Array#join.

@ads = Ad.find(:all , :conditions => [
@catlist,"#{@values}"],:order=>“updated_on”)

Pour “#{@values}”, comme @values est une String, la remarque
en haut s’applique aussi ici.

avec @values=val1,val2,val3 etc

Ben non, @values=“val1,val2,val”, ie une chaîne de caractères
avec des virgules dedans, ce n’est pas la même chose
que : @values = [“val1”, “val2”, “val3”]

Le probléme c’est que rails voit @values comme une seul variable au
lieu de plusieurs. Est-ce qu’il y a un moyen que rails reconnaissent
@values comme plusieurs attributs?

D’abord pour adopter une terminologie commune, tu souhaites
que Ruby considère @values comme un tableau d’éléments (un
tableau de String même) plutôt qu’une String unique ; de plus,
quand on parle d’attributs pour un Modèle dans Rails, ça désigne
autre chose ça correspond au mapping colonnes de ta table <->
attributs de ton modèle.

Revenons-en à ta construction de ton find. option[:conditions]
doit être sous la forme d’un tableau bien spécifique :

tu écris [ @catlist, “#{@values}” ] qui est équivalent Ã
[@catlist, @values]

avec @values=“val1,val2,val3”, tu as en fait
[@catlist, “val1, val2, val3” ]. Ce que tu souhaites, c’est
[@catlist, “val1”, val2", val3"]. Tu dois plutôt manipuler
@values comme un tableau car :

  • si tu as [“foo”, bar], avec bar un tableau, par exemple = %w(un deux)
    tu as [“foo”, [“un”, deux"]] et il suffit d’appeler #flatten pour
    obtenir
    [“foo”, “un”, “deux”]
  • autre solution, si tu as [“foo”] et bar = %w(un deux), tu peux
    concaténer les deux tableaux [“foo”].concat(bar) pour encore obtenir
    [“foo”, “un”, “deux”].

Ainsi [@catlist, @values] se transforme en
[@catlist].concat(@values) pour obtenir ce que tu souhaites avec
@values un tableau.

Or @values = “val1, val2, val3”. Facile, il suffirait d’utiliser #split
pour avoir un tableau ! Mais finalement, faire un join suivi d’un
split à partir de @searchkeywords qui est déjà un tableau (de String),
ça sert à quoi ?

Au final, ton code se simplifie en :

catlist = [“category_id = ?”] * subcats.size * " OR "
@ads = Ad.find(:all,
:conditions => [ catlist ].concat(@searchkeywords),
:order => “updated_on”)

(et si finalement tu avais besoin de catlist dans ta vue, tu remets les
@ )

A+

– Jean-François.

ok merci a tous vos réponses

Pat