【Rails】Action Controller(コントローラー)の書き方

はじめに

ActionController(または単にコントローラー)はMVCモデルのCに相当します。コントローラーはルーティングからリクエストを受け取ると、必要であればモデルと連携してデータベースからデータを取得し、それをビューに渡します。あるいは、ビューから受け取ったデータをモデルに渡し、データベースの更新を指示します。コントローラーはモデルとビューの橋渡し役を担っています。

本記事では、ActionController(コントローラー)の使い方についてまとめています。

コントローラーの基本

コントローラー名は複数形にすることが推奨されていますが、必ずしもそうしなければいけないというわけではありません。ただし、コントローラー名が複数形でない場合、ルーティングで作成されるヘルパーメソッド(_path_url)が一貫して使えなくなることにご注意ください。

コントローラーの作成

コントローラーを作成するには以下のコマンドを実行します。

$ rails generate controller Users

上記のコマンドを実行すると、以下のファイルとディレクトリが作成されます。

  • app/controllers/users_controller.rb
  • app/views/users/
  • test/controllers/users_controller_test.rb
  • app/helpers/users_helper.rb
  • app/assets/stylesheets/users.scss

アクションの追加

アクションを追加してコントローラーを作成したい場合は以下のコマンドを実行します。追加するアクションはスペースで区切って何個でも指定することができます。

$ rails generate controller Users index

上記のコマンドを実行すると、アクションを指定せずに実行したときと同じファイルとディレクトリに加え、users/index.html.erbというビューも作成されます。また、UsersController#indexアクションが追加され、UsersController#indexに対応するルーティングも追加されます。

Resourcefulアクションの追加

Resourcefulアクションを一度に追加してコントローラーを作成したい場合は以下のコマンドを実行します。

$ rails generate scaffold_controller Users

上記のコマンドを実行すると、UsersControllerにResourcefulアクションが完全な形で追加され、必要なビューもすべて作成されます。ただし、Resourcefulルーティングは追加されません

ヘルパーが作成できない場合

コントローラーを削除してすぐに再作成しようとすると、以下のエラーメッセージが出てヘルパーが作成できない場合があります。

The name 'UsersHelper' is either already used in your application or reserved by Ruby on Rails. Please choose an alternative or use --force to skip this check and run this generator again.

UsersHelperという名前が既に使われているというエラーメッセージです。ヘルパーは削除しても一定時間キャッシュに残るようです。すぐに再作成したい場合は、以下のコマンドを実行してキャッシュを削除します。

# Springプロセスの確認
$ ps -ef | grep spring
  501 21596     1   0 12:39PM ttys001    0:00.94 spring server | rails-contentful | started 43 mins ago   
  501 22251 21281   0  1:22PM ttys001    0:00.01 grep spring

# Springプロセスの停止
$ kill -9 21596

コントローラーの削除

コントローラーを削除するには以下のコマンドを実行します。scaffold_controllerで作成されたコントローラーも削除できます。

$ rails desctroy controller Users

コントローラー作成時に同時に作成されたすべてのファイルとディレクトリが削除されます。ただし、ルーティングは削除されません。

コントローラーの書き方

暗黙的なレンダリング

コントローラーはアクションにレンダリングを記述しなくても、コントローラーとアクションから暗黙的にレンダリングするビューを決定します。

class UsersController < ApplicationController
  def index
  end
end

このようなコントローラーがあり、/usersというリクエストを受け取るとindexアクションが実行され、暗黙的にusers/index.html.erbをレンダリングします。

変数の引き継ぎ

変数をビューに引き継ぎたい場合、変数名を@variableのように先頭に@をつけます。逆に言うと、ビューに引き継ぐ必要のない変数は@をつけないでください。

class UsersController < ApplicationController
  def index
    @message = "ようこそ、 #{params[:name]} さん"
  end
end

レンダリング

ビューのレンダリングを行うにはrenderメソッドを使います。暗黙的なレンダリングのセクションで説明したとおり、コントローラーとアクションの組み合わせと、ビューの名前が一致しているなら、明示的にrenderメソッドを記述する必要はありません。そうでない場合、レンダリングするビューを指定します。ビューの指定には様々な方法があります。

render :edit
render action: :edit
render "edit"
render "edit.html.erb"
render action: "edit"
render action: "edit.html.erb"
render "books/edit"
render "books/edit.html.erb"
render template: "books/edit"
render template: "books/edit.html.erb"
render "/path/to/rails/app/views/books/edit"
render "/path/to/rails/app/views/books/edit.html.erb"
render file: "/path/to/rails/app/views/books/edit"
render file: "/path/to/rails/app/views/books/edit.html.erb"

ビューの指定方法に限りませんが、Railsはひとつの結果を得るのに多彩な書き方をサポートをしていることが多いです。そのほとんどは歴史的な経緯から後方互換性を維持するために残されているだけに過ぎないのでしょう。

省略できるところは省略し、できるだけシンプルな書き方を選ぶのがいいと思います。

レンダリングの重複エラー

renderメソッドのよくある認識違いとして、「レンダリングを行ったら処理を抜ける」というものがあります。以下の例では、ユーザーが管理者ならば管理者用のビューをレンダリングしていますが、処理はそこで終わらず、次のrenderメソッドが実行されることになります。これはレンダリングの重複としてエラーになります。

class UsersController < ApplicationController
  def show
    @user = Users.find(params[:id])

    if @user.admin?
      render "admin_show"
    end

    render "show"
  end
end

これを回避するには、以下のようにデフォルトのレンダリングを削除します。ユーザーが管理者ならば管理者用のビューがレンダリングされ、そうでなければ暗黙的なレンダリングによりusers/show.html.erbがレンダリングされます。

class UsersController < ApplicationController
  def show
    @user = Users.find(params[:id])

    if @user.admin?
      render "admin_show"
    end
  end
end

リダイレクト

新しいURLのリクエストにリダイレクトするにはredirect_toメソッドを使います。リダイレクト先の指定にはルーティングで作成されるヘルパーメソッド(_path_url)が使えます。

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    redirect_to user_path(@user.id)
  end
end

レンダリングとリダイレクトの違い

レンダリングはクライアントにビューの内容を返します。一方、リダイレクトは新しくリクエストを発行します。Railsは新しいリクエストのルーティングからコントローラーとアクションを決定し、そのアクションが実行されます。わかりにくい場合は、ざっくりと以下の通り覚えておけばいいと思います。

メソッド メソッド実行後に動くもの
レンダリング 指定したビュー
リダイレクト 指定したコントローラーのアクション

ストロングパラメーター

コントローラーはルーティング情報やビューから渡されたパラメーター情報をparamsハッシュで受け取ります。ストロングパラメーターで許可するパラメーターを定義することができます。

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
  end

  private
    def user_params
      params.require(:user).permit(:email, :password, :password_confirmation)
    end
end

user_paramsというストロングパラメーターで許可されたパラメーター以外を使用しようとするとActiveModel::ForbiddenAttributes例外が発生します。

データの保持

コントローラーでデータの保持を行う方法は3つあります。いずれも通常のハッシュのようにキーと値を組み合わせて使います。使い方はほとんど同じですが、それぞれで有効期限が異なります。なお、キーとなる文字列は何でも構いません(日本語でも可)。

セッションやクッキーは暗号化されて保存されます。復号化にはCredentialsのsecret_key_baseの値が使用されます。secret_key_baseの値を変更してしまうとセッションやクッキーの値が復元できなくなってしまうのでご注意ください。

Flash(フラッシュ)

フラッシュの有効期限は次のリクエストが終わるまでです。コントローラーで行った処理の結果を簡単にユーザーに伝えるためなどに使われます。

class UsersController < ApplicationController
  def create
    @user = User.new(user_params)

    if @user.save
      flash[:notice] = "新しいユーザーを作成しました。"
      redirect_to user_path(@user)
    else
      flash[:alert] = "新しいユーザーの作成に失敗しました。"
      redirect_to root_path
    end
  end
end

Session(セッション)

セッションの有効期限はブラウザを閉じるまでです。ユーザーのログイン状態を一時的に保持しておくためなどに使われます。以下の例では、ヘルパー内でユーザー情報の登録/破棄を行っています。

module SessionsHelper
  def log_in(user)
    session[:user_id] = user.id
  end

  def log_out
    session.delete(:user_id)
  end

クッキーの有効期限はクライアントが明示的に削除するまでです。ユーザーのログイン状態を永続的に保持しておくためなどに使われます。以下の例では、ヘルパー内でユーザー情報の登録/破棄を行っています。

module SessionsHelper
  def remember(user)
    user.remember
    cookies.permanent.signed[:user_id] = user.id
    cookies.permanent[:remember_token] = user.remember_token
  end

  def forget(user)
    user.forget
    cookies.delete(:user_id)
    cookies.delete(:remember_token)
  end
end

アクションの前後に行う処理の定義

アクションを実行する前、後、その両方に行う処理を定義することができます。ここでは、よく使うであろうbefore_actionの使い方のみ記述します(after_actionaround_actionの使い方もほとんど同じです)。

class DashboardsController < ApplicationController
  before_action :require_login

  def index
    @user = current_user
  end

  private
    def require_login
      redirect_to login_path if !logged_in?
    end
end

上記の例では、管理ページをレンダリングするアクションの前に、require_loginメソッドで管理者としてログインしているかのチェックを行い、ログインしていなければログインページにリダイレクトしています。

アクションの前後に行う処理の制限

アクションの前に行う処理を制限することができます。アクションを限定するにはonlyオプションをつけます。また、アクションを除外するにはexceptオプションをつけます(after_actionaround_actionも同様)。

class ArticlesController < ApplicationController
  before_action :require_login, except: [:index, :show]

  # 各アクションは省略

  private
    def require_login
      redirect_to login_path if !logged_in?
    end
end

上記の例では、indexshow以外のアクションでログインチェックを行っています。

ヘルパーメソッドの使用

コントローラーでヘルパーメソッドを使う場合、先頭にview_contextをつけます。

class ArticlesController < ApplicationController
  def index
    @articles = view_context.contentful.entries(content_type: 'article')
  end

  def show
    @article = view_context.contentful.entry(params[:id])
  end
end

まとめ

コントローラーはモデルとビューの橋渡し役に徹し、あまり凝った処理は行わないことが推奨されています。例えば、データベースから取得したデータの加工処理はコントローラーではなくモデルで行ったり、ビューで表示するメッセージをコントローラーでは作成しないようにします。

本記事を参考にして、ActionController(コントローラー)の使い方を覚えていただければと思います。

関連記事

【Rails】Railsアップグレードまとめ
# はじめに Ruby on Railsに限らず、何らかのフレームワークを使ってWebシステムを構築している場合、フレームワークのアップグレード作業は避けて通れません。 一般的にフレームワークはバージョン毎にEOL (End of Life [...]
2022年10月1日 14:32
【Rails】ユーザー登録時に行うメールアドレス認証機能の実装方法
# はじめに ユーザー登録/解除やログイン/ログアウトといった認証機能の導入に「devise」というGemを使っている人は多いと思います。「devise」では以下のように記述するだけで、ユーザー登録時に確認メールを送付しメールアドレス認証を行う機 [...]
2022年9月24日 14:24
【Rails】モデルに列挙型(enum)を定義し、使いこなす方法
# はじめに Railsはモデルでカラム名と同名の列挙型(enum)を定義することで、カラムと列挙型の変数を紐付けることができます。カラムと列挙型の変数を紐付けると、カラムに対して様々な便利な使い方ができるようになります。 本記事では、モデ [...]
2022年9月3日 10:29
【Rails】RailsでCORSとPreflight requestの設定を行う方法
# はじめに RailsアプリをAPIサーバーとして構築するには、CORS (Cross-Origin Resource Sharing)と Preflight requestの設定を行う必要があります。APIサーバーは外部からの要求に対して処理 [...]
2022年8月27日 10:44
【Ruby】Bundlerを使ってRubyGemsを作成/公開する方法
# はじめに Bundlerを使ってRubyGemsを作成および公開する方法について説明します。Bundlerを使わずにRubyGemsを作成/公開する方法については以下の記事を参照してください。 <iframe class="hatena [...]
2022年7月12日 23:18
【Ruby】RubyGemsを作成/公開する方法
# はじめに RubyGemsを作成および公開する方法について説明します。Bundlerを使ってRubyGemsを作成する方法については以下の記事を参照してください。 <iframe class="hatenablogcard" style [...]
2022年7月11日 21:52
【Rails】M1チップ搭載MacでRuby on Railsの開発環境構築
# はじめに M1チップ搭載MacにRuby on Railsの開発環境を構築する手順を記載します。 - MacBook Air (M1, 2020) - macOS Monterey 12.3.1 # Homebrew ## [...]
2022年5月5日 11:56
【Rails】Rakeタスクの基本情報と作成・実行方法
# はじめに Railsには標準でRakeというGemが同梱されています。RakeはRubyで実装されたMake(UNIX系のOSで使用できるコマンド)のようなビルド作業を自動化するツールです。Ruby Make、略してRakeというわけですね。 [...]
2022年3月7日 22:12