has_many throughでscopeを使う

実装したいこと

articleとtagが多対多の関係で、中間テーブルにarticle_tagを設定。タグ名で検索して、そのタグ名に該当する記事を表示させる機能を作ります。

今回は、この機能を実装する上で、自分が間違えたところをアウトプットします。 article.rb

 class Article < ApplicationRecord
  belongs_to :category
  belongs_to :author

  has_many :article_tags
  has_many :tags, through: :article_tags
  has_many :article_blocks, -> { order(:level) }, inverse_of: :article
 ...
 end

article_tag.rb

 class ArticleTag < ApplicationRecord
  belongs_to :article
  belongs_to :tag
 end

tag.rb

 class Tag < Taxonomy
  has_many :article_tags
  has_many :articles, through: :article_tags
 end



scopeを使って実装

article.rb

 scope :by_tag, ->(tag_id) { joins(:tags).where(article_tags: { tag_id: tag_id }) }
 class SearchArticlesForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :tag_id, :integer

  def search
    # データベースから重複のないデータを取得してくる
    relation = Article.distinct

    # タグで検索内容を絞り込む
    relation = relation.by_tag(tag_id) if tag_id.present?
  end
 end



自分が間違えた内容

article.rb

 scope :by_tag, ->(tag_id) { joins(:tags).where( tag_id: tag_id ) }

articleと紐付いているtagsのテーブルをjoinで結合させて、tag_idを取得すればできるのでは?と思っていました。しかしこれをしてしまうと、SQLがテーブルを取得できないエラーが出てしまう。

 article = Article.first

 [26] pry(main)> article
=> #<Article:0x00007fee1cf737c8
 id: 2,
 category_id: 4,
 author_id: 3,
 uuid: "09030ca8-9f8a-44b7-8cd9-2a1b8a696918",
 slug: "konan",
 title: "名探偵コナン",
 description: "真実はいつも一つ",
 body: "<p>名探偵コナン</p>",
 state: "draft",
 published_at: Fri, 25 Jun 2021 18:00:00 JST +09:00,
 created_at: Sat, 05 Jun 2021 07:01:24 JST +09:00,
 updated_at: Thu, 10 Jun 2021 18:46:59 JST +09:00,
 deleted_at: nil>
 tag = Tag.first

 [27] pry(main)> tag
  Tag Load (1.9ms)  SELECT  "taxonomies".* FROM "taxonomies" WHERE "taxonomies"."type" IN ('Tag') ORDER BY "taxonomies"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<Tag:0x00007fee1578f158
 id: 1,
 type: "Tag",
 name: "saa",
 slug: "saae",
 description: nil,
 created_at: Thu, 03 Jun 2021 21:20:32 JST +09:00,
 updated_at: Thu, 03 Jun 2021 21:20:32 JST +09:00>
[28] pry(main)> 

tagはarticleのカラムならこの記載でいけるが、articleとtagが紐付いているのでテーブルをjoinで結合したらそりゃエラー出ますね。

 [33] pry(main)> article.article_tags
=> [#<ArticleTag:0x00007fee1f096d60 id: 1, article_id: 2, tag_id: 1, created_at: Sat, 05 Jun 2021 07:17:03 JST +09:00, updated_at: Sat, 05 Jun 2021 07:17:03 JST +09:00>,
 #<ArticleTag:0x00007fee1f0b4ae0 id: 2, article_id: 2, tag_id: 5, created_at: Sat, 05 Jun 2021 07:17:51 JST +09:00, updated_at: Sat, 05 Jun 2021 07:17:51 JST +09:00>]

whereメソッドは「結合先のテーブル名」を指定している事に注意してください。

where(article_tags: {tag_id: tag_id})は、article_tagsテーブルのtag_idカラムに対しての条件になっている。

joinsメソッドの基本的な使い方は、joinsメソッドを実行したArticleモデルのレコードを取得するもので、あくまでも関連するモデルはwhereメソッドなどで条件を指定して絞り込みをしているだけなので注意してください。



参考URL

pikawaka.com