前 基礎Ruby on Rails Chapter9 ActiveRecordの活用/コールバック
次 基礎Ruby on Rails Chapter10 モデル間の関連付け/ブログ機能の追加(モデル編)
スコープ
スコープの記述
- スコープは、検索の方法に名前を付けたもの。
- スコープの定義は、
scope :スコープ名, -> { クエリーメソッド }
のように記述する。 -
open_to_the_public
というスコープを定義して、一般公開の記事を取り出す。
class Article
# 中カッコでの定義方法
scope :open_to_the_public, -> { where(member_only: false) }
class Article
# do、endでの定義方法
scope :open_to_the_public, -> do
where(member_only: false)
end
-
-> {}
はProcオブジェクトを作成する記法。Procとはコードのかたまりを表すオブジェクト。
# Procの定義
p = -> { Math.sqrt rand(100) }
puts p.call
# 引数を取るProcオブジェクト
p = -> (n){ Math.sqrt rand(n) }
puts p.call(100)
# `->{}`の代わりにラムダ式を渡す
p = lambda{ |n| Math.sqrt rand(n) }
puts p.call(100)
- スコープを使うには、モデルのメソッドとして呼び出すと、リレーションオブジェクトを返すようになる。
articles = Article.open_to_the_public.order(released_at: :desc)
# 以下のように順番が変わっても、上記と同じ。
articles = Article.order(released_at: :desc).open_to_the_public
スコープの定義
app/models/article.rb(一部)
# メンバーのみではない(一般公開の記事)一覧を取得
scope :open_to_the_public, -> { where(member_only: false) }
# 掲載開始日時以降で、掲載終了日時以内か掲載終了日時が設定されていない記事一覧を取得
scope :visible, -> do
now = Time.current
where("released_at <= ?", now).where("expired_at > ? OR expired_at IS NULL", now)
end
サイドバーでの記事表示
- 全員にスコープ
visible
の記事一覧を見せる。さらに、ログインしていなければ、open_to_the_public
のスコープも追加する。(メンバー向け記事は見せない)
app/views/shared/_sidebar.html.erb(一部)
<h2>最新ニュース</h2>
<%
articles = Article.visible.order(released_at: :desc).limit(5)
articles = articles.open_to_the_public unless current_member
%>
<ul>
<% articles.each do |article| %>
<li><%= link_to article.title, article %></li>
<% end %>
</ul>
ニュース記事一覧ページの変更
- ログインしていなければ、
open_to_the_public
のスコープを追加する。(メンバー向け記事は見せない) - ログインしていれば、
open_to_the_public
のスコープはかけない。(メンバー向けにかかわらず見られる) - さらに、管理者であれば、スコープ
visible
のスコープをかけない。(掲載開始日時、掲載終了日時にかかわらず見られる)
app/controllers/articles_controller.rb(一部)
def index
@articles = Article.order(released_at: :desc)
@articles = @articles.open_to_the_public unless current_member
unless current_member&.administrator?
@articles = @articles.visible
end
end
ニュース記事詳細ページの変更
- 上記と同様に、showメソッドにもアクセス制限を追加する。
app/controllers/articles_controller.rb(一部)
def show
articles = Article.all
articles = articles.open_to_the_public unless current_member
unless current_member&.administrator?
articles = articles.visible
end
@article = articles.find(params[:id])
end
TopControllerの修正
- トップ画面にも記事を表示しているので、サイドバーと同様の制限をかける。
app/controllers/top_controller.rb
class TopController < ApplicationController
def index
@articles = Article.visible.order(released_at: :desc).limit(5)
@articles = @articles.open_to_the_public unless current_member
end
end
- トップ画面のviewの修正。
- 記事を表示するのに使用している、
truncate
ヘルパーメソッドは、lengthオプションの桁数を超えた場合、切って...
を付ける。
app/views/top/index.html.erb
<% @articles.each do |article| %>
<h2><%= article.title %></h2>
<p>
<%= truncate article.body, length: 80 %>
<%= link_to "もっと読む", article %>
</p>
<% end %>
- トップ画面に記事が表示された。
validate do ... end
- バリデーションの別の書き方。validatesではない。ブロック内に条件を自由に書くことができる。
- 最後にerrorsに
add(属性名のシンボル,エラーの種類)
する。
app/models/article.rb(一部)
validate do
if expired_at && expired_at < released_at
errors.add(:expired_at, :expired_at_too_old)
end
end
- ja.ymlにエラーメッセージを追加する。
config/locales/ja.yml(一部)
expired_at_too_old: は掲載開始日より新しい日時にしてください。
- エラーメッセージが表示された。
ページネーション
Gemパッケージkaminari
- Gemfileにページネーション機能のGemパッケージ
kaminari
を指定する。
Gemfile(一部)
gem 'kaminari'
gem 'kaminari-i18n'
- Bundlerを使ってインストールする。
$ bundle install
- kaminariをインストールすると、モデルのクエリーメソッドに
page
メソッドが追加される。Member.page(2)
のようにすると2ページ目の一覧が取得できる。 - デフォルトで1ページ当たり25件のレコードが取り出される。
per(10)
とすれば10件に変更できる。
@members = Member.page(2)
@members = Member.page(2).per(10)
-
<%= paginate @members %>
で、ページネーションのリンクを埋め込める。
<%= paginate @members %>
ページネーション機能の実装
会員一覧
- 会員一覧と検索画面に
.page(params[:page]).per(15)
を追加する。
app/controllers/members_controller.rb(一部)
def index
@members = Member.order("number").page(params[:page]).per(15)
end
def search
@members = Member.search(params[:q]).page(params[:page]).per(15)
render "index"
end
- 会員一覧のテーブルタグの下に、ページネーションのリンクを追加する。
app/views/members/index.html.erb(一部)
</table>
<%= paginate @members %>
- 会員が10人しかいないので、30人追加する。
- シードデータを以下のように追記して、
bin/rails db:rebuild
コマンドを実行する。
db/seeds/development/members.rb(一部)
0.upto(29) do |idx|
Member.create(
number: idx + 20,
name: "John#{idx + 1}",
full_name: "John Doe#{idx + 1}",
email: "John#{idx+1}@example.com",
birthday: "1981-12-01",
sex: 1,
administrator: false,
password: "password",
password_confirmation: "password"
)
end
- 画面下部にページネーションが表示され、クリックするときちんと動作する。
- cssを追加して、見た目を良くする。
app/assets/stylesheets/pagination.css
nav.pagination {
font-size: 75%;
padding: 4px 8px;
border: 1px solid #499;
word-spacing: 4px;
}
nav.pagination span.current {
font-weight: bold;
}
- 四角で囲まれた。
ニュース記事一覧
- ニュース記事一覧もページネーションを適用する。indexメソッドを修正する。
- 最後の行に、
@articles = @articles.page(params[:page]).per(5)
を追加する。
app/controllers/articles_controller.rb(一部)
def index
@articles = Article.order(released_at: :desc)
@articles = @articles.open_to_the_public unless current_member
unless current_member&.administrator?
@articles = @articles.visible
end
@articles = @articles.page(params[:page]).per(5)
end
- 記事一覧のテーブルタグの下に、ページネーションのリンクを追加する。
app/views/articles/index.html.erb(一部)
</table>
<%= paginate @articles %>
- membersと同様にシードデータを以下のように追記して、
bin/rails db:rebuild
コマンドを実行する。
db/seeds/development/members.rb(一部)
0.upto(29) do |idx|
Article.create(
title: "Article#{idx+10}",
body: "blah, blah, blah...",
released_at: 100.days.ago.advance(days: idx),
expired_at: nil,
member_only: false
)
end
- 画面下部にページネーションが表示され、クリックするときちんと動作する。
- スコープは、権限や期間によって非表示にすることが簡単にできました。
- ページネーションは、Gemパッケージを使うとすぐに実現できるので便利ですね。