Ruby on Rails 学習メモ.1

#Ruby on Rails学習メモ.1

以下、初学者の独学によるメモ（チラシの裏）。
Progateでの学習もある程度進んできたため、アウトプットをすることにした。
学習中のため、誤った解釈もあるかもしれない。

**Railsでのサイト作成は、
view(ブラウザ)→
ルーティング（アクションを呼び出す）→
コントローラー（URLに対応したHTMLを送信）
（→view…）
の編集の連続である。**

これらの基本をよく理解すること。
**また、何をやろうとしているのか（目的を）理解しないまま、コードを覚える事を深追いしながら無理矢理学習を進めようとすると単なる写経になってしまうので、まず目的を理解する事に努めること（理解できない場合は何度も読む)。**（先の学習内容まで進めた後に復習すると理解できる場合もあるので、まず進める方が良い場合もある。）

#1.新規投稿

**HTML(view)の設定**

```
<div class="form-body">
  <%= form_tag("/posts/create") do %>
  <textarea name="content"></textarea>
  <input type="submit" value="投稿">
  <% end %>
</div>
```
`<%= form_tag("/posts/create") do %>`のform_tagが設定されていることにより、
`<input type="submit" value="投稿">`、つまり投稿ボタンを押すと、submitされたデータを「/posts/create」のURLに送信するようになっている。
`<textarea name="content"></textarea>`
入力したデータにname属性を指定している。
これにより、コントローラーのアクション内(変数：params)で入力したデータを受け取れるようになる。

**ルーティングの設定**

```
get "posts/create" => "posts#create"
```

URL「/posts/create」から情報を受け取り、
「posts」コントローラーの「create」アクションを呼び出す。

**コントローラーの設定**

```
def create
    @post = Post.new(content: params[:content])
    @post.save
    redirect_to("/posts/index")
end
```
ルーティングからcreateアクションが呼び出され、以下の処理が実行される。
`@post = Post.new(content: params[:content])
 @post.save`
変数@postに投稿された内容を代入(受け取るために変数paramsを使用している)し、データベースに保存する。
`redirect_to("/posts/index")`
URL:/posts/indexに転送（ページ移行）する。

※`@user = User.new(name: params[:name], email: params[:email])`のように、配列として複数のデータを受け取ることもできる。

#2.投稿詳細画面の表示

**HTML**

```
<% @posts.each do |post| %>
      <div class="posts-index-item">
        <div class="post-right">
          <%= link_to(post.name, "/posts/#{post.id}") %>
        </div>
      </div>
<% end %>
```

idを取得・送信するように設定。

**ルーティング**

```
get "posts/index" => "posts#index"
get "posts/:id" => "posts#show"
```

showアクションのルーティングはindexアクションより下に書かないといけないことに注意する。


**コントローラー**

```
def show
    @posts = Post.find_by(id: params[:id])
end
```




#3.投稿成功・失敗の表示

**クラス定義：投稿の制限(Validate:検証の意)**

```
class Post < ApplicationRecord
  validates :content, {presence: true}
  validates :content, {length: {maximum: 140}}
end
```

Validateはクラス内(models/post/rb)で定義するので、postクラス配下すべてで適用される。
上記は空の投稿と140字以上の投稿にfalseを返すようになっている。

**コントローラー(アクション)**

```
def create
    @post = Post.new(content: params[:content])
    if @post.save
    redirect_to("/posts/index")
    flash[:notice] = "投稿を作成しました"
    else
    render("posts/new")
end
```

if @post.saveがtrueの場合、「/posts/index」に飛び、
「flash[:notice] = "投稿を作成しました"」を実行する。

falseの場合、「posts/new」に飛ぶ

**HTML**
flashはいろいろな箇所で共通で使っていくのでapplication.html.erbに設定する。
投稿成功の場合：

```
 <% if flash[:notice] %>
  <div class="flash">
    <%= flash[:notice] %>
  </div>
 <% end %>
```

**投稿失敗の場合**
投稿失敗の場合posts/newに飛ぶので、application.htmlではなくnew.htmlを編集する。（2行目から6行目までが該当。）

```
<%= form_tag("/posts/create") do %>
  <% @post.errors.full_messages.each do |message| %>
       <div class="form-error">
       <%= message %>
       </div>
       <% end %>
       <textarea name="content"><%= @post.content %></textarea>
       <input type="submit" value="投稿">
<% end %>
```
saveメソッドを呼び出した際にバリデーションに失敗すると、Railsでは自動的に@post.errors.full_messagesの中にエラーメッセージが配列で入るようになっている。
each文を用いることで、配列の中のメッセージ全てを表示させる。
また、`<textarea>`内に`<%= @post.content %>`を設定しているため、入力した（投稿に失敗した)文章が出力される。
**(newアクションではこの時点では変数postが定義されていないため、@post = Post.newを定義する必要があることに注意。←newアクション作成時に作っておく？)**

#4.投稿一覧ページの作成
**先にターミナルで「rails g model Post content:text」(Post:モデル名、content:カラム名、text:データ型)を実行し、モデルとマイグレーションファイル（データベース）を作成しておく。**

**コントローラー(アクション)**

```
def index
  @posts = Post.all
end
```

**HTML**

```
<% @posts.each do |post| %>
      <div class="posts-index-item">
          <%= post.content %>
      </div>
<% end %>
```

each文で変数@posts内の内容を繰り返し（全て）表示させる。


#5.投稿の編集
**HTML(edit.htmlを作成する必要あり)**

```
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">編集する</h1>
    <%= form_tag("/posts/#{@post.id}/update") do %>
    <div class="form">
      <div class="form-body">
        <% @post.errors.full_messages.each do |message| %>
            <div class="form-error">
              <%= message %>
            </div>
          <% end %>
        <textarea name="content"><%= @post.content %></textarea>
        <input type="submit" value="保存">
      </div>
    <% end %>
  </div>
</div>
```
4行目：idを取得し「/posts/id/update」のURLを送信する。
7行目：アクション(コントローラー)内のifでエラーが出てrenderメソッドを介してページに戻ってきた場合、エラーメッセージを表示させるようになっている。

**ルーティング**

```
post "posts/:id/update" => "posts#update"
```

**コントローラー（アクション）**

```
def update
    @post = Post.find_by(id: params[:id])
    @post.content = params[:content]
    if @post.save
      flash[:notice] = "投稿を編集しました"
      redirect_to("/posts/index")
    else
      render("posts/edit")
    end
end
```

`@post = Post.find_by(id: params[:id])`：送信されてきたURLとidを参照し、変数@postに代入する。
`@post.content = params[:content]`：送信されてきた内容をparamsで受け取り、変数@postのcontentに代入する。

`if @post.save`が成立したら「投稿を編集しました」のメッセージとともに、「/posts/index"」に戻る。
成立しない（エラーの）場合、renderで「posts/edit」に戻る。
renderで戻らないと「/posts/index」を介し投稿内容の変数が更新されてしまう（編集内容が最初の投稿に戻り、編集したかった投稿がフォームに維持されない。）

#6.投稿の削除

**HTML(show.html)**

```
 <div class="post-menus">
        <%= link_to("編集", "/posts/#{@post.id}/edit") %>
        <%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "post"}) %>
      </div>
```

（投稿詳細画面のshow.htmlで完結するので、新しくhtml(view)を追加する必要はない。）
link_toメソッドの第三引数に{method:"post"}を設定しないと、次のルーティングでpostではなくgetを探してしまい、エラーとなってしまう。

**ルーティング**

```
post "posts/:id/destroy" => "posts#destroy"
```



**コントローラー（アクション）**

```
def destroy
    @post = Post.find_by(id: params[:id])
    @post.destroy
    flash[:notice] = "投稿を削除しました"
    redirect_to("/posts/index")
end
```
編集の際と同様、HTMLより送信されたidを変数に代入し、destroyアクションで削除する。
**(実際のポートフォリオでは「削除しますか？」の質問を出せるようにする？)**