はじめに
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
Cookie(クッキー)
クッキーの有効期限はクライアントが明示的に削除するまでです。ユーザーのログイン状態を永続的に保持しておくためなどに使われます。以下の例では、ヘルパー内でユーザー情報の登録/破棄を行っています。
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_action
とaround_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_action
とaround_action
も同様)。
class ArticlesController < ApplicationController
before_action :require_login, except: [:index, :show]
# 各アクションは省略
private
def require_login
redirect_to login_path if !logged_in?
end
end
上記の例では、index
とshow
以外のアクションでログインチェックを行っています。
ヘルパーメソッドの使用
コントローラーでヘルパーメソッドを使う場合、先頭に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(コントローラー)の使い方を覚えていただければと思います。