• WebCamp_インタビュー
    【卒業生インタビュー】台湾で月間100万UUの訪日旅行メディアを手掛けるCEOが、プログラミングを学んで得たものとは?
    2018.01.15
  • WebCamp_インタビュー
    【WebCamp受講生インタビュー】起業準備中にCEOが気がついたプログラミングスキルの重要性とは?
    2017.12.25
  • WebCamp_インタビュー
    「1カ月頑張ればこの先が見えてくる」地元メディアを立ち上げたママさん
    2017.07.20
  • WebCamp_インタビュー
    「自信の持てるスキルを得たい!」内定辞退した女子大生がプログラミングを学習したワケ。
    2018.04.28
  • WebCampPro_インタビュー
    未経験から上京し、エンジニアとしてチームラボグループに転職!【WebCampPro卒業生インタビュー】
    2018.03.10
  • WebCampPro_インタビュー
    未経験31歳からエンジニア転職を実現【実際に聞いてみた】
    2017.04.01
  • WebCampPro_インタビュー
    自分で稼ぐ力をつけるため、新卒5年目の営業マンがエンジニアに転職!【WebCampPro転職者インタビュー】
    2018.02.02
  • WebCamp_インタビュー
    “未経験”でもたった1ヶ月で営業からエンジニアとして転職!『WebCamp』受講者インタビュー
    2017.10.04
  • WebCampPro_インタビュー
    未経験でも寿司職人からWebデザイナーになった!ホスピタリティでスクールを支える宮脇トレーナー
    2017.05.26
  • WebCamp_インタビュー
    時間や場所にとらわれず自由に働くために必要なスキルとは?【WebCamp卒業生インタビュー】
    2018.01.26
  • WebCamp_インタビュー
    【WebCamp卒業生インタビュー】1ヶ月でRubyをゼロから学び、Webエンジニアとして転職!
    2018.01.15

◆当サイトで人気のプログラミング教室のおすすめランキングはこちら!
プログラミングは独学では非効率で、時間を無駄にするリスクがあります。効率的なカリキュラムで学べるスクールを受講しましょう。

WEBCAMP【マンツーマンサポート】1ヶ月短期集中でプログラミングを学ぶスクール
1ヶ月通い放題・メンター常駐の教室環境でプログラミングを学びたい方!
TechAcademyオンラインで開講しているプログラミングスクール
オンラインでどこでも学べる!/教室に行くのが忙しい人でも安心!
Tech Camp教養としてのITスキルを学べるスクール
Webデザイン/AI(人工知能)/IOS/Androidアプリ制作/VRを学びたい方!
WEBCAMP PRO転職保証付き!エンジニアとして転職したい人におすすめ!
未経験からプロのエンジニアへ3ヶ月で転職する為のスクールです!
12月生募集中!当社人気の転職保証コース
プログラミング学習から転職成功まで導く、当社人気のWebCamp Proコース。
11月生は満員となっております。12月生募集に向け、お早めの申込みをオススメします。
プログラミング未経験でもエンジニア転職を絶対成功させたい
スキルを身に着けて人生を自ら切り開きたい
上記にあてはまる方は、ぜひご検討ください!

はじめに

Railsアプリケーションを作っていく中で、モデルからデータを抽出したり並べ替えたりと行った処理を作成するのは、それほど難しくありません。

しかし、難しくないが故に、同じようなプログラムコードを繰り返してしまっていることはないでしょうか?

Railsの基本理念の1つは、「同じことを繰り返すな (Don't Repeat Yourself: DRY)」です。つまり、同じようなプログラムコードを書いてしまうことは、Railsプログラマーにとっては悪と言えるものでしょう。

そして、DRYを実現するためにとても有効な機能が、Railsにはあります。それが「scope」です。

「scope」を使えば、何度も同じクエリを書く必要はありません。

ここでは、そんな「scope」について、詳しく解説します。

scopeとは

scopeメソッドは、Railsのスコープ機能を利用するためのメソッドです。

そして、スコープ機能というのは、クエリを定義することができる機能で、何度も同じクエリを使うような場合に、メンテナンス性を上げることができます。

また、定義名を適したものにすれば、可読性も上げることができます。

SQLで並べ替えや絞り込みを行う

例えば、商品名と価格が登録されているItemモデルがあったとします。

そこからデータを取得するときに、価格が1000円未満のものだけを取得した場合、以下のようなプログラムコードを記載することになります。

@item = where("price < ?", 1000)

また、価格の降順(高いものから順に)で取得したい場合は、以下のようになるでしょう。

@item = Item.order("items.price DESC")

こういったプログラムコードは必要なシーンが複数になることが多いものです。初期の一覧表示だけではなく、編集項目を選択させるようなシーン、表示方法を変更した場合などもあるでしょう。

そのたびに、これらの1文を追記してしまうと、条件が変わってしまった場合などに変更漏れや変更ミスが起こるリスクがあるだけではなく、すべてのシーンでテストを行うための時間も必要になってしまうのです。

そのため、「同じことを繰り返すな (Don't Repeat Yourself: DRY)」に基づいたプログラムを行わなければいけません。

SQLクエリを定義して使い回す

前述の問題を解決するために、スコープ機能を使ってみましょう。

スコープは、モデルのクラスに定義します。

class Item < ActiveRecord::Base
  scope :under1000, -> { where("price < ?", 1000) }  # => 1,000円未満のレコードを取得
  scope :desc_list, -> { order("items.price DESC") } # => 価格の降順に並んだレコードを取得
end

使用するときは、あたかもモデルのメソッドのように使うことで、該当するレコードを取得できます。

  :
@item = Item.under1000  # => 1,000円未満のレコードを取得
  :
@item = Item.desc_list  # => 価格の降順に並んだレコードを取得
  :

もし、条件が変更となった場合は、定義しているモデルを修正するだけで、他のコントローラやViewなど、スコープを利用している部分は変更する必要がありません。そのため、修正漏れのリスクは低く、テストの時間を大きく減らすことができるでしょう。

環境構築

ここから先、具体的に手を動かしながら確認できるようにするため、環境を作成しておきます。

まずは、以下の手順に沿って、環境の作成とデータの登録を行いましょう。
※scaffoldの詳細や、has_manyについては「【Rails入門説明書】scaffoldについて解説」「【Rails入門説明書】has_manyについて解説」を参照してください

コマンドプロンプトで以下のコマンドを順に実行してください。

rails new scope_test
cd scope_test
rails generate scaffold Seller name:string
rails generate scaffold Item seller_id:integer title:string price:integer
rake db:migrate

rails c
Seller.create(name:"Okada")
Seller.create(name:"Tanaka")
Seller.create(name:"Kimura")
Item.create(seller_id: 1, title:"book", price:500)
Item.create(seller_id: 2, title:"computer", price:200000)
Item.create(seller_id: 2, title:"phone", price:15000)
Item.create(seller_id: 3, title:"bag", price:30000)
Item.create(seller_id: 3, title:"watch", price:15000)
Item.create(seller_id: 1, title:"pen", price:100)
exit

次に、関連付けを設定します。それぞれのモデルファイルに追記してください。
(app/models/seller.rb)

class Seller < ApplicationRecord
  has_many :items
end

(app/models/item.rb)

class Item < ApplicationRecord
  belongs_to :seller
end

サーバーを起動し、ブラウザで「localhost:3000/sellers」「localhost:3000/items」にアクセスすると、以下のように表示されます。

未経験から上京し、エンジニアとしてチームラボグループに転職!【WebCampPro卒業生インタビュー】
2018-03-10 14:42
今回の記事では、たまたま見たテレビ番組がきっかけでエンジニアに興味を持ち、WebCampProを受講して見事転職を決めた中村さんにインタビューしました。 <プロフィール> 中村 駿...

scopeの使い方

ここからは、Railsのスコープ機能について、定義方法や使い方を細かく説明していきます。

ここまでに説明したように、スコープ機能をうまく使うことで、プログラムコードから面倒なクエリ部分を隠し、可読性とメンテナンス性を大きく上げられます。

ぜひ、ここから説明している内容をマスターして、DRYに則ったプログラムを作っていきましょう。

定義方法は2種類

すでに、定義方法は紹介していますが、スコープの定義方法にはscopeメソッドとラムダ式を用いた定義方法と、メソッドを用いた定義方法の2種類があります。

それぞれ、具体的なプログラムコードを紹介しながら説明しましょう。

scopeメソッドとラムダ式を用いた定義方法

すでに紹介した方法が、scopeメソッドとラムダ式を用いた定義方法です。

モデルクラスの中でscopeメソッドに、スコープ名とラムダ式を引数として渡すことで定義します。

構文は、以下です。

scope: スコープ名, ラムダ式

ラムダ式というのは、メソッドをdefで事前に定義するのではなく、特定の処理の中で動的に定義するもので、Javascriptなどで用いられている処理の定義手法です。

ラムダ式の構文は、以下のようになっています。

lambda { |引数| 処理 }    # 基本となる定義方法
-> (引数){ 処理 } # 略式の表現(引数あり)
-> { 処理 }       # 略式の表現(引数なし)

スコープの場合は、「処理」の部分にクエリとなる処理を記載すれば良いわけです。

メソッドを用いた定義方法

ラムダ式を使うことで、1行で定義できますので、コードはシンプルになります。ただし、ラムダ式に慣れていない人には少し可読性が低いかもしれません。

そんな場合は、メソッドの形式で定義することも可能です。

def self.スコープ名(引数)
  処理
end

※引数がなければ、(引数)は省略可能です

先に紹介したラムダ式の形式の定義を、メソッド形式にすると、以下のようになります。

  # 1,000円未満のItem
  def self.under1000
    where("price < ?", 1000)
  end

  # 価格の降順
  def self.desc_list
    order("items.price DESC")
  end

試してみよう

では、テスト環境でスコープ機能を使ってみましょう。

まずは、モデルファイルに定義します。Itemモデルにスコープを追加しますので、手を入れるのは「app/models/item.rb」です。

なお、今回は練習ですので、ラムダ式での定義とメソッドの定義の両方を追加します。
(app/models/item.rb)

class Item < ApplicationRecord
  belongs_to :seller

  # 1,000円未満のItem
  scope :under1000, -> { where("price < ?", 1000) }

  # 価格の降順
  def self.desc_list
    order("items.price DESC")
  end
end

これでスコープの定義ができましたので、このスコープを利用する処理を作成します。ここでは、あまり複雑なことをせず、コントローラのindexメソッドを直接変更します。

まずは、1,000円未満のItemだけ表示してみましょう。
(app/controllers/items_controller.rb)

    :
  def index
#    @items = Item.all
    @items = Item.under1000
  end
    :

「localhost:3000/items」を再読込すれば、1,000円未満のItemだけのリストになっています。

降順表示を試すのは、以下の通りです。
(app/controllers/items_controller.rb)

    :
  def index
#    @items = Item.all
    @items = Item.desc_list
  end
    :

引数を渡す

前述で追加したスコープに、1,000円未満というスコープがあります。

このスコープは価格を元にして表示を絞り込むものですが、1,000円固定というのは、応用が利かなさすぎます。

そのため、一般的には、引数を使って金額部分を可変にするべきでしょう。

スコープ定義の構文にある通り、引数を追加することができますので、それで実現します。
(app/models/item.rb)

class Item < ApplicationRecord
  belongs_to :seller

  # 指定金額未満のItem
  scope :under_price, ->(price) { where("price < ?", price) } # 引数追加

  # 価格の降順
  def self.desc_list
    order("items.price DESC")
  end
end

では、20,000円未満のItemを表示してみましょう。
(app/controllers/items_controller.rb)

    :
  def index
#    @items = Item.all
    @items = Item.under_price(20000)
  end
    :

AND条件として使う

スコープを組み合わせることで、AND条件(両方のスコープの条件に合ったもの)に合致するレコードを取得できます。

その場合は、取得されたレコードに対してスコープを実行しますので、スコープを繰り返して利用する形になります。

「指定の価格以上」のスコープを追加して、100円よりも高くて20,000円未満のItem一覧を表示してみましょう。
(app/models/item.rb)

class Item < ApplicationRecord
  belongs_to :seller

  # 指定金額未満のItem
  scope :under_price, ->(price) { where("price < ?", price) }

  # 指定金額より大きいのItem
  scope :over_price, ->(price) { where("price > ?", price) }

  # 価格の降順
  def self.desc_list
    order("items.price DESC")
  end
end

次のようにして、100円よりも高くて20,000円未満の指定を、コントローラに施します。
(app/controllers/items_controller.rb)

    :
  def index
#    @items = Item.all
    @items = Item.under_price(20000).over_price(100)
  end
    :

joinして、他モデルのスコープを利用する

例えば、10,000円以上のItemを出品しているSellerだけを抽出したいような場合、Sellerのレコード取得時にItemのレコードにあるスコープを利用する必要があるでしょう。

そういった場合には、mergeメソッドを使ってスコープを利用します。

具体的な例を見た方が理解しやすいかと思います。

Sellerモデルにスコープを追加し、その中でItemのスコープを利用します。
(app/models/seller.rb)

class Seller < ApplicationRecord
  has_many :items

  # 1,0000円以上のItemを出品しているSeller
  scope :higher_seller, -> { joins(:items).merge(Item.over_price(10000)) }
end

Itemモデルのときと同じく、コントローラのindexメソッドの処理を変更します。
(app/controllers/sellers_controller.rb)

    :
  def index
#    @sellers = Seller.all
    @sellers = Seller.higher_seller
  end
    :

「localhost:3000/sellers」にアクセスしてみると、10,000円以上のItemを出品しているSellerのリストが表示されています。

※同じSellerが重複しているのは、複数のItemがマッチしているからです。この重複を解消するのは別の対応が必要ですので、ここでは割愛します。

コツコツ独学×スクールで実践。未経験からエンジニアに転職!【WebCamp卒業生インタビュー】
2018-03-22 23:28
今回の記事では、独学でPHPを1年半学習し、その後WebCampを受講して未経験からエンジニアへと転職された佐々木さんにお話を伺いました。 <プロフィール> 佐々木 祐樹 さん(2...

デフォルトスコープ

ここまでは、処理の中で都度スコープを使っていましたが、常にスコープが適用された状態にすることも可能です。

その場合は、scopeメソッドではなく、default_scopeメソッドを使用します。

例えば、Itemに追加したスコープdesc_listを常に適用されるデフォルトスコープにしてみます。

まず、通常はどうなっているのかを改めて確認するため、Itemコントローラのindexメソッドを元に戻しまして、確認しておきましょう。
(app/controllers/items_controller.rb)

    :
  def index
    @items = Item.all
#    @items = Item.under_price(20000).over_price(100)
  end
    :

では、デフォルトスコープを定義します。
(app/models/item.rb)

class Item < ApplicationRecord
  belongs_to :seller

  # 指定金額未満のItem
  scope :under_price, ->(price) { where("price < ?", price) }

  # 指定金額より大きいのItem
  scope :over_price, ->(price) { where("price > ?", price) }

  # 価格の降順
  deafult_scope :desc_list, -> { order("items.price DESC") }  # デフォルトスコープに変更
end

この状態で、改めて「http://localhost:3000/items」を再読込してみましょう。

コントローラでスコープを利用していませんが、価格の降順になっています。

使用上の注意!

デフォルトスコープは、常に適用されるため、プログラムコードのあちこちでスコープを記載する手間が省けます。

しかし、ただ定義するだけで適用されますので、デフォルトスコープの存在に気付けず、思わぬ不具合の元になってしまうことがあります。

特に、複数人で開発しているときなどは、デフォルトスコープの存在を知らない人も出てきますので、その人が意図した表示にならないために解析に多くの時間を浪費してしまうことにもなりかねません。

デフォルトスコープの使用は、慎重に行うようにしましょう。

“未経験”でもたった1ヶ月で営業からエンジニアとして転職!『WebCamp』受講者インタビュー
2017-10-04 10:26
今回の記事では、未経験からWebCampを1ヶ月受講し、その後エンジニアとして転職をした喜田さんにお話を伺いました。 <プロフィール> 喜田 大介 さん (28歳) 大学時代はプ...

まとめ

今回は、scopeについて説明しました。

scopeを利用することで、何度も同じクエリを作成する必要がなく、プログラムのメンテナンス性を上げ、可読性も改善することができます。

 

なにより、Railsの基本理念であるDRY(Don't Repeat Yourself)を実現するためのとても協力な武器になりますので、積極的に使っていくようにしましょう。

・scopeを利用することで、クエリを定義して再利用することができる
・スコープの定義はモデルで行う
・スコープの定義はラムダ式を使う方法とメソッド形式を使う方法がある
・スコープの定義では、引数も使える
・スコープの定義を組み合わせることで、AND条件を表現できる
・他モデルのスコープを使う場合は、mergeを使う
・デフォルトスコープは常に適用されるため、慎重に使うこと

WebCamp・WebCamp Proについて

WebCampは1ヶ月でプログラミング・Webデザインスキルを学ぶ通い放題のプログラミングスクールです。WebCamp Proは3ヶ月間で未経験から即戦力エンジニアを育成するプログラミングスクールです。

2つのサービスを運営するインフラトップでは、「学びと仕事を通して人生を最高の物語にする」という理念で会社を経営しています。

キャリアアップを目指す方は、この機会に私達と一緒にプログラミングを学んでみませんか?

12月枠も残りわずか当社人気の転職保証コース
プログラミング学習から転職成功まで導く、当社人気のWebCamp Proコース。
11月受入枠は満員となっております。12月枠に向け、お早めの申込みをオススメします。
プログラミング未経験でもエンジニア転職を絶対成功させたい
スキルを身に着けて人生を自ら切り開きたい
上記にあてはまる方は、ぜひご検討ください!

▼未経験から1ヶ月でWebデザイン・プログラミングを学びたい方はこちら!

▼ついに開講!オンラインでWebデザインを学びたい方はこちら!

 

【インタビュー】1ヶ月でRubyをゼロから学び、Webエンジニアとして転職!

ブラジルから帰国し技術をつけようとRubyエンジニアを目指してWebCampでRubyを学び、見事Webエンジニアとして転職を果たした田中さんにお話を伺いました。

Rubyの学習がしたい。基礎をしっかりと理解したい

転職のサポートがほしい

と考えている方はぜひお読み下さい。

【WebCamp卒業生インタビュー】1ヶ月でRubyをゼロから学び、Webエンジニアとして転職!
2018-01-15 13:23
今回の記事では、WebCampで1ヶ月間Rubyを学習し、Webエンジニアとして転職した卒業生の田中さんにお話を伺いました。 <プロフィール> 田中 デニス 昭彦さん(...
関連キーワード
Ruby on Railsの関連記事
  • 【Rails入門説明書】includesについて解説
  • 【Rails入門説明書】RubyとRailsの初心者/中級以上用の厳選本8選
  • 【Rails入門説明書】joinsについて解説
  • 【Rails入門説明書】 jQueryの導入方法について解説
  • 【Rails入門説明書】scopeについて解説
  • 【Rails入門説明書】has_manyについて解説
おすすめの記事