【Rails入門】Controllerでよく使われる機能を紹介!

みなさんはControllerを使いこなしていますか?

この記事では、Controllerの基本的な使いかたとして、以下のような内容を説明します。

・Controllerとは何か
・Controllerの作成方法は?
・Controllerでよく使われる機能は?
・Controllerはモジュール化できませんか?

Controllerには、ViewModelを統括するというもっとも重要な役割があります。

もっとも重要な役割を粛々と行うためにも、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.rbControllerファイル
config/routes.rbルーティングファイル
app/views/grapes/action1.html.erb
app/views/grapes/action2.html.erb
Viewファイル
test/controllers/grapes_controller_test.rbtest用ファイル
app/helpers/grapes_helper.rb独自helper設定用のファイル
app/assets/javascripts/grapes.coffeecoffeescript
app/assets/stylesheets/grapes.scssscss

※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を活用したモジュール化を検討してはいかがでしょうか。

この記事を書いた人

侍エンジニア塾は「人生を変えるプログラミング学習」をコンセンプトに、過去多くのフリーランスエンジニアを輩出したプログラミングスクールです。侍テック編集部では技術系コンテンツを中心に有用な情報を発信していきます。

目次