Help us understand the problem. What is going on with this article?

Ruby on Rails チュートリアル 第2章 MVCモデル Usersリソース 継承やDBの仕組みを解説

前回の続き

著者略歴
YUUKI
ポートフォリオサイト:Pooks
現在:RailsTutorial2周目

第2章 難易度 ★ 4時間

挫折しないRailsチュートリアルの進め方を先にお読みください↓↓

Railsチュートリアルで挫折しない3つのポイント

この章ではRailsの根幹であるMVCモデルについて解説する。

アプリケーションの計画

ユーザーのモデル設計

ユーザーという概念をデータモデルで表すと、以下の通りになる。

image.png
出典:第2章 Toyアプリケーション

users = データベースのテーブル
行 = レコード
列 = カラム
integer = 整数
string = 文字列

idのintegerには重複のない一意のキーを割り当てる。
name,emailのstringにはそれぞれ文字列を割り当てる。
なお、emailはユーザー名としても使われる。

id,name,emailは「属性」と呼ぶ。
integer,stringは「データ型」と呼ぶ。

マイクロポストのモデル設計

image.png
出典:第2章 Toyアプリケーション

content = idとマイクロポストのテキスト内容を格納するテキスト
text = テキストを入れるデータ型
user_id = マイクロポストと投稿者を関連付ける属性

Usersリソース

今回の章ではscaffoldジェネレーターと呼ばれる、Railsに標準装備されているチート機能で生成する。

$ rails generate scaffold User name:string email:string

読み方
「scaffoldコマンドでUsersリソースを生成し、name属性、email属性をstring型で定義しますよ」
scaffoldコマンドの引数にはリソース名を単数形にしたものを使う点に注意

これを行うことで、「Userモデル」の内容が先程説明した「usersテーブル」のようになる。
idパラメータはRailsによって自動で主キーとして追加される。

次に、データベースをマイグレートする

$ rails db:migrate
== 20181201023516 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0015s
== 20181201023516 CreateUsers: migrated (0.0022s) =============================

このコマンドで生成したusersデータモデルをデータベースに反映させる。
ここまでできたら、rails serverコマンドで、ローカルでhello worldテキストが表示されているかを確認する。

ユーザーページを探検する

scaffoldコマンドを使ったことにより、ユーザー管理用のページが自動生成される。

URLから

/users
/users/new

を辿って見ると、ユーザー一覧、新規ユーザー作成ページが表示されることが分かる。

URL アクション 用途
/users index すべてのユーザーを一覧するページ
/users/1 show id=1のユーザーを表示するページ
/users/new new 新規ユーザーを作成するページ
/users/1/edit edit id=1のユーザーを編集するページ

出典:第2章 Toyアプリケーション

/users/new から、新規ユーザーを作成してみる。


こんな感じに作られる。
users/2に表示されている内容はshowアクションの中身である。

今度はeditページを表示してみる。

編集してUpdateしてみる。

別のユーザーを新規作成してusersページを表示。

あとはDestoryして削除してみる。

スッキリ。

このような挙動を作るのは結構大変なんだけれども、こういった仕組みをscaffold commandで一発構築。ただし、初心者はscaffoldに頼ってはいけないとTutorial師匠のお叱りがあるので、これから仕組みを覚えていこう。

MVCの挙動

Railsの根幹とも言えるMVCの挙動を紹介。
例えば、「/usersにあるindexページをブラウザで開く」という操作をする場合。

これはイメージを頭の中に叩き込んで覚えよう。

image.png
出典:図 2.11: RailsにおけるMVC

①ブラウザから/usersと言うURLリクエストをRailsサーバーに送信
②/usersリクエストは、RouterによってUsersコントローラ内のindexアクションに割り当てられる。
③indexアクションが実行され、user.rbからUserモデルにuser.allで全てのユーザーを取り出すよう問い合わせる
④Userモデルが全ユーザーをデータベースから取り出す
⑤取り出したユーザー一覧をコントローラに返す
⑥Usersコントローラはユーザーの一覧を@users変数に保存し、indexビューに渡す。
⑦indexビューが起動、ERB拡張子を実行して、HTMLを作成する。
⑧コントローラは、ビューで生成されたHTMLを受け取り、ブラウザに返す。

例えば、routes.rbをこのように変更する。

routes.rb
Rails.application.routes.draw do
  resources :users
  root 'users#index'
end

すると、ルートにアクセスするとusersコントローラのindexアクションの中身が表示される。

それ以外にもcreate、update、destroyアクションがあり、これらはページを出力せずに内部で動いている。

ここで、Usersコントローラのindexアクションのコードを見てみる。

users_controller.rb
def index
  @users = User.all
end

このコードの意味は
「Userモデルから全てのレコードを取り出し、それを@usersと言う変数に代入する」

メソッド内で変数を定義する際は、@を付ける点に注意。
これを「インスタンス変数」と言う。
インスタンス変数には、initializeメソッドとオブジェクトのインスタンスメソッドだけアクセスできる

ちなみに、Active RecordというRubyライブラリのお陰で、userモデルがUser.allというリクエストを実行できる(DBから引っ張ってこれる)

最後に、indexアクションに対応しているviewを見てみる。

index.html.rb
  <tbody>
    <% @users.each do |user| %>
      <tr>
        <td><%= user.name %></td>
        <td><%= user.email %></td>
        <td><%= link_to 'Show', user %></td>
        <td><%= link_to 'Edit', edit_user_path(user) %></td>
        <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
  </tbody>

@users.each do |user|の部分に注目。
この部分で、インスタンス変数@usersを読み出し、 each do |user|で、user変数に繰り返し表示している。
user.nameuser.emailでは、User変数の中の名前、メールアドレスを表示している。

|user|の部分はブロックパラメータと呼ぶ。

Usersリソースの欠点

scaffoldで作成したUsersリソースの問題は以下

  • データの検証が行われていない

ユーザー名が空欄だったり、デタラメなメールアドレスでも通る(例えばasdfadsfds←こんなんでも通る)

  • ユーザー認証が行われていない

誰でも無制限に操作できてしまう。この問題を回避する為には、ログインログアウト機能を導入する

  • テストがお粗末

満足にテストコードが書かれていない

  • レイアウトが酷い

見た目がダサいよね。

  • 理解が困難

scaffoldで生成されたコードの理解は大変。

これらの問題を、Tutorialを通して解決して学んでいく。

Micropostsリソース


Micropostsリソースでも同じことをやってみる。

rails generate scaffold Micropost content:text user_id:integer
$ rails db:migrate
== 20181202035043 CreateMicroposts: migrating =================================
-- create_table(:microposts)
   -> 0.0051s
== 20181202035043 CreateMicroposts: migrated (0.0060s) ========================

micropostsテーブルが作成された。
ここでroutes.rbを確認。

routes.rb
Rails.application.routes.draw do
  resources :microposts
  resources :users
  root 'users#index'
end

resourcesにmicropostsが追加されている。
このルーティングの意味は、マイクロポスト用のURIをMicropostsコントローラ内のアクションに割り当てる。
ほんとは個別で用意するけどresourcesが自動で割り当てる。

ここで、newページを開いてマイクロポストを作成してみる。


user_id1が作成したmicropostsページが表示された。

マイクロポストへのバリデーション

マイクロポストの入力欄の最大文字数を制限する

micropost.rb
class Micropost < ApplicationRecord
  validates :content, length: { maximum: 140 } #contentに入力できる文字数を最大140文字に制限する。
end

マイクロポストで141文字以上の文字列を投稿してみる。

文字列が長すぎるとエラーが出る。
先程設定したバリデーションが機能していることが分かる。

データモデル同士の関連付け

Userモデルと、Micropostモデルの関連付けを行う。

user.rb
class User < ApplicationRecord
    has_many :microposts  #Micropostモデルと関連付け
end

micropost.rb
class Micropost < ApplicationRecord
  belongs_to :user  #Userモデルと関連付け
  validates :content, length: { maximum: 140 }
end

has_manyとbelongs_toの関係性は多重度で言うところの1対多である。
一人のユーザーに対し、多くの投稿が存在するイメージ。

Rails Console

ターミナルでrails consoleコマンドを打つと、Railsを実験的に動かすことができる。

今回はDBからuserモデルに格納されている一人目のユーザー情報取り出し、
first_user変数に格納する。

$ rails c
Running via Spring preloader in process 5117
Loading development environment (Rails 5.1.6)
$ first_user = User.first
  User Load (0.1ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
 => #<User id: 5, name: "moku", email: "yuukitetsuyanet@gmail.com", created_at: "2018-12-01 07:10:12", updated_at: "2018-12-01 07:10:12"> 
$ first_user.microposts
  Micropost Load (0.1ms)  SELECT  "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? LIMIT ?  [["user_id", 5], ["LIMIT", 11]]
 => #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 4, content: "こらこら", user_id: 5, created_at: "2018-12-02 10:47:21", updated_at: "2018-12-02 10:47:21">]> 
$ micropost = first_user.microposts.first
  Micropost Load (0.2ms)  SELECT  "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."id" ASC LIMIT ?  [["user_id", 5], ["LIMIT", 1]]
 => #<Micropost id: 4, content: "こらこら", user_id: 5, created_at: "2018-12-02 10:47:21", updated_at: "2018-12-02 10:47:21"> 
$ micropost.user
 => #<User id: 5, name: "moku", email: "yuukitetsuyanet@gmail.com", created_at: "2018-12-01 07:10:12", updated_at: "2018-12-01 07:10:12"> 
2.4.1 :005 > exit

初見だと「なんじゃこりゃ!」ってなるかも知れんが実際は簡単なことやってる。

①最初のコマンドでfirst_user変数にuserモデルの一人目のユーザーを代入。
first_user.micropostsでid5に関連付いたmicroposts(今回はid4)を読み出す
micropost変数にfirst_user.microposts.firstでid4のmicropostsの中にある最初の投稿を代入
micropost.userで「id4の投稿」をしたユーザー情報を表示
exitでrails consoleの終了

Rails Consoleからは実際にデータベースを弄る事も可能なので、micropostsを新規作成したり、userを追加したりすることも可能。この辺は都度調べてやってみると良い。

継承

これまで、UserとMicropostと言う二つのクラスが紹介されたが、この二つはApplicationRecordとは親子関係に当たる。この親子関係は、以下のように表す。

user.rb
class User < ApplicationRecord
micropost.rb
class Micropost < ApplicationRecord

さらにApplicationRecordはActiveRecord::Baseを継承している。

image.png
出典:図 2.18: UserモデルとMicropostモデルの継承階層

コントローラも同じような親子関係になっている。

users_controller.rb
class UsersController < ApplicationController
microposts_controller.rb
class MicropostsController < ApplicationController

image.png
出典:図 2.19: UsersコントローラとMicropostsコントローラにおける継承関係

だから何?って話だが、要は親クラスで定義した設定は子クラスでも使えるから、どの子クラスでも扱うような共通事項は親クラスにまとめた方が効率的ですよ、ということ。

この仕組みを「継承」と言う。

継承については第4章で詳解する。

デプロイ

前回同様、リポジトリをBitbucketとherokuにpushする。
今回の注意点はherokuへのデプロイ。

toy_appを作成してからherokuをインストールしていないので、インストールする。

$ source <(curl -sL https://cdn.learnenough.com/heroku_install)

で、push後にデータベース起動のコマンドを打つ

$ heroku run rails db:migrate

これで、heroku open でアプリケーションをひらけばOK。

ちなみに、「なんでherokuインストールしていないのにpushできるねん?」
と疑問思うかも知れないが、herokuのリモートが登録されていればpushできる、ということを覚えておこう。

第3章へ

単語集

  • Usersリソースとは

データモデルとモデル表示の為のWebインターフェイスを組み合わせたもの。
UsersリソースによってユーザーをHTTPプロトコル(ポート番号80)経由で自由にCRUDできる「オブジェクト」とみなすことができるようになる。

  • scaffold

自動でCRUDできて、なおかつコントローラー、モデル、ビュー、ルーティングの仕組みを用意できてしまうチート機能。

  • CRUD

CREATE/READ/UPDATE/DELETE のまとまりと言えば分かるでしょう。

  • パラメータ

メソッドや関数に定義される値のこと。
関数の定義の中で関数に渡す値はパラメータ、関数を呼び出すときに渡す値を引数、と考えると分かりやすい

  • 主キー

idに定義されるキー値のトップ。階層構造で言う上の部分。
英語ではprimary keyと呼ぶ
主キーには他のレコードと同じ値になってはならない(ユニークな情報を持っている)ことや、
NULL(空の情報)を登録できないと言う性質がある。
主キーをリレーションシップで外部キーと繋げて、外部の表から情報を読み込んだりする。

  • ビルド

ソースコードを書く所から実際のプログラムを作るまでの一連の工程のこと。

  • HTTP requestメソッド

HTTPプロトコルでリクエストされるメソッドのこと。

GET
HEAD
POST
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH

などがある。
これらはHTTPサーバーのレスポンスを処理する。

  • REST

REpresentational State Transferの略。Webサービスを設計するときのモデルで、以下の原則がある。

  • アドレス指定可能なURIで公開されていること
  • インターフェース(HTTPメソッドの利用)の統一がされていること
  • ステートレスであること
  • 処理結果がHTTPステータスコードで通知されること

出典REST入門 基礎知識

  • バリデーション

フォームなどから送信される処理に何らかの制限を加える機能のこと。

具体的には、
①入力内容をチェック
②予め設定したルールに反してないか確認する
③true ルールに合致してれば許可 false ルールに反していれば拒否

このような流れで処理が行われる。

  • lengthメソッド

文字列の長さを求めるメソッド。

使い方

ruby = "こんにちは" #rubyに文字列"こんにちは"を代入する。
ruby.length()    #rubyに格納されている文字列の数を表示する
 => 5
  • オブジェクト指向とは?

RubyやJavaの言語のそもそもの仕組み。いわゆる「物」を基準に考えることで、オブジェクト指向のプログラミング言語には以下のような特徴がある。

  • オブジェクト指向(OOP)
    • クラスやインスタンスの存在
    • 継承
    • カプセル化
    • 多相性

参考:初心者向けに徹底解説!オブジェクト指向とは?

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away