みなさんはControllerを使いこなしていますか?
この記事では、Controllerの基本的な使いかたとして、以下のような内容を説明します。
・Controllerの作成方法は?
・Controllerでよく使われる機能は?
・Controllerはモジュール化できませんか?
Controllerには、ViewやModelを統括するというもっとも重要な役割があります。
もっとも重要な役割を粛々と行うためにも、Controllerの基礎をしっかりと学び、ViewやModelとの連携をスムーズにできるようにしておきましょう。
Controllerとは
Controllerは、MVCを構成するコンポーネントの1つです。
MVCは、以下の3つのコンポーネント(構成要素)から構成されるデザインパターンです。
| コンポーネント(構成要素) | 説明 | 備考 |
|---|---|---|
| Model(モデル) | データベースを取り扱う | データベースへの格納方法は、Modelに隠ぺいする |
| View(ビュー) | 画面表示を取り扱う | 表示方法は、Viewに隠ぺいする |
| Controller(コントローラー) | (ユーザーの入力を受けて)ModelとViewにアレコレ指示する | データベースへの格納方法や表示方法は知らない |
Controllerはユーザーの意思を反映したHTTPリクエストを受け取り、ModelやViewと連携しながら、ユーザーに返すデータを決定する処理を担当します。
上の表のとおり、データの追加、削除、更新は、Modelを利用します。
Modelについては、以下の記事をご覧ください。
Controllerの使いかたを理解するためにRuby on Railsをインストールする
Controllerの使いかたを理解するために、Ruby on Railsをインストールしておきましょう。
(1)Ruby on Railsをインストールします。
私は、以下の記事を参考に、VirtualBoxで作成した仮想パソコンにインストールしたLinux Mintに、Ruby on Railsの開発環境を作成しました。
基本的には記事の手順に従って操作しますが、app/samurai/sample1ディレクトリを作成する代わりに、app/samurai/controller-demoディレクトリを作成しました。
また、Ruby on Railsを起動して、ブラウザで画面が表示されることを確認したら、いったんRuby on Railsを終了してから次に進みます。
Linux Mintのインストールについては、以下の記事で詳しく説明しています。
Controllerを作成する
では、Controllerを作成してみましょう。
Controller(+基本的なViewやルーティング)を作成するには、以下のコマンドを使用します。
bin/rails generate controller Controller名 アクション1 アクション2...
Controller名のあとに入力するアクションは複数指定できます。
ルーティングについては、以下の記事を参考にしてください。
では、実際にControllerを作成してみましょう。
(1)「端末」で以下のコマンドを入力します。
bin/rails generate controller Grapes action1 action2
実行結果:
Running via Spring preloader in process 18774
create app/controllers/grapes_controller.rb
route get 'grapes/action2'
route get 'grapes/action1'
invoke erb
create app/views/grapes
create app/views/grapes/action1.html.erb
create app/views/grapes/action2.html.erb
invoke test_unit
create test/controllers/grapes_controller_test.rb
invoke helper
create app/helpers/grapes_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/grapes.coffee
invoke scss
create app/assets/stylesheets/grapes.scss
作成されたファイルの用途は以下のとおりです。
| ファイル名 | 説明 |
|---|---|
| app/controllers/grapes_controller.rb | Controllerファイル |
| config/routes.rb※ | ルーティングファイル |
| app/views/grapes/action1.html.erb app/views/grapes/action2.html.erb | Viewファイル |
| test/controllers/grapes_controller_test.rb | test用ファイル |
| app/helpers/grapes_helper.rb | 独自helper設定用のファイル |
| app/assets/javascripts/grapes.coffee | coffeescript |
| app/assets/stylesheets/grapes.scss | scss |
※config/routes.rbは変更されます。
Controllerの命名規則について
Modelと結びつきがある場合は、Controller名をGrapesのように複数形にします。
Railsでは、使用するファイルなどを名前によって自動的に推測するため、命名規則に従っていないと予期しないエラーが発生してしまいます。
名前を正しく決める必要があり、それだけなら欠点のように思えますが、その代わりに冗長なコードが減るという利点があります。
Controllerを削除する
Controller(+基本的なViewやルーティング)を削除するには、以下のコマンドを使用します。
bin/rails destroy controller Controller名
では、実際にGrapesを削除してみましょう。
(1)「端末」で以下のコマンドを入力します。
bin/rails destroy controller Grapes
実行結果:
Running via Spring preloader in process 19022
remove app/controllers/grapes_controller.rb
invoke erb
remove app/views/grapes
invoke test_unit
remove test/controllers/grapes_controller_test.rb
invoke helper
remove app/helpers/grapes_helper.rb
invoke test_unit
invoke assets
invoke coffee
remove app/assets/javascripts/grapes.coffee
invoke scss
remove app/assets/stylesheets/grapes.scss
rails generate controllerコマンドで作成されたファイルが削除されます。
ここで、config/routes.rbに設定されたルーティングの設定は残っていることに注意してください。
config/routes.rbには、他のControllerのアクションに関するルーティングも記述されているため、ファイルを削除すると問題になるためです。
そこで、config/routes.rbを編集して、Grapesに関するルーティングを削除しましょう
(2)config/routes.rbを編集します。
編集前:
Rails.application.routes.draw do get 'grapes/action1' get 'grapes/action2' # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end
編集後:
Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end
Controllerでよく使われる機能
次に、Controllerでよく使われる機能を紹介しましょう。
params
params[:id]のように書くと、Railsで送られてきた値(パラメータ)を受け取れます。
パラメータの受け渡しは、以下の記事を参考にしてください。
redirect_to
redirect_to action: :example1のように書くと、自動的なページの切り替え(次のアクションの呼び出し)を簡単に実装できます。
redirect_toの使いかたは、以下の記事を参考にしてください。
flash
flash[:notice] = “任意のメッセージ”のように書くことで、任意のメッセージをViewに表示できます。
flashの使いかたは、以下の記事を参考にしてください。
render
render “help”と書くことで、help(app/views/pages/help.html.erb)テンプレートを表示できます。
renderの使いかたは、以下の記事を参考にしてください。
ActiveSupport::Concernを使ってアクションをモジュール化する
一つのControllerにたくさんのアクションを記述しすぎると、コードが管理しにくくなったり、テストに時間がかかりすぎたりする問題が起きます。
そんなときは、ActiveSupport::Concernを使うことで、アクションをモジュール化できます。
Viewの場合は部分テンプレート(partial)を使って小分けにしますが、Controllerではモジュール化して小分けにします。
ここからは、実際にActiveSupport::Concernを使ってみましょう。
動作確認用のWebアプリは、scaffoldで作成します。
scaffoldについてわからない方は、以下の記事を参考にしてください。
(1)「端末」で以下のコマンドを1行ずつ順番に入力します。
bin/rails generate scaffold Banana name:string bin/rails db:migrate bin/rails console
(2)以下のコードを入力します。
Banana.create(name:"キャベンディッシュ") Banana.create(name:"ラカタン") Banana.create(name:"レディ・フィンガー") Banana.create(name:"シマバナナ") Banana.create(name:"プランテン") Banana.create(name:"グロスミッチェル") Banana.create(name:"ハイランド") exit
これで必要なファイルとデータが作成されました。
(3)以下のコマンドを入力します。
bin/rails server
scaffoldを使ってWebアプリを作成すると、ルート構成がRESTfulの考え方に従って作成されます。
app/controllers/bananas_controller.rb:
class BananasController < ApplicationController
before_action :set_banana, only: [:show, :edit, :update, :destroy]
# GET /bananas
# GET /bananas.json
def index
@bananas = Banana.all
end
# GET /bananas/1
# GET /bananas/1.json
def show
end
# GET /bananas/new
def new
@banana = Banana.new
end
# GET /bananas/1/edit
def edit
end
# POST /bananas
# POST /bananas.json
def create
@banana = Banana.new(banana_params)
respond_to do |format|
if @banana.save
format.html { redirect_to @banana, notice: 'Banana was successfully created.' }
format.json { render :show, status: :created, location: @banana }
else
format.html { render :new }
format.json { render json: @banana.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /bananas/1
# PATCH/PUT /bananas/1.json
def update
respond_to do |format|
if @banana.update(banana_params)
format.html { redirect_to @banana, notice: 'Banana was successfully updated.' }
format.json { render :show, status: :ok, location: @banana }
else
format.html { render :edit }
format.json { render json: @banana.errors, status: :unprocessable_entity }
end
end
end
# DELETE /bananas/1
# DELETE /bananas/1.json
def destroy
@banana.destroy
respond_to do |format|
format.html { redirect_to bananas_url, notice: 'Banana was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_banana
@banana = Banana.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def banana_params
params.require(:banana).permit(:name)
end
end
今回はActiveSupport::Concernを使って、これらのアクションの一部を別のファイルに切り出してみましょう。
具体的には、app/controllers/concernsディレクトリにファイルを作成します。
(1)app/controllers/concerns/banana_one.rbを以下の内容で作成します。
module BananaOne
extend ActiveSupport::Concern
# GET /bananas
# GET /bananas.json
def index
@bananas = Banana.all
end
# GET /bananas/1
# GET /bananas/1.json
def show
end
# GET /bananas/new
def new
@banana = Banana.new
end
# GET /bananas/1/edit
def edit
end
end
Railsらしく、module名は、頭文字大文字、単語の境界に「_」無しで、ファイル名は、頭文字小文字、単語の境界に「_」有りという命名規則があります。
上の例のように、module名をBananaOneにした場合は、ファイル名はbanana_one.rbにします。
この命名規則に従っていないと、ActiveSupport::Concernが適切に読み込めません。
(2)app/controllers/bananas_controller.rbを以下のように修正します。
変更後:
class BananasController < ApplicationController
before_action :set_banana, only: [:show, :edit, :update, :destroy]
include BananaOne
# POST /bananas
# POST /bananas.json
def create
@banana = Banana.new(banana_params)
respond_to do |format|
if @banana.save
format.html { redirect_to @banana, notice: 'Banana was successfully created.' }
format.json { render :show, status: :created, location: @banana }
else
format.html { render :new }
format.json { render json: @banana.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /bananas/1
# PATCH/PUT /bananas/1.json
def update
respond_to do |format|
if @banana.update(banana_params)
format.html { redirect_to @banana, notice: 'Banana was successfully updated.' }
format.json { render :show, status: :ok, location: @banana }
else
format.html { render :edit }
format.json { render json: @banana.errors, status: :unprocessable_entity }
end
end
end
# DELETE /bananas/1
# DELETE /bananas/1.json
def destroy
@banana.destroy
respond_to do |format|
format.html { redirect_to bananas_url, notice: 'Banana was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_banana
@banana = Banana.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def banana_params
params.require(:banana).permit(:name)
end
end
(3)ブラウザで「http://localhost:3000/bananas」にアクセスします。

ActiveSupport::Concernでモジュール化したindexアクションもしっかり呼び出されていますね。
同様に、以下のURLにアクセスして、各アクションが呼び出せることを確認してみてください。
- http://localhost:3000/bananas/1(showアクション)
- http://localhost:3000/bananas/new(newアクション)
- http://localhost:3000/bananas/1/edit(editアクション)
このように、include BananaOneで、app/controllers/concerns/banana_one.rbの内容を読み込み、モジュール化する前と同様に呼び出せるのです。
なお、モジュール化したBananaOneは、別のControllerからも呼び出せるため、共通のアクションが複数のControllerに存在する場合にも活用できます。
respond_toを使ってJSONやXMLを取得する
respond_toを使うと、URLにあわせて表示するフォーマットを切り替えられます。
具体的には、以下のようなコードで、htmlとJSONとXMLを切り替えられるようになります。
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
# GET /posts
# GET /posts.json
def index
@posts = Post.all
respond_to do |format|
format.html
format.json {render :json => @posts}
format.xml {render :xml => @posts}
end
end
(省略)
respond_toの使いかたは、以下の記事で詳しく説明していますので、ぜひご覧ください。
まとめ
この記事では、Controllerの使いかたを説明しました。
作成方法と命名規則さえわかっていればControllerを作成できますが、このページで紹介したような機能をしっかりマスターしておけば、Webアプリを開発するときに必要な基本的な機能はカバーできるでしょう。
また、実装していく段階で、ファイルが大きくなりすぎたと思ったらActiveSupport::Concernを活用したモジュール化を検討してはいかがでしょうか。






