1. earth660

    No comment

    earth660
Changes in title
-rails 基礎講義資料2章 Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
+Before Rails Tutorial2章 Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
Changes in body
Source | HTML | Preview
@@ -1,611 +1,611 @@
+本投稿は講義資料であり、Rubyの基礎は理解しているが、rails tutorialで躓く読者を対象としています。
[1章:環境構築](https://qiita.com/earth660/items/a51874eba618e2aa5ce6)
[2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解](  https://qiita.com/drafts/c791f1e46875e8a9994e)
[3章:Scaffold を用いない開発方法 及び 応用](https://qiita.com/earth660/items/161fa9090860cbd174bf)
#1.Scaffold
この章では、Railsの強力な機能をいくつか紹介するためのアプリケーションを作成します。
-大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使って**蔵書管理アプリケーション**を生成し、
-それを元にRailsの概要を一緒にみていきましょう。
+大量の機能を自動的に生成するscaffoldジェネレータというスクリプトを使って**蔵書管理アプリケーション**を生成し、それを元にRailsの概要を一緒にみていきましょう。
*3章以降では、scaffold は使わずに、自力で作る方法を学びます。
#### scaffold コマンド
まずは、名前と学籍番号を登録できるだけの機能を作成します。
**下記のコマンドは一字一句間違いなくタイプするか、コピペで行ってください。**
>失敗した場合は、下記のページに解決策が記載してあります。(すこし複雑です)
>[scaffold作成時にカラム名(属性名)を打ち間違えた場合の修正手順](https://qiita.com/jnchito/items/ab17ed02c82f86077022)
```terminal:terminal
$bin/rails generate scaffold User name:string number:integer
```
Railsのscaffoldは、rails generateスクリプトにscaffoldコマンドを渡すことで生成されます。scaffoldコマンドの引数には、**リソース名を単数形にしたもの** (この場合はUser) を使い、必要に応じてデータモデルの属性をオプションとしてパラメータに追加します
> リソースとは、コントローラが扱う対象に名前をつけたものです。
> 例:モデル、画像、セッション...etc
自動で多数のファイルが生成されたとおもいます。
その中で重要なのは次のファイルです。
```terminal:terminal
create app/models/user.rb
create app/views/users/(略)
create app/controllers/users_controller.rb
route resources :users
```
ここで生成された Models Views Controllers の頭文字をとって
MVC と呼びます。MVCについては後ほど、説明します。
####データベースの利用
このアプリケーションではデータを保存するために、データベースを扱います。
下記コマンドを実行することで、データベースに users という名前のテーブルが作成されます。
```terminal:terminal
$bin/rails db:migrate
```
下記のような結果が表示されていればテーブルの生成ができています。
(migrate については今後の章で解説します)
```terminal:terminal
== CreateUsers: migrating =============================
-- create_table(:users)
-> 0.0007s
== CreateUsers: migrated (0.007s) =============================
```
>コマンド実行後に、結果の文章を読む癖もつけていきましょう
>初学者が陥る問題に、terminalにエラー文が表示されているにも関わらず気付かない問題があります。
>エラー文を見つけた場合は、エラー文をそのまま検索することで解決策を見つけることも難しくありません。
>コマンド実行の結果、どういったことが行われているのかを見ることも理解を深めることに役立ちます。
いま実行した2行で下記の機能が自動的に生成されています。
・ユーザーの作成
・ユーザーの一覧表示
・ユーザーの編集
・ユーザーの削除
試しにサーバーを起動してみましょう。
```terminal:terminal
$bin/rails server
```
ブラウザで開いてみましょう。
下記URLにアクセスすると。それに対応した機能が呼び出されます。
一覧表示
 http://localhost:3000/users
新規作成
 http://localhost:3000/users/new
ユーザー表示
 http://localhost:3000/users/1
ユーザー編集
 http://localhost:3000/users/1/edit
たった2行で想像以上の機能が実装されてしまい驚きと困惑があると思います。
多くの初学者が、何が起きたのかわからず Rails を魔法のように感じてしまいます。
自動で作られたファイルについて、簡略的な説明を行っていきます。
3章以降で、いちからつくるので事前に概要を知っておくことは、
今後の理解を深める上で重要です。
#### ルーティング
まずはどの機能が、どのURLと紐付いているかですが、
下記のコマンドで確認できます。
```terminal:terminal
$bin/rails routes
```
下記のような表示がなされていると思います。
```
GET /users(.:format) users#index
POST /users(.:format) users#create
GET /users/new(.:format) users#new
GET /users/:id/edit(.:format) users#edit
GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
```
よくみると、show update destroy がすべて /users/:id という同じURLになっています。
同じURLでも機能が異なるのは、利用しているHTTPメソッドが異なるためです。
>show : GET データの取得
>update : PUTデータの送信(主に更新の際)
>destroy : DELETE:データの削除
> HTTPメソッドについて、もっと知りたい方はこちらをご覧ください
> https://tsuyopon.xyz/2019/01/31/understand-4-http-methods/
この紐付け(ルーティング)は、scaffold によって自動的に行われたものですが、
自力で行う場合には config/routes.rb の中に結びつけを書いていきます。
いまは **紐付けは routes.rb で行われている。** とだけ覚えてもらえば大丈夫です。
3章以降で詳しく説明していきます。
紐付けと言われても、すこし伝わりづらいとおもいます。
ルーティングの見方を、例をあげて説明します。
[/users](http://localhost:3000/users) に対するルーティングをみると
GET /users(.:format)      users#index
と書かれています。これは **usersコントローラーの index アクション**を呼び出す
と理解してください。
コントローラー (app/controllers/users_controller.rb) を見てみると下記のコードが書かれています。
```ruby:users_controller.rb
def index
@users = User.all
end
```
 Userモデルからもらったすべてのデータを、変数@users に代入せよ
 という意味です。
 開かれる htmlファイルはどこにあるのでしょう?
 
 実は Rails では、特に指定をしない場合、
 **アクション名.html.erb が自動的に呼び出されます。**
 重要なので覚えておいてください。
 app/views/users/index.html.erb が呼びされます。
ここまでの流れをまとめると、下記のようになります。
> ①URLにアクセスがあった場合
> ②ルーティングに従ってコントローラーが呼び出され
> ③モデルを経由してデータを受け取り、コントローラーにデータを返し
> ④コントローラーのアクションに従って、データがビューに渡され
> ⑤ビューは受け取ったデータを利用して、HTMLを生成する
上記は、Rails を理解する上で重要な流れなので、覚えておいてください。
MVC(Model View Controller)については後ほど詳しく説明しますが、
**Modelは、データベースとのやりとりを担う**
**Viewは、表示関連全般を担う**
**Controllerは、橋渡しの役割を担う**
と、大まかにご理解ください。
理屈だけより実際に触れてみると理解が深まるとおもうので、
一連のながれを体感するために、View と Controller を触っていきましょう。
#2.View
####ページの編集
ブラウザから一覧ページを開いてみましょう
http://localhost:3000/users
まずは、メンバー一覧表示ページを編集してみましょう。
編集するファイルは、app/views/users/index.html.erb です。
3行目 h1タグ内の、 Users の部分を下記のように置き換えてみましょう。
```html:index.html.erb
<h1>ユーザーの一覧</h1>
```
編集が完了したら、上書き保存し、ブラウザを更新してください。
編集が反映されていれば成功です。
####erb
ERBとは、Embedded RuBy の略であり、Rubyを埋め込めるHTMLファイルのようなものと
いまは思っておいて頂いて結構です。
>ERBは、HTMLテンプレートエンジンと呼ばれ、他にも **haml** **slim** などあるので
>興味がある方は、調べてみてください。
Ruby の埋め込みも体験してみましょう。
erb 内に下記のコードを記述することで、その中にrubyを埋め込むことができます。
<% %> この中にRubyコードを記述できる。
<%= %> この中に書かれた Ruby は文字列として出力される。
#### erb:変数の利用
index.html.erb の1行目に下記の2行を追加してください。
```erb:index.html.erb
<% name = "Player" %>
<%= "ようこそ #{name} さん" %>
```
Ruby と同様に、変数の代入と展開が行われ
ようこそ Playerさん と表示されます。
####Timeクラスの利用
下記のように追加すると、今日の日付が表示されます。
```erb:index.html.erb
<% t = Time.now %>
<%= "いまの時刻は #{t} です" %></br>
<%= "今日は #{t.month} 月 #{t.day} 日です" %></br>
<%= "一週間前は #{t.weeks_ago(1)} 日です" %></br>
<%= "一週間前は #{t.weeks_ago(1).month} 月 #{t.weeks_ago(1).day} 日です" %>
```
[Timeクラスについて詳しく](https://qiita.com/prgseek/items/c0fc2ffc8e1736348486)
####【課題1】 erbの変更
5分ほど使って index.html.erb を自由に変更してみてください。
変更例)日本語化
1, name,number をそれぞれ、名前,学籍番号 に変更
2, New user を、新規ユーザー作成 に変更
3, Show,Edit,Destoroy を 表示、編集、削除 に変更
4, Ruby のコードをerbの中に書いてみる...etb
つづいてコントローラーをみていきましょう。
#3.Controller
#### Controller から View への、データの受け渡しを確認
Controllerは、**クライアント** **Model** **View** の仲介を担っています。
**Controller名は、複数形にする(命名規則)**
再度、コントローラー (app/controllers/users_controller.rb) の下記の行をみてください。
```ruby:users_controller.rb
def index
@users = User.all
end
```
 これは、Userモデルからもらったすべてのデータを、変数@users に代入せよ
 という意味でした。
今回はデータベースを使わずに、データを作成してみましょう。
(このあと Model の項目で、データベースを利用するように置き換えます)
@book という連想配列を作成し、その中に情報を書き込んでいきます。
```ruby:users_controller.rb
def index
@users = User.all
@book = Hash.new
@book[:country] = "Japan"
@book[:title] = "吾輩は猫である"
@book[:author] = "夏目漱石"
@book[:year] = 1905
@book[:book_id] = 1
@book[:comment] = "おもしろかった"
end
```
> @book[:title] Hash(連想配列)のSymbolです。お忘れの方は
> [Array(配列)とHash(連想配列)入門](https://qiita.com/kentarok/items/5d38c3f7df37a7396ef9)
> を御覧ください。
本の情報を @book 連想配列に追加したので、 View で受け取れるか確認してみましょう。
(def **index** に記載しているので、 **index**.html.erb で受け取れます)
```erb:index.html.erb
<%= @book %></br>
<%= "本のタイトルは #{@book[:title]} です" %></br>
<%= "著者は #{@book[:author]} です" %></br>
```
http://localhost:3000/users
ブラウザを更新すると下記のように表示されます。
 {:country=>"Japan", :title=>"吾輩は猫である", :author=>"夏目漱石", :year=>1905,
  :book_id=>1, :comment=>"おもしろかった"}
 本のタイトルは 吾輩は猫である です
 著者は 夏目漱石 です
無事、情報を受け取れていますね。
#### erbの応用
ここに和書かどうかを調べる機能を追加してみましょう。
**erb はRubyが使えるので、条件分岐も利用できます**
下記のコードを追加してみましょう。
```erb:index.html.erb
<% if @book[:country] == "Japan" %>
<p>この本は、和書です</p>
<% end %>
```
 この本は、和書です
と表示されます。
すこし蔵書管理アプリっぽくなりました。
蔵書データを増やしていきたいところですが、問題があります。
Controller に書くには蔵書データはあまりに多すぎるという問題です。
(そしてユーザーは追記できない)
これを解決するのが、データベースと**Model**です。
**@book に関するコードは、すべて消してしまいましょう。**
#4.Model
####Books リソースの作成
Modelは基本的に、1つのテーブルに対して1つのModelを作成します。
**Model名は、複数形にする(命名規則)**
人に関するリソースは作成したので、本に関するリソースも作成していきましょう。
(usersテーブルとは、別にbooksテーブルを作成するので、Book Modelを作成します)
再度、Scaffold を実行します。
```terminal:terminal
$bin/rails generate scaffold Book country:text title:text author:text year:integer user_id:integer comment:text
```
Users リソースの時と同様に、自動で多くのファイルが作成されます。
Model View Controller route もそれぞれ作成されています。
```terminal:terminal
create app/models/book.rb
create app/views/books/(略)
create app/controllers/books_controller.rb
route resources :books
```
前回同様、下記コマンドを実行することで、データベースに books という名前のテーブルが作成されます。
```terminal:terminal
$bin/rails db:migrate
```
ルーティングも確認してみましょう。
```terminal:terminal
$bin/rails routes
```
下記のように、紐付けされていることが確認できます。
```
GET /books(.:format) books#index
POST /books(.:format) books#create
GET /books/new(.:format) books#new
GET /books/:id/edit(.:format) books#edit
GET /books/:id(.:format) books#show
PUT /books/:id(.:format) books#update
DELETE /books/:id(.:format) books#destroy
```
ブラウザで確認してみましょう。本の情報を入力できるようになっています。
http://localhost:3000/users/new
このままではユーザーは自由に投稿できる状態です。
year と user_id は数値を入力してもらいたいので入力制限をかけてみましょう。
Rails では制限(validation)も簡単に設定することができます。
#### Validation
app/models/book.rb を開き下記のように編集してください。
```ruby:book.rb
class Book < ApplicationRecord
validates :year, :user_id, numericality: true
end
```
この1行だけで制限を設けることができます。
Book Model に追記しているので、 books テーブルの yearカラム と user_idカラムに対して制限をかけます。
実際に試してみると、下記のようなエラー文が表示されます。
![validate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33092/363df694-895d-1b0c-15d0-3dff20c39037.png)
####【課題2】自作バリデーション
[Railsバリデーションまとめ](https://qiita.com/h1kita/items/772b81a1cc066e67930e)
を参考にして、制限を設けてみましょう。
例)
1, Title を空白禁止にする
2, 著者の名前を125文字以内にする ...etc
バリデーションがうまくかけたかを試すために、
実際にブラウザから本のデータを2件登録してみてください。
**内容は自由で良いですが、 user の項目だけは 1 にしてください**
## rails console
うまく情報が登録されたかを確認するために、データベースから情報を取り出してみましょう。
SQL文が苦手な方もご安心ください。
Rails console を使えば、簡単にデータを取り出してみることができます。
新規ターミナルを起動し、下記のコマンドを実行してください。
```terminal:terminal
$bin/rails c
```
すると **>>** といった表示がされるとおもいます。これでrails console が起動しました。
( **exit** 入力し、Enterを実行すると終了します )
試しに、最初に登録したユーザー情報を、(Model を経由し)データベースから取り出してみましょう
```ruby:railsConsole
>> user = User.find(1)
```
user はただの変数です。(名前は自由ですが、わかりやすく use にしました)
User.find(1) は **User Model を利用して、 1番目の人を取り出せ**
という意味ですので、 U は大文字です。(Model は頭文字が大文字で単数形でしたね)
さきほど、ブラウザから入力したデータが登録されていると思います。
####【課題3】 rails c を使った、データの取り出し
本のデータを取り出して表示してみましょう。
その際に、find(1) 意外の取り出し方も試してみましょう。
[データ取得メソッドまとめ](https://programming-beginner-zeroichi.jp/articles/61)
例) .all を使用してみる。
#MVC総合 User が所持しているデータを表示してみる。
さてここまでで、冒頭に説明した下記の役割を体験してみました。
**Modelは、データベースとのやりとりを担う**
**Viewは、表示関連全般を担う**
**Controllerは、橋渡しの役割を担う**
なにもわからなかった状態から、
ルーティング・DB・コンソール・MVC
と多くのことを吸収し、成長を感じられると思います。
まだ大まかな理解だと思うので、更に理解を深めるべく
応用課題に取り組んでいきましょう!
id: 1 のUserが所持している本のデータを取り出してみましょう。
ブラウザでの表示は View を編集する必要があり大変なので
まずは console の中で、取り出しを確認するところだけみていきましょう。
(小さく着実に作っていくことは大事です)
rails console を起動した端末にて下記のコマンドを実行します。
```ruby:railsConsole
>> user = User.find(1)
>> user.books
```
と入力することで、 id:1 のuserが持っている本のデータ
(booksテーブルの中で、 user_id を 1 にした本のデータ)
がすべて表示されます!
.
..
...
されないとおもいます。
実行結果を見てみると、エラー文が出ていますね
```ruby:railsconsole
>> user.books
Traceback (most recent call last):
1: from (irb):2
NoMethodError (undefined method `books' for #<User:0x00007fa63942dce0>)
```
NoMethodError undefined method `books'
これは、 books がなにかわからない。定義されていない。という意味のエラーです。
(今後良くみかけることになるとおもいます。タイピングミスしてもこのエラーが表示されます)
今回の原因は、 **user テーブルと books テーブルが紐付いていない**ことによるものです。
>booksテーブルで勝手に user_id というカラムを追加しただけで、
>userテーブルにはなにも書いていないので当然ですね。
>**Userモデル**から、**bookテーブル**のデータを取得できないのでエラーが出ています
>こういったテーブル同士の紐付けも、Railsでは1行で出来ます。
## has_many belongs_to
いま、 users と books の2つのテーブルがあります。
どの蔵書データが、どのユーザーに紐付いているのかといった関連付けを行いたいとき
どうすれば良いのかを学んでいきます。
**1,一人のuserは、たくさんの本を持っています**
こういう場合、 user.rb (UserのModel)に、
**has_many :books** (たくさんなので、複数形です)
と1行追加するだけで、上記のような紐付けが完了します。
**2,1冊の本は、一人のUserに紐付いています**
こういう場合、 book.rb (BookのModel)に、
**belongs_to :user** (ひとつなので、単数形です)
と追加するだけで、上記のような紐付けが完了します。
```ruby:user.rb
class Book < ApplicationRecord
has_many :books
end
```
```ruby:book.rb
class Book < ApplicationRecord
belongs_to :user
validates :year, :user_id, numericality: true
end
```
has_many belongs_to コード追加が終われば
rails console を立ち上げ直してください
(exitとタイプしEnterで終了できます)
再度 rails c を起動し、さきほどと同じコマンドを実行するとどう変わっているでしょうか。
```ruby:railsConsole
>> user = User.find(1)
>> user.books
```
↓出力結果例
```ruby:railsConsole
Loading development environment
>> user = User.find(1)
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "daichi", number: 10
>> user.books
Book Load (0.1ms) SELECT "books".* FROM "books" WHERE "books"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Book id: 1, country: "Japan", title: "吾輩は猫である", author: "夏目漱石", year: 1905, user_id: 1, comment: "おもしろかった"
```
紐付けられたことでエラーが解消され、上記のような結果が表示されます。
テーブルに関連性を持たせたいときは has_many belongs_to を使用すること
覚えておいてください。
Modelにて、データベース内のテーブルの関連づけは出来き、
user.books にてユーザーが持っている本のデータを取り出せることが確認できました。
さて、Modelで取り出したデータを、View に送るにはどうすればよかったでしょうか?
そうですね。Controller の役割でしたね。
## Controller
app/controllers/users_controller.rb
をひらきます(@bookを消していない人は消しておいてください)
前回は、index.html.erb に情報を渡したかったので index アクションに
@book のデータを(データベースを使わずに直接)書きました。
今回は、show.html.erb に情報を渡したいので、 show アクションに
モデルを用いて、データを取り出すコードを書きます
```ruby:app/controllers/users_controller.rb
def show
@user = User.find(1)
@books = @user.books
end
```
(小文字大文字, s の有無に気をつけてください)
最後に、Controller から送られてきたデータの表示の部分をやっていきます。
## View
表示させたい場所は、1番目のユーザーのページなのでこちらです
http://localhost:3000/users/1
>【MVCの流れの復習】
>ちなみに、ルーティングには下記のように書いてあります
>  GET /users/:id       users#show
>users/(idの数字) にGETリクエストがあったら users controller の show アクションを実行でしたね。
>show アクションが、実行されたら自動的に、 show.html.erb が表示されるのでした。
show.html.erb の一番上に下記コードを追記してください。
```erb:app/views/users/show.html.erb
<% @books.each do |book| %>
<p><%= "本のタイトルは #{book[:title]} です" %></p>
<p><%= "著者は #{book[:author]} です" %></p></br>
<% end %>
```
これで、 Controller から受け取った @books (本の情報が格納された連想配列)
の中身が表示されていると思います。
(配列の中身を順番に取り出していくときは each でした。 Ruby をお忘れの方は復習を)
####【最終課題】
show.html.erb を自由に編集してください
例)
・本の情報をすべて表示
#まとめ
以上で、scaffold を用いた、高速蔵書アプリケーションの制作講座は終わりです。
お疲れ様でした。
3章では、scaffold が自動生成していた部分を、1から自分で制作していきます。
今回の内容を再度読み返すか、もう一度素早く作成して復習をしておいてください。