はじめに
Railsでアプリケーション作成しております。
作成中にルーティングについて少々学習したので、自分用のメモとして残しておきます。
ルーティングについて
ルーティングは、受け取ったHTTPリクエストに応じて、特定のコントローラー内のアクションを動作するように割り当てを行なっている。
Railsのルーティングはconfig/routes.rb
で設定する。
Rails.application.routes.draw do
# ここにルーティングを設定する
root 'home#index'
end
設定したルーティングは、以下のコマンドを実行することで確認できます。
$ bin/rails routes
Prefix Verb URI Pattern Controller#Action
root GET / home#index
実装したかったこと(前提)
ユーザー(user)が、ある施設(house)に口コミ(review)を投稿する機能を実装します。
施設は複数あり、1つの施設に複数の口コミが投稿されると想定する。
各々の投稿のURLを規則的に振り当てるとすると、https://○○○/house/id/review/id
というふうにしたい。
今回の各モデルのアソシエーションは以下のようにしている。
Userの認証モデルについてはdeviseにて実装済みである。
結論
ルーティングをネストするといい!
Rails.application.routes.draw do
root 'home#index'
devise_for :users
# ネストしたルーティング
resources :houses do
resources :reviews
end
end
ルーティングを確認する際は、-g
や-c
オプションを用いると見やすくなります。
# -g :名前付きルーティングやHTTP動詞、URLパスのいずれかに部分マッチするルーティングのみ表示する
$ bin/rails routes -g reviews
Prefix Verb URI Pattern Controller#Action
house_reviews GET /houses/:house_id/reviews(.:format) reviews#index
POST /houses/:house_id/reviews(.:format) reviews#create
new_house_review GET /houses/:house_id/reviews/new(.:format) reviews#new
edit_house_review GET /houses/:house_id/reviews/:id/edit(.:format) reviews#edit
house_review GET /houses/:house_id/reviews/:id(.:format) reviews#show
PATCH /houses/:house_id/reviews/:id(.:format) reviews#update
PUT /houses/:house_id/reviews/:id(.:format) reviews#update
DELETE /houses/:house_id/reviews/:id(.:format) reviews#destroy
# -c :特定のコントローラーのルーティングのみ表示する
$ bin/rails routes -c reviews
Prefix Verb URI Pattern Controller#Action
house_reviews GET /houses/:house_id/reviews(.:format) reviews#index
POST /houses/:house_id/reviews(.:format) reviews#create
new_house_review GET /houses/:house_id/reviews/new(.:format) reviews#new
edit_house_review GET /houses/:house_id/reviews/:id/edit(.:format) reviews#edit
house_review GET /houses/:house_id/reviews/:id(.:format) reviews#show
PATCH /houses/:house_id/reviews/:id(.:format) reviews#update
PUT /houses/:house_id/reviews/:id(.:format) reviews#update
DELETE /houses/:house_id/reviews/:id(.:format) reviews#destroy
memberやcollectionとの違いは?
ルーティングにREST以外のアクションを追加したい場合に、member
やcollection
を使用します。
memberやcollectionを使う場合とどのような違いが出るのか、確認してみます。
①memberを使う場合
Rails.application.routes.draw do
root 'home#index'
devise_for :users
resources :houses do
member do
resources :reviews
end
end
end
$ bin/rails routes -g reviews
Prefix Verb URI Pattern Controller#Action
reviews GET /houses/:id/reviews(.:format) reviews#index
POST /houses/:id/reviews(.:format) reviews#create
new_review GET /houses/:id/reviews/new(.:format) reviews#new
edit_review GET /houses/:id/reviews/:id/edit(.:format) reviews#edit
review GET /houses/:id/reviews/:id(.:format) reviews#show
PATCH /houses/:id/reviews/:id(.:format) reviews#update
PUT /houses/:id/reviews/:id(.:format) reviews#update
DELETE /houses/:id/reviews/:id(.:format) reviews#destroy
一見これでも良さそうだが、houses後の:id
とreview後の:id
が同じになってしまう。
②collectionを使う場合
Rails.application.routes.draw do
root 'home#index'
devise_for :users
resources :houses do
collection do
resources :reviews
end
end
end
$ bin/rails routes -g reviews
Prefix Verb URI Pattern Controller#Action
reviews GET /houses/reviews(.:format) reviews#index
POST /houses/reviews(.:format) reviews#create
new_review GET /houses/reviews/new(.:format) reviews#new
edit_review GET /houses/reviews/:id/edit(.:format) reviews#edit
review GET /houses/reviews/:id(.:format) reviews#show
PATCH /houses/reviews/:id(.:format) reviews#update
PUT /houses/reviews/:id(.:format) reviews#update
DELETE /houses/reviews/:id(.:format) reviews#destroy
この場合、houses後に:id
が付かないURLになってしまう。
以上のことから、今回のケースではネストした方が良いということがわかりました。
コントローラーやビューでの振る舞い
ルーティングをネストすれば、houseとreviewのIDを別々に定義できることがわかりました。
それでは、それぞれのidを用いて、DBにデータを保存するにはどうすれば良いのか?
今回はform_withを用いた口コミ作成、編集フォームを例に考えます。
コントローラーでのインスタンスの定義や、ビューでの記述については以下のようにしました。
コントローラーの記述
class ReviewsController < ApplicationController
def new
@house = House.find(params[:house_id])
@review = current_user.reviews.build
end
def create
@review = current_user.reviews.build(review_create_params)
@review.house_id = params[:house_id]
if @review.save
flash[:success] = "口コミを投稿しました!"
redirect_to house_path(@review.house_id)
else
render 'new'
end
end
def edit
@house = House.find(params[:house_id])
@review = Review.find(params[:id])
end
def update
@review = Review.find(params[:id])
if @review.update(review_update_params)
flash[:success] = "口コミを修正しました!"
redirect_to root_url
else
render 'edit'
end
end
private
def review_create_params
params.require(:review).permit(:content, :user_id)
end
def review_update_params
params.require(:review).permit(:content)
end
end
口コミは、どの施設に対する口コミなのかということを定義する必要があります。
そのため、@house
を上記のような方法で取得しています。
ビューファイルの記述
= form_with model: [@house, @review], local: true do |f|
= f.text_field :content, placeholder: "口コミ本文を書いてください(10,000字以内)"
= f.submit "口コミを投稿する"
= form_with model: [@house, @review], local: true do |f|
= f.text_field :content
= f.submit '口コミを修正する'
ビュー側では、form_with
の引数に[@house, @review]
のように配列を渡すように記述する必要があります。
以上になります。誤りなどありましたら、ご指摘お願いします。
参考
https://railsguides.jp/routing.html
https://pikawaka.com/rails/form_with