おれろぐ #z_a_ki3

(・∀・) オイ!

【復習】Ruby on Rails入門 ~ミニブログを作りながら学ぶWebアプリケーション制作 第3回~

無料動画のオンライン学習サイトのスクーで鳥井 雪(@yotii23)先生が教えてくださる
RoR入門第3回を復習します。

Ruby on Rails入門 ~ミニブログを作りながら学ぶWebアプリケーション制作 第3回~ 鳥井 雪 先生 - 無料動画学習|schoo(スクー)

ゴール

  • Modelの役割、拡張のしかたを身につける
  • 1:多の「リレーション」の考え方を理解する

バリデーション:入力値に制約をつけてみよう

バリデーションとは(Railsとしての)

Railsがリクエストを受けて、コントローラからモデルに渡された値が、
保存(処理?)する条件にふさわしいか検証するModelの機能。

例えば)電話番号がすべて数字であるか?郵便番号の桁数があっているか?

やってみること

  • 「本文(body)」を必須項目にする(ないとエラー)
  • 「名前(name)」に文字数制限をつける(15文字以内)

「本文(body)」を必須項目にする

entry.rb にバリデーションの処理を追加する。

#修正前
class Entry < ActiveRecord::Base
end

#修正後
class Entry < ActiveRecord::Base
  validates :body, presence: true
end

詳細

http://railsguides.jp/active_record_validations.html#presence

「名前(name)」に文字数制限をつける

#修正前
class Entry < ActiveRecord::Base
  validates :body, presence: true
end

#修正後
class Entry < ActiveRecord::Base
  validates :body, presence: true
  validates :name, length: { maximum: 15 }
end

詳細

http://railsguides.jp/active_record_validations.html#length

他にも様々なバリデーションの設定が使えるので、実装しながら調べながら覚えるしかないですね。

http://railsguides.jp/active_record_validations.html

コメント機能を追加してみよう

作りたいものを考える

  • 投稿詳細画面(Showを押下し、遷移する画面)
  • もともとの投稿表示の下に投稿に対する複数のコメントのリスト
  • 新規コメントフォーム

データの名前と種類、関係性を考える

コメントというモデルはcommentという名前とします。
名前(投稿者名)はnameという名前で短い文字列、本文はbodyという名前で長いテキストとします。

短い文字列の形式はstring、長いテキストはtextという形式です。

Model同士の関係性を実現する

データの構造

entriesテーブル

id name body
1 hoge 本文1
2 foo 本文2
3 bar 本文3

commentsテーブル

id name body entry_id
1 hoge 本文1に対するコメント1 1
2 foo 本文1に対するコメント2 1
3 bar 本文2に対するコメント3 2

※関係性を表すデータは、Model名_idというカラムに相手のidを保存する。

commentモデルを生成する

$ rails generate scaffold comment name:string body:text entry_id:integer
$ rake db:migrate

Model同士の関係性をModelに書く

Entryモデル entry.rb

class Entry < ActiveRecord::Base
    has_many :comments
  (中略)
end

Commentモデル comment.rb

class Comment < ActiveRecord::Base
    belongs_to :entry
end

「今回時間が無いので言うとおりにやってください!」

今回はモデルの授業なのでコントローラやビューに関する実装はさくさく進みました。

コントローラ:Entry entries_controller.rb に追加。

# 修正前
def show
end

# 修正後
def show
    @comment = @entry.comments.build
end

entryに属するcommentを用意するメソッドを呼び出している。

この時点ではまだDBに保存されていない。

ビュー:entriesの詳細(show) /entries/show.html.erb に追加。

<h3>コメント</h3>

<% @entry.comments.each do |comment| %>

  <div>

    <strong><%= comment.name %></strong>

    <br />

    <p><%= comment.body %></p>

    <% if comment.persisted? %>

      <p><%= link_to 'Delete', comment_path(comment), method: :delete, data: { confirm: '削除してもよろしいですか?' } %></p>

    <% end %>

  </div>

<% end %>

<%= render 'comments/form' %>

<% @entry.comments.each do |comment| %> でentryに属するcommentを1つずつ取得して、
コメント毎の<div>ブロックを組み立ててます。

<%= render 'comments/form' %>はコメント投稿用のフォームをパーシャルで組み込んでるんですね。

<% if comment.persisted? %>persisted?は保存済みかどうかチェックするメソッドなので、
対象のコメントデータがDBに保存されていればコメントを削除する為のリンクを表示します。

ビュー:commentsのフォーム用パーシャル /comments/_form.html.erb を変更

変更前
<div class="field">
  <%= f.label :entry_id %><br>
  <%= f.number_field :entry_id %>
</div>

変更後
<%= f.hidden_field :entry_id %>

一般的にコメントを投稿するときにどのエントリーに属しているかを投稿者が入力することがないので、
ここでは関連づけの entry_id をhidden(隠し)項目に変更しています。

投稿者が関連づけを変更できてしまうとリレーションがめちゃめちゃになります。

コントローラ:Entry comments_controller.rb を修正。

# POST /comments
# POST /comments.json
def create
  @comment = Comment.new(comment_params)

  respond_to do |format|
    if @comment.save
      # 修正前
      format.html { redirect_to @comment, notice: 'Comment was successfully created.' }
      # 修正後
      format.html { redirect_to @comment.entry, notice: 'Comment was successfully created.' }

投稿完了後のリダイレクト先を @comment.entry でコメントが属しているエントリを取得し、設定している。

※このままだと削除したときにcomment一覧にリダイレクトしてしまうので、destroyも変更すると
完璧だと思います。

# DELETE /comments/1
# DELETE /comments/1.json
def destroy
  @comment.destroy
  respond_to do |format|
    # 変更前
    format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
    # 変更後
    format.html { redirect_to @comment.entry, notice: 'Comment was successfully destroyed.' }

リレーション:has_many, belongs_to について

直訳すると

A has many B : Aは、多くのBを所有する。

Entry has_many comments(EntryはたくさんのCommentsを持っている)

A belongs to B : Aは、Bに属する。

Commentは(ひとつの)Entryに属している。

Comment belongs_to entry(Commentは(ひとつの)Entryに属している。)

has_many <-> belongs_to は対になって1:多の関係性を表す。

宿題

  • Commentモデルにバリデーションを追加してみよう
  • 名前に15文字制限
  • 本文に必須項目制限、140文字制限

これまで宿題はすっぽかしてきたので、しっかりやります。

# 変更前
class Comment < ActiveRecord::Base
  belongs_to :entry
end

# 変更後
class Comment < ActiveRecord::Base
  belongs_to :entry
  validates :name, length: { maximum: 15 }
  validates :body, presence: true
  validates :body, length: { maximum: 140 }
end

この実装だけだと、バリデーションエラーになったら + 投稿する + コメントの詳細画面に飛ぶ + エラー詳細が表示される

な流れでいちいち画面遷移するの気持ち悪いからコメント入力しているエントリの詳細画面に
パーシャルで埋め込んだコメントのフォーム部分でエラー詳細を出したいんだけど、、、

f:id:z_a_ki3:20150304171443p:plain

コントローラを修正するんだろうなと当たりはついているけど、どう直せば良いか
わからない、、、0(:3 )~ =͟͟͞͞(’、3)_ヽ)_

おまけ:Railsのモデルを可視化する。

Graphvizパッケージをインストールする

sudo yum install graphviz

Gemfilerails-erd を追加する。

gem 'rails-erd'

rake erd を実行する。

$ bundle exec rake erd

実行するとカレントディレクトにpdfファイルができます。
文字化けは気にしないでください。

今回のcommentモデル作成時点

f:id:z_a_ki3:20150304171632p:plain

モデルに関連性(has_many, belongs_to)追加時

f:id:z_a_ki3:20150304171726p:plain

ちゃんと関連性がついたのがわかります。便利ですね。

オススメ書籍

紹介されてました電車本です。

RailsによるアジャイルWebアプリケーション開発 第4版

RailsによるアジャイルWebアプリケーション開発 第4版

ただ、Rails3.2対応と古いですし、常にRailsは変化していくので
先生もおっしゃられていましたが Ruby on Rails チュートリアル で 学んだ方がいいと思います。

Ruby on Rails チュートリアル:実例を使って Rails を学ぼう