Rails1　MVCとは(勉強メモ)

※勉強メモです。

##Rails(Webアプリケーションフレームワーク ) 
ユーザがHTTPリクエストの4つのCRUD（POST、GET、PUT、DELETE）を使って、Web上のリソースを操作するアプリケーション 
Web上のリソースとは、Webページに表示されるすべて 
ただし、ユーザに変更権限があるものとないものがある 

#####・MVC役割構造 
URLとHTTPメソッドを受取り、Controllerに渡すのが、Router 
リソース（データ）として振舞うのが、Model 
Modelを表示・整形するひな形が、View 
ModelやViewを制御するのが、Controller 

#####・Rails開発の流れ 
1.Model：リソースであり操作対象であり扱うデータを決める 
2.Router：Modelを置くURLを決定 
3.ルーティングごとに 
 (1)Controller 
 (2)View 
 
#####・Railsプロジェクト作成コマンド 
```rails new プロジェクト名```  

#####・Bundler  
RubyやRailsのプロジェクトで使用するライブラリの管理ツール 
Gemfileというファイルにインストールしたいライブラリ名と必要なバージョンの一覧を全て書き出し、bundle installで一気にそれらのライブラリをインストールできる 
『gem 'ライブラリ名', 'バージョン指定'』 

```
# バージョン 5.2.x の内、最新をインストールする
gem 'rails', '~> 5.2.2'

# バージョン 0.4.4 以上で 0.6.0 未満の中で、最新のライブラリをインストールする
gem 'mysql2', '>= 0.4.4', '< 0.6.0'

# 最新のライブラリをインストールする（バージョン指定無し）
gem 'spring'
```  

#####・Gitでバージョン管理 
プロジェクト作成後はまずコミットしておく 

(リポジトリの初期化) 
ターミナル、cdで作成フォルダに移動した時(master)と書かれていれば初期化不要。入っていない場合はgit intでバージョン管理を開始

```
$ git add .　＃フォルダ内全て選択
$ git commit -m "init Rails Project"　＃-ｍを付けてcommit
$ git branch -M main　＃ブランチ名をmainに変更  
```  

(REPLとは) 
Read-eval-printloopの略で「読込み-処理-表示」の繰り返しの意味 

ターミナルで```irb```を実行することで、RubyのREPLが起動 
終了はexit  

#####・Rails console 
ターミナルで実行すると、irb(main):001:0>というirbによるRubyコマンド待受状態になる 
コードを変更したらreload!する。reload!コマンドを実行しないと再変更後のRubyコードは反映されない 

#####・サーバの起動```rails s``` 

#####・エラーの発生 
#####Mysql2::Error::ConnectionError
mysql2 の Gem からエラーは発せられていてり、データベースとの連携でエラーが出ていることを予想する 
エラーを解消するために、新規でターミナルのタブを開き、```sudo service mysql start``` で MySQL サーバを起動  

#####Activerecord::NoDatabaseError 
エラーメッセージから、'message-board_development' のデータベース名で作成すれば良さそうだと予想  

#####・MySQL連携の設定ファイルの確認と変更 
Rails のデータベースの接続設定を管理しているファイルは config/database.yml 
この設定をRailsがサーバ起動時に読み込むことで、RailsとMySQLの接続が確立（連携）される  

#####・MySQL接続時のユーザを変更  
初期ではdefaultでrootに設定されているので、MySQLレッスンで作成したdbuserに変更 
config/database.yml の16・17行目あたりにある username と password の設定を以下のとおりに変更し、ファイルを保存  
username: dbuser 
password: dbpass  

#####・データベースの作成  
```rails db:create``` 
👇

```
Created database 'message-board_development'
Created database 'message-board_test'
```  
上記のデータベースが作成され、エラーが解決

#####・サーバを起動してデータベースの接続を確認  
```rails s```  

#####・Model  
リソースであり操作対象。永久保存されるデータのためデータベースと密接に関係している。 
はじめに作成するアプリケーションは、どんなデータを扱うのかを決める。  

#####・Modelwo作成する  
```rails g model Message content:string```  

#####「メッセージのテーブル設計」 
#####・マイグレーションファイル  
Railsではマイグレーションファイルという、テーブル管理ファイルによってデータベースのテーブルを管理・実行する 
主に、テーブル作成・削除、カラム追加・削除に関することがファイル内に記述される 

Railsでテーブルの設定を変更したいときは、マイグレーションファイル作成、Railsからマイグレーション実行をする 

```def change ... end```内にテーブル操作が書かれる  

#####・マイグレーションの実行  
マイグレーションファイルの作成だけでは実行されない 
```rails db:migrate```で実行 
※マイグレーション実行後は、このファイルを修正してもテーブルの変更はできない！ 
変更を加える時は、```rails g migration```によって追加のマイグレーションファイルをさらに生成する 

#####・マイグレーションの確認  
```
MySQL ログイン
$ sudo mysql -u root

データベースの選択
mysql> use message-board_development

テーブル一覧確認（messagesテーブルが作成されているか確認）
mysql> show tables;

テーブル設計の確認
mysql> describe messages;
```  

#####・モデルクラスの作成  
マイグレーションでmodels/messages.rbも作成される 
ActiveRecord::Base → ApplicationRecord という順番でクラスを継承した Message モデルには、それらの機能が全て継承されており、Message クラスにコードが無くてもモデル操作できる

#####ActiveRecord：
Railsという大きく統合されたライブラリ群の中の1つで、Model全般の操作を担うライブラリ 
Railsアプリとのデータベース連携を担う 
ActiveRecordを介する事で、ほとんどSQLを書かず、Rubyの文法でレコードを操作可能になる  

#####・Modelで使用するCRUDメソッド一覧  
all：全レコード取得 
new：新規レコードの為のモデルインスタンス作成 
find：idを指定して検索 
find_by：id以外でも指定して検索 
where：検索条件を文字列、配列、ハッシュのいずれかの方法で与えられる 
first：最初のレコードを1件だけ取得する 
save：レコードの作成 
update,save：データの更新 
destroy：データ削除 

モデル名::メソッド名やモデルのインスタンス.メソッド名で使用 

これらのRubyメソッドを使うことで、Rubyコードが自動的にSQLに変換されて実行される 
ORM(Object-relational mapping)：オブジェクト指向プログラミング言語コードでデータベースを操作できる技法 

#####・Rails consoleでモデルのCRUD操作
rails console前に必ずSQLサーバ起動確認```sudo service mysql status```  

・モデルの一覧を確認 
Messages.all  

・モデルのインスタンスを作成 
message = Message.new(content: "test")  

・モデルのインスタンスに値を代入 
message.content = "hello"  

・モデルのインスタンスをデータベースへ保存(Create)　
message.save  

#####・モデルのインスタンスの取得(Read)  
all：複数のレコードを取得するメソッドで、レコード全体を取得 
First：最初のレコードのインスタンスを返す 
find：idから単一のレコードを検索し返す 
find_by：id以外でも単純な条件で検索可能 
Where：条件指定してレコードを検索 

・レコードの更新(Update) 
findなどでレコードを取得、インスタンスに対して値を再代入、Saveするのが通常  

・レコードの削除(Delete) 
destroy：削除  

#####・RouterとControllerとViewの開発概要  
1. Router：URLのルーティングを一元管理している 
config/routes.rbの1ファイルで、ルーティングを全て把握できる  

2. Controller：ユーザから送信されてきたHTTPリクエストの処理を担当 
HTTPリクエストはRouterからControllerの1つのメソッドに割り当てられる 
Routerと対応するControllerのメソッドをアクションと呼ぶ  

3. View：最終的にHTTPレスポンスとして返すWebページ  

#####・RouterとControllerとViewの開発の流れ  
どんな機能を作るか考え、実装すべき機能が挙がると、
1.ルーティングを決める 
2.Controllerのメソッドを決める 
3.Viewの名前を決める 


#####「Router」  
・リソースに対するCRUDの為の4つのルーティング  

```rails
Rails.application.routes.draw do
  get 'messages/:id', to: 'messages#show'
  post 'messages', to: 'messages#create'
  put 'messages/:id', to: 'messages#update'
  delete 'messages/:id', to: 'messages#destroy'
end
```  

:idはURLにidを入れる
POSTの場合だけidは不要。新規作成のためidはまだ用意されていない  

・CRUDのための残り3つの補助ページ  
詳細ページ(show)にアクセスするには、一覧ページ(index)が必要 
```get 'messages/:id', to: 'messages#show'``` 
この詳細ページにアクセスするには、まずリソースの一覧ページがないと、個々のリソースページである詳細ページへ辿り着けない 
👉一覧ページ(index)を付け加える 
```get 'messages', to: 'messages#index'```  

・保存アクション(create)にデータを送るには、新規作成用フォームページ(new)が必要 
```get 'messages/new', to: 'messages#new'```  

・更新アクション(update)にデータを送るには、更新用のフォームページ(edit)が必要 
```get 'messages/:id/edit', to: 'messages#edit'```  

・削除アクション(delete)はボタンがあればOK 
どこかのページに削除ボタンが設置してあれば、削除ページは不要  


#####・7つの基本ルーティングの省略形  

```rails
Rails.application.routes.draw do
  # CRUD
  get 'messages/:id', to: 'messages#show'
  post 'messages', to: 'messages#create'
  put 'messages/:id', to: 'messages#update'
  delete 'messages/:id', to: 'messages#destroy'

  # index: show の補助ページ
  get 'messages', to: 'messages#index'

  # new: 新規作成用のフォームページ
  get 'messages/new', to: 'messages#new'

  # edit: 更新用のフォームページ
  get 'messages/:id/edit', to: 'messages#edit'
end
```  
👇  

```rails
Rails.application.routes.draw do
  resources :messages
end
```  
これでさっきのルーティングと全く同じ意味になる。 
indexやshowという命名をした理由は、resourcesで生成されるルーティングがそのように決まっているから  

・Routerの完成 
トップページにアクセスした時のルーティングをMessagesControllerのindexアクションに設定する。 
indexアクションは、トップページ「/」と「/messages」にアクセスした両方で同じルーティングが設定されたことになる 

```rails
Rails.application.routes.draw do
  root to: 'messages#index'

  resources :messages
end
```  

・Routerの確認 
routes.rbで少し込み入ったコーティングをした場合、```rails routes```で現状のルーティングを確認できる 

resources :messagesで生成される7つのルーティングを『RESETfulなルーティングと呼ぶ』  
RESETfulと言われたら、7つの基本アクション(index,show,new,create,edit,update,destroy)を思い出すように  

#####●ControllerとView  
・Controllerの作成 
```rails g controller コントローラ名(モデルの複数形)``` 
Modelと同様に、継承によってControllerとしての基本機能が提供される  

・ControllerをRESETfulなルーティングに対応させる 
routes.rbで設定したルーティングに対応したアクションをmessages_controller.rbに追加  
Controller内のルーティングと同じ名前のメソッド名として定義 

・7つのアクションに対応したViewファイルの作成 
GETメソッドで指定されたルーティングのみ 
GET以外はリソースの具体的な操作の為 

index.html.erb のように .erb だけでなく、.html と入れているのは、 ERB ファイルが HTML ファイルへと変換されることを Rails に対して明示するため 

・共通部の書き出し 
HTMLを書く時に必ず共通部分が出てくる 
<!doctype html>やhead内要素 
application.html.erbにまとめる！ 
body要素内の<%= yield %>に埋め込まれるページが切り替わる 
👉<%= yield %>にindex.html.erbなどの内容が代入される 

・7つのルーティングに対するレスポンスの実装 
controllerのindexアクション：Messageモデルのレコード一覧表示 

```
def index
    @messages = Message.all
end
```

viewの作成ほか・・・ 

link_to 表示文字列, リンク先 
<%= link_to message.id,message %> 
message のように Model のインスタンス（レコード）を渡すと、自動的にそのインスタンスの show アクションへとリンクされる  
省略せずに書くと message_path(message) となり、リンク先 にこう記述しても正常に動作する 
message も message_path(message) もどちらも最終的に /messages/1 など/messages/:id の形の URL を生成しているだけ 

【リンク生成の為のメソッド】 
Prefixとしてmessages,new_messagesなどが載せられている 
ルーティングを設定すると、自動的にリンク生成の為のメソッドも定義される 

（Prefix + _path）となる  
:idが必要なものはインスタンスを特定する必要があるので、引数として@messageなどのインスタンスが必要 
<%= link_to message.id, message_path(@message) %>  

#####・View  
ERBにフォームを設置 
form_withを使用して生成 

```
<h1>メッセージ新規作成ページ</h1>

<%= form_with(model: @message, local: true) do |f| %>
  <%= f.label :content, 'メッセージ' %>
  <%= f.text_field :content %>

  <%= f.submit '投稿' %>
<% end %>

<%= link_to '一覧に戻る', messages_path %>
```  
form_withでフォーム開始、endで終了 
form_with(model: @message)のように、Controllerのnewアクションで用意した@messageを使用してフォームを作成する事を明示する 

(local: true)抜かすと画面推移しない通信が発生 
今回はサイト内での遷移であること、ならびに画面遷移を伴う同期通信のみで構わないため local: true をつける 

f.label,f.text_fieldでカラムを指定し、@messageの中のどのカラムに対する入力欄なのかを明示 
f.submitで送信ボタンを生成 

<%= link_to '新規メッセージの投稿', new_message_path %> を追加して、 index から new へのリンクを作成 

```
  def create
    @message = Message.new(message_params)

    if @message.save
      flash[:success] = 'Message が正常に投稿されました'
      redirect_to @message
    else
      flash.now[:danger] = 'Message が投稿されませんでした'
      render :new
    end
  end

  def edit
  end

  def update
  end

  def destroy
  end

  private

  # Strong Parameter
  def message_params
    params.require(:message).permit(:content)
  end
```  
コード下部 privateは、それ以降に定義されたメソッドがアクションではなく、クラス内でのみ使用する事を明示 
def message_params　が【Strong Parameter】 
ちゃんと必要なパラメータを把握し、送信されてきたデータを精査（フィルタリング）しようということ 
今回は:content以外のデータはフィルタにかけて捨てるようにしている 

params.require(:message)でMessageモデルのフォームから得られるデータに関するものと明示し、.permit(:content)で必要なカラムだけを選択している 

コード上部 @message = Message.new(message_params)で、Messageインスタンス生成時に、Strong parameterが使用されている

・redirect_toとrenderの違い
redirect_to：強制的に移動させる。createアクション実行→showアクション実行後、show.html.erbが呼ばれる 
render：単にmessage/new.html.erbを表示するだけ（アクションは実行しない） 

・flashとflash.nowの違い 
redirect_toの前ならflash。HTTPリクエストを発生させるため、flash.nowだと内容を保存できず消えてしまう。 
renderの前ならflash.now。HTTPリクエストを発生させないため消えない。 


・flash表示ファイルの作成 

```erb:view/layouts/_flash_messages.html.erb
<% flash.each do |message_type, message| %>
  <div><%= message %></div>
<% end %>
```  
全てのviewで表示させたいため、application.html.erbでrenderする 
(renderと書かれた場所に_flash_messages.html.erb の記述内容が埋め込まれる) 

名前が＿から始まるファイルを作成し、Viewの一部を抜き出して記述したものを『パーシャル』と呼ぶ 

flashに代入されたメッセージを1つ1つ取り出し、全表示する。flashはハッシュである為、|key, value|のペアで取り出される。 
今回は|message_type, message|という変数名を用いている。  

```erb:views/layouts/application.html.erb 
<body>
    <div class="container">
      <%= render 'layouts/flash_messages' %>

      <%= yield %>
    </div>
</body>
```  
●messages#edit 
既存のメッセージレコードを編集するので、idでメッセージレコードを検索する。(params[:id]) 

 
destroyアクションのredirect_to messages_url 
今までprefix_pathだったが、リダイレクトの場合だけは上記で↑ 
messages_urlはPrefixがmessagesのためindexへリダイレクトされる 
リダイレクトの時だけ_urlを使用する 

```erb:show.html.erb  
<%= link_to 'このメッセージを削除する', @message, method: :delete, data: { confirm: '本当に削除してよろしいですか？' } %>
```  
method: :delete　＝DELETEメソッドを送信するのを明示 
data: { confirm: ... }　＝javaScript  


【bootstrapの適用】 
・link_toにクラスを指定する 
class "..." 

・bootstrapのページネーション 
コマンドでBootstrap用のkaminariのviewを生成 
```rails g kaminari:views bootstrap4```  
ページネーションに手を加える場合、viws/kaminariフォルダ内のファイルを編集 

・show
showのp要素をテーブルに 

```
<table>
  <thead>  table header
    <tr>   table rou(行)
      <td>  table data
...
```  

一覧に戻るリンクを削除。navbarがあるため、タイトルクリックでindexに戻る為 

・new 
フォームにform-groupとform-controlのclass属性を付与 

グリッドシステム 
横長のテキスト入力欄をグリッドシステムで横半分に 
フォームをrowとcol-6のdiv要素で囲む→6/12で半分 

new.html一覧に戻るリンク削除 

・edit 
フォームはパーシャルで更新済 
一覧に戻るリンク削除 

####まとめ 
ModelはControllerによって必要なものが取得され、Viewに流れて表示される。ユーザのHTTPリクエストをRouterが解析し、リクエストに沿って適切なModelをユーザに表示するのがWebアプリケーション。 
Modelはデータベースが無いと保存できない。データベースが用意される事で、Modelのインスタンスをレコードとして保存できるようになる。 

### 【エラーのデバック】 
エラー画面には以下の情報が含まれる 
エラータイトル：例外クラスの名前と、発生場所 
エラーが起きたファイル名：ファイルのパスと行番号 
エラー詳細：エラーメッセージ 
エラーが起きた行：強調表示される 
トレース状況：エラーが起きたメソッドの呼び出し状況を出力 
リクエスト情報：Railsサーバに送信したリクエスト 
パラメータ：必要なパラメータが含まれているか、間違っていないか 
レスポンス情報：Railsサーバーがどんなレスポンスを返したか 

・エラーの修正手順 
1.エラーの概要を理解する 
タイトルと詳細を読んで概要を理解する
よくあるエラー👇 
「NoMethodError in ...」 
undefined method (a)for(b) 
bにaというメソッドが存在しない。定義されていない、ｂに正しいデータが入っていない場合が多い 
bがnil:NilClassの場合、エラーが起きた場所より前で正しくデータをセットできているか確認 

「ActiveRecord::RecordNotFound in ...」 
Coludn't find a with b 
bの条件を満たすaが見つからない。ユーザＩＤを指定したけど、DB上にデータが存在しないなど。 

「routingError」 
No route matches [a] "b" 
bのURLへのaメソッドのリクエストを受け取ったが、対応するルーティング設定がない 
rails routesコマンドの結果や、routes.rbの内容を確認する 

「Missing Template」 
コントローラのアクションに対応するビューが存在しない 
erbファイルが存在し、ファイルパスが正しい事を確認 

「Syntax Error」 
プログラムの文法エラー 

・エラー発生部分を特定する 

・ソースコードを修正する 

### Railsのデバックツール  
pry-byebug Gemfileの追加 
```gem 'pry-byebug', group: :deveropment``` 
```bundle install``` 
使い方 
ソースコードの途中で、Railsアプリケーションの実行を一時的に止められる。実行を止めたい部分に「binding.pry」という１行を追加