基本中の基本のRouting設定の話になるのですが、改めて振り返ってみます。

基本の考え方

RailsではGET/POST/PUT/PATCH/DELETEの使用方法を明確に分けて考えます。
それぞれイメージとしてSQLと以下のように対応します。

method SQL 意味
GET SELECT データの取得
POST INSERT データの作成
PUT/PATCH UPDATE データの更新
DELETE DELETE データの削除

SQLとひも付けての説明ですが、必ずしもDBのデータを弄らないときでも使います。例えば、ログインセッションの管理では、ログイン=セッションの作成と考えてPOSTを利用し、ログアウトはセッションの削除と考えてDELETEを利用します。

独自のアクションを作るときには意識するようにしましょう。

基本の使い方

RailsではRoutingの設定を行うためにconfig/routes.rbを編集していきます。

基本的には何を操作する画面なのかを意識するためにresourcesを使って行きましょう。

config/routes.rb

resources :posts

こうすることで、自動的に以下の様なRoutingが設定されます。

基本の用途 action url method
一覧画面 index /posts GET
詳細画面 show /posts/1 GET
新規作成画面 new /posts/new GET
編集画面 edit /posts/1/edit GET
データ作成 create /posts POST
データ更新 update /posts/1 PUT/PATCH
データ削除 destroy /posts/1 DELETE

不要なアクションの削除

先述の通り、resorcesメソッドでは様々なアクションが作られます。
ただ、必ずしも全て必要じゃないこともあります。

そのようなときに、不要なRoutingが残るのは美しくないですし、妙なセキュリティ・ホールになるかもしれません。

そのときは必要なものだけを残すようにしましょう。以下の例では「一覧画面」、「新規作成画面」、「データ作成」のみを残す設定です。

resources :posts, only: [:index, :new, :create]

また、これとは逆に利用しないアクションを指定することで、以下のようにもかけます。

resources :posts, except: [:show, :edit, :update, :destroy]

独自のアクションの作成

当然ながら、Railsは独自のアクションを定義することもできます。
例えば、CSVをダウンロードするアクションdownload_csvとして定義してみます。

CSVをダウンロードする使用を考えた時、考え方は2つあります。

  1. 全てのpostの内容をCSVとしてダウンロードするとき
  2. 指定されたpostの内容をCSVとしてダウンロードするとき

これらをそれぞれ書いてみます。

全てのpostの内容をCSVとしてダウンロードするとき

resources :posts do
  collection do
    get :download_csv # => /posts/download_csv
  end
end

指定されたpostの内容をCSVとしてダウンロードするとき

resources :posts do
  member do
    get :download_csv # => /posts/1/download_csv
  end
end

大きな違いはidがURLに含まれるかどうかです。「指定されたpost」をidとして与えています。このidPostsController内でparams[:id]として参照することができます。

resourcesの入れ子

投稿にコメントが付いているときなど、関連するデータはしばしば、入れ子構造になります。
そのときroutesを入れ子にすることができます。

resources :posts do
  resources :comments
end

こうすると、以下の様なURLでcommentsのコントローラにアクセスできるようになります。

基本の用途 action url method
一覧画面 index /posts/1/comments GET
詳細画面 show /posts/1/comments/1 GET
新規作成画面 new /posts/1/comments/new GET
編集画面 edit /posts/1/comments/1/edit GET
データ作成 create /posts/1/comments POST
データ更新 update /posts/1/comments/1 PUT/PATCH
データ削除 destroy /posts/1/comments/1 DELETE

入れ子のURL設計にしておくと、下記のように、本来ありえないデータにアクセスすることによる、データの不整合を起こさないような実装が可能です。

app/controllers/comments_controller.rb

class CommentsController < ApplicationController
  before_action :set_post
  before_action :set_comment

  def index
    @comments = @post.comments
  end

  # 中略

  private
  def set_post
    @post = Post.find(params[:post_id])
  end

  def set_comment
    @comment = @post.comments.find(params[:id])
  end  
end

postの編集権限などを付けるとき、concernとして、まとめやすくなります。

欠点は長すぎるURLになることがあることですが、そうなってしまうときは、そもそも何か間違っている気がします。

まとめ

以上が一番、単純なRoutingの仕方になります。
もし、もっと複雑なURLを使いたいなどがあれば、matchを利用することでid以外のパラメータをURLとして渡してやるなどのことが可能です。

  • 基本はresourcesを利用する
  • 不要なアクションへのroutingは必ず削除する
  • has_manyな関係のデータへのRoutingは入れ子にする

以上を意識することで、綺麗にバグが少なく、実装が可能になるかと思います。