【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】Paranoiaを使用した論理削除(ソフトデリート)
# はじめに Paranoiaは、Railsアプリケーションで論理削除(ソフトデリート)を実現するためのGemです。 論理削除は、データベースのレコードを物理的に削除するのではなく、削除フラグを設定することで「削除済み」とみなす方法です。こ [...]
2024年7月20日 21:33
【Rails】activerecord-multi-tenantを使用したマルチテナントアプリケーションの作成
# はじめに マルチテナントアプリケーションでは、複数の顧客(テナント)が同じアプリケーションを利用するため、データの分離が必要です。 activerecord-multi-tenantは、このようなマルチテナント環境をサポートするための便 [...]
2024年7月18日 16:50
【Rails】RubyとRailsにおけるattr_reader, attr_writer, attr_accessorの概念と使用方法
# はじめに RubyとRailsの開発において、`attr_reader`,`attr_writer`,`attr_accessor`は非常に便利なメソッドです。これらは、クラス内でインスタンス変数に対するゲッターおよびセッターメソッドを簡単に [...]
2024年7月17日 18:11
【Rails】RubyとRailsにおけるyieldの概念と使用方法
# はじめに RubyとRailsにおける`yield`は、メソッドやテンプレートの中で動的にコードブロックを実行する能力を提供し、これによってコードの再利用性と拡張性が大幅に向上します。本記事では、RubyとRailsにおける`yield`の概 [...]
2024年7月17日 13:15
【Rails】AASMを使用してオブジェクトの状態遷移を効率的に管理
# はじめに Railsアプリケーションにおいて、オブジェクトの状態管理は重要な課題の一つです。AASM (Acts As State Machine) gemは、複雑な状態遷移を効率的に管理します。本記事では、AASMの基本的な使い方を解説して [...]
2024年7月16日 18:00
【Rails】RSpec + Swagger + rswagでアプリケーションのAPIをテストおよびドキュメント化する方法
# はじめに Railsアプリケーションの開発において、APIのテストとドキュメント化は重要な要素です。 RSpecはテストフレームワークとして広く利用されており、SwaggerはAPIの設計とドキュメント化を支援します。これらを統合するr [...]
2024年7月16日 14:27
【Rails】mailcatcherを使用して開発環境でメール送信をテストする方法
# はじめに mailcatcherは、開発環境でのメール送信をキャプチャするためのツールです。ローカルで送信されたメールをブラウザ上で簡単に確認できるようにします。mailcatcherをRailsアプリケーションで使用する方法について説明しま [...]
2024年7月15日 16:37
【Rails】impressionistを使用してページビューやクリック数を追跡する方法
# はじめに impressionist Gemを使用してRailsアプリケーションでページビューやクリック数を追跡する方法について説明します。 # 実装方法 ## impressionist Gemのインストール まず、impre [...]
2024年7月15日 14:18