J'utilise des associations polymorphes pour suivre les commentaires dans mon projet. Tous les trucs très simples.
Le problème que j'ai est dans l'interrogation basée sur l'association polymorphe et la jonction du modèle de commentaire à son propriétaire.
Alors ...
J'ai un modèle de commentaire
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
Et un mode ForumTopics:
class ForumTopic < ActiveRecord::Base
has_many :comments, :as => :commentable
end
J'ai plusieurs autres modèles "commentables" qui ne sont pas importants pour le moment. Tout cela fonctionne.
Ce que j'essaie de faire, c'est de trouver tous les commentaires qui appartiennent à un ForumTopic avec une condition spécifiée (dans ce cas, "en vedette" == vrai).
Lorsque j'essaie d'utiliser un Finder pour rejoindre les modèles:
@comments = Comment.find(:all
:joins => :commentable
:conditions => ["forum_topics.featured = ? ", true]
)
Je reçois l'erreur suivante:
Impossible de charger avec impatience l'association polymorphe: commentable
Utilisation de l'AR "include syntax":
@comments = Comment.find(:all
:include => :forum_topics
:conditions => ["forum_topics.featured = ? ", true]
)
retour:
L'association nommée 'forum_topics' est introuvable; peut-être l'avez-vous mal orthographié?
Si j'essaie de me joindre à un nom de table au lieu du nom de l'association (chaîne au lieu de symbole):
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Je vois:
Mysql :: Erreur: commentaires de la table inconnue: commentaires SELECT. FROM commentaires forum_topics WHERE (forum_topics.featured = 1) *
(Vous pouvez voir ici que la syntaxe de la requête sous-jacente est totalement désactivée et que la jointure est complètement manquante).
Je ne sais pas si ce que je fais est même possible, et il existe d'autres façons d'obtenir le résultat souhaité, mais il semble que devrait être faisable.
Des idées? Quelque chose me manque?
Argh!
Je pense avoir trouvé le problème.
Lors de l'adhésion via:
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Vous avez besoin de la jointure entière!
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",
Voir le toujours génial: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Une vieille question, mais il existe un moyen plus propre d'y parvenir en créant une association directe pour le type spécifique avec le polymorphe:
#comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :forum_topics, -> { where( comments: { commentable_type: 'ForumTopic' } ).includes( :comments ) }, foreign_key: 'commentable_id'
...
end
Vous pouvez alors passer :forum_topics
à includes
supprimant la nécessité d'une jointure en désordre:
@comments = Comment
.includes( :forum_topics )
.where( :forum_topics => { featured: true } )
Vous pouvez ensuite nettoyer cela en déplaçant la requête dans une étendue:
#comment.rb
class Comment < ActiveRecord::Base
...
scope :featured_topics, -> {
includes( :forum_topics )
.where( :forum_topics => { featured: true } )
}
...
end
Vous laissant être capable de faire simplement
@comments = Comment.featured_topics
Beaucoup de gens y ont fait allusion dans les réponses et les commentaires, mais je sentais que les gens, y compris moi-même, se feraient trébucher si j'atterrissais ici et ne lisais pas suffisamment.
Alors, voici la bonne réponse, y compris le conditionnel qui est absolument nécessaire.
@comments = Comment.joins( "INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id" )
.where( comments: { commentable_type: 'ForumTopic' } )
.where( forum_topics: { featured: true } )
Merci à tous, en particulier @Jits, @Peter et @prograils pour leurs commentaires.
La solution acceptée ne fonctionne pas une fois que vous avez introduit un autre modèle qui a une association utilisant "commentable". commentable_id n'est pas unique et vous commencerez donc à récupérer les mauvais commentaires.
Par exemple:
Vous décidez d'ajouter un modèle de news qui accepte les commentaires ...
class News < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Vous pouvez maintenant récupérer deux enregistrements si vous avez fait un commentaire sur un forum_topic avec un identifiant de 1 et un article de nouvelles avec un identifiant de 1 en utilisant votre requête:
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"
Vous pourriez probablement résoudre le problème en fournissant un type commentable comme l'une de vos conditions, mais je ne pense pas que ce soit la meilleure façon d'aborder ce problème.
Vérifié pour fonctionner sous Rails 5 :
Solution 1:
@comments = Comment
.where(commentable_type: "ForumTopic")
.joins("INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id")
.where(forum_topics: {featured: true})
.all
ou
Solution 2:
@comments = Comment
.joins("INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id AND comments.commentable_type = 'ForumTopic'")
.where(forum_topics: {featured: true}).all
Faites attention à la syntaxe SQL brute: aucun backticks n'est autorisé. Voir http://guides.rubyonrails.org/active_record_querying.html#joining-tables .
Personnellement, je préfère la solution 1 car elle contient moins de syntaxe SQL brute.
Je suis tombé sur ce post et cela m'a conduit à ma solution. Utiliser le commentable_type comme une de mes conditions mais utiliser un LEFT OUTER JOIN à la place. De cette façon, les sujets du forum sans commentaires seront inclus.
LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
Rails n'inclut pas de jointure polymorphe par défaut, mais ce joyau vous aiderait à rejoindre facilement votre relation polymorphe. https://github.com/jameshuynh/polymorphic_join