どうも、現在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
endjoins(:tags) - レシピとタグを結びつける
レシピとタグはN対Nの関係なので、中間テーブルでつながっています。
recipes ── recipe_tags ── tagsjoins(: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;この流れをまとめます。
まとめ
以上、タグをクリックしても絞り込みされなかった問題の共有でした。
プログラミング学習初めてもうすぐ3ヶ月になりますが、まだまだわからないことだらけです。
これからも継続して学習していきます。