railsのタグ絞り込み機能が反映されないことについて解説したアイキャッチ画像。

【Rails】joins・where・orderを組み合わせたタグ検索の仕組みについて

どうも、現在ITエンジニア転職に向けてプログラミング学習中なのですが、つまづいた部分があったのでそれをアウトプットしたいと思います。

投稿機能をつくっていて、タグで投稿を絞り込む機能実装をしていたのですが、タグをクリックしても絞り込みできない問題が発生。

スクールのカリキュラム内容のため、今回は投稿ではなく料理レシピのタグ機能に置き換えて説明します。

タグをクリックしても絞り込みできない

レシピ一覧ページにタグを表示するため、以下のようなコードを入力。

<% recipe.tags.each do |tag| %>
  <%= link_to tag.name,
              recipes_path(tag: tag.name),
              class: "badge rounded-pill bg-warning text-dark" %>
<% end %>

見た目としてはタグが表示されるようになりましたが、タグをクリックしても画面が変わらない問題が発生しました。

原因:コントローラーがタグ検索に対応していなかった

調べてみると、RecipesController#indexが下記状態でした。

def index
  @recipes = Recipe.all
end

このコードだと、

/recipes?tag=ヘルシー

のようにパラメータがきても、

  • params[:tag] を見ていないため何のフィルタもしない
  • いつも Recipe.all が読み込まれる
  • 画面の内容は常に全件表示のまま

だから、タグをクリックしても画面が変わらなかったのです。

修正コード

def index
  if params[:tag].present?
    @recipes = Recipe.joins(:tags)
                     .where(tags: { name: params[:tag] })
                     .order(created_at: :desc)
  else
    @recipes = Recipe.order(created_at: :desc)
  end
end

このコードによって:

  • /recipes?tag=ヘルシー → 「ヘルシー」タグのレシピだけ表示
  • /recipes → 全件表示

となり、無事にタグ絞り込みが動作するようになりました!

続いて、このコードが実際何をしているのか?具体的にみていきます。

修正コードの解説

def index
  if params[:tag].present?
    @recipes = Recipe.joins(:tags)
                     .where(tags: { name: params[:tag] })
                     .order(created_at: :desc)
  else
    @recipes = Recipe.order(created_at: :desc)
  end
end

joins(:tags) - レシピとタグを結びつける

レシピとタグはN対Nの関係なので、中間テーブルでつながっています。

recipes ── recipe_tags ── tags

joins(:tags) を使うことで、レシピに紐づくタグの情報を SQL JOIN で結合します。

Rails が内部でこんな SQL を作ります(イメージ):

SELECT recipes.*
FROM recipes
INNER JOIN recipe_tags ON recipes.id = recipe_tags.recipe_id
INNER JOIN tags ON tags.id = recipe_tags.tag_id

これによって、タグで検索できる状態になります。

where(tags: { name: params[:tag] }) - タグ名で絞り込む

次にwhereを実行します。

where(tags: { name: "ヘルシー" })

これはSQLだと、

WHERE tags.name = "ヘルシー"

つまり、ヘルシーというタグを持つレシピだけを抽出します。

order(created_at: :desc) - 新しい順に並べる

そのまま検索すると「古いレシピ」から表示されるので、order を使って新しい順に並べます。

ORDER BY recipes.created_at DESC;

この流れをまとめます。

join → where → orderの流れ

  1. 関連データを JOIN して(検索可能にする)
  2. 検索条件で絞り込み(タグで検索)
  3. 並べ替えて仕上げる(新しい順)

まとめ

以上、タグをクリックしても絞り込みされなかった問題の共有でした。

プログラミング学習初めてもうすぐ3ヶ月になりますが、まだまだわからないことだらけです。

これからも継続して学習していきます。

-Ruby on Rails
-, ,

Translate »