Ruby on RailsでSNSを作る

# はじめに
Ruby on Rails理解しなからSNSのWebアプリを作成していく備忘録です。
開発環境はMacbookで、Railsは既にインストールされた状態からはじめます。
※本記事は記述途中です

# Railsアプリの準備
まずはじめにRailsのワークディレクトリで、下のコマンドを打ち雛形の作成をすすめる。

1. railsプロジェクトの作成
2. 起動
3. トップページの作成

```sh
$ rails new test_app
$ rails server
$ rails generate controller home top
```

ブラウザからそれぞれアクセスしてみる。

localhost:3000
localhost:3000/home/top

`$ rails generate`によって、Webアプリに必要な以下のコンポーネントが作成される。
1. View
2. Controller
3. Routing

# View
Viewは文字通り見た目部分のHTMLファイル。
自動で作成されたビューは、`test_app/app/views/home/top.html.erb`にある。
(「erb」とは「Embedded Ruby（埋め込みRuby）」の略) *後述
この`top.html.erb`を適当なHTMLに書き換えるとブラウザから見るページも変更される。

```top.html.erb
<div class="main top-main">
  <div class="top-message">
    <h2>ほげほげ</h2>
    <p>ふがふが</p>
  </div>
</div>
```

# Controller

ブラウザでページが表示されるときは、このコントローラを経由してビューをブラウザに返す。
コントローラファイルの場所: `test_app/app/controllers/home_controller.rb`
**top**メソッドが定義されてあるが、コントローラ内のメソッドを**アクション**と呼び、ブラウザに返すビューを``views
`フォルダから探し出す役目を持つ。
アクションは、コントローラと同じ名前のビューフォルダから、アクションと同じ名前のHTMLファイルを探す。

```home_controller.rb
class HomeController < ApplicationController
  def top
  end
end
```

## 新しいコントローラの追加
新しいページ（例えば、トップページの他に投稿ページなど）を作るときは、新しいコントローラを追加する。
（homeコントローラでも作成可能だが、投稿ページは**home**という訳ではないので、投稿に関する事は投稿用のコントローラを作成する）

**"g"**は、**"generate"**の省略
投稿一覧ページを作るため、コントローラ名は**"posts"**、アクション名は一覧なので**"index"**とする。

```sh
$ rails g controller posts index
```

これによって、以下が追加される。
1. View (`app/views/posts/index.html.erb` + `app/assets/stylesheets/posts.scss`)
2. Controller (`app/controllers/posts_controller.rb`)
3. Routing (`app/config/router.rb` -> `get "posts/index" => "posts#index"`が追加される)

# Routing
ルーティングは、ブラウザとコントローラを繋ぐ役目を持つ。
ページが表示されるまでの一連の動きは、`ルーティング->コントローラ->ビュー`というふうになる。

ルーティングは、送信さえたURLに対してどのコントローラのどのアクションで処理するのかを決める対応表である。
ブラウザに入力されたURLは、ルーティングによって適切なコントローラ、アクションが呼び出される。

```
localhost:3000/home/app
Railsサーバー/コントローラ/アクション
```

ルーティングは`test_app/app/config/routers.rb`に作成される。

```routers.rb
Rails.application.routes.draw do
  get 'home/top'
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
```

ルーティングを変更したい場合、例えば`localhost:3000/home/top`から`localhost/top`に変えるときは以下のように変更。

```routers.rb
Rails.application.routes.draw do
  get 'top' => "home#top"
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
```

## URLの変更 
`localhost:3000`でアクセスすると、railsのHwlloWorld的なページが出力されるが、これを本来のトップページに変更したい場合

```routers.rb
Rails.application.routes.draw do
  get '/' => "home#top"
  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
```

# ページの追加

トップページ以外のなにかしらのページを追加したい場合など、以下のようにする。

- ビューを追加
適当に**about.html.erb**という名前でHTMLファイルを作成し、home配下に置く

`app/views/home/about.html.erb`

- コントローラファイルにアクション(about)を追加

```controllers/home_controller.rb
class HomeController < ApplicationController
  def top
  end

  def about
  end

end
```

- アクション(about)のルーティングを追加

```app/config/routers.rb

Rails.application.routes.draw do
  get "top" => "home#top"

  get "about" => "home#about"

end
```

新しいviewファイル`about.html.erb`をapp/home/配下に作成

```about.html.erb
<div class="about-main">
  <h2>ほげほげ</h2>
  <p>
    ほげほげサービスです。
    ふがふが。
  </p>
  <img class="about-img" src="/tweets.png">
</div>
```

これで`localhost:3000/about`にブラウザアクセスすると、Viewに追加したHTMLが表示される。


# レイアウト(CSS)
CSSファイルは`app/assets/stylesheets/home.scss`に生成されている。
`stylesheets`配下に書いたCSSは全てのビューに適用される。

例

```home.scss
/* reset ================================ */
* {
  box-sizing: border-box;
}

html {
  font: 100%/1.5 'Avenir Next', 'Hiragino Sans', sans-serif;
  line-height: 1.7;
  letter-spacing: 1px;
}

ul, li {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

a {
  text-decoration: none;
  color: #2d3133;
  font-size: 14px;
}

h1, h2, h3, h4, h5, h6, p {
  margin: 0;
}

input {
  background-color: transparent;
  outline-width: 0;
}

form input[type="submit"] {
  border: none;
  cursor: pointer;
}

/* 共通レイアウト ================================ */
body {
  color: #2d3133;
  background-color: #3ecdc6;
  margin: 0;
  min-height: 1vh;
}

.main {
  position: absolute;
  top: 64px;
  width: 100%;
  height: auto;
  min-height: 100%;
  background-color: #f5f8fa;
}

.container {
  max-width: 600px;
  margin: 60px auto;
  padding-left: 15px;
  padding-right: 15px;
  clear: both;
}

/* ヘッダー ================================ */
header {
  height: 64px;
  position: absolute;
  z-index: 1;
  width: 100%;
}

.header-logo {
  float: left;
  padding-left: 20px;
  color: white;
  font-size: 22px;
  line-height: 64px;
}

.header-logo a{
  color: white;
  font-size: 22px;
}

.header-menus {
  float: right;
  padding-right: 20px;
}

.header-menus li {
  float: left;
  line-height: 64px;
  font-size: 13px;
  color: white;
  padding-left: 15px;
}

.header-menus a {
  float: left;
  font-size: 13px;
  color: white;
}

.header-menus .fa {
  padding-right: 5px;
}

.header-menus input[type="submit"] {
  padding: 0 20px;
  float: left;
  line-height: 64px;
  color: white;
  margin: 0;
  font-size: 13px;
}

/* top ================================ */
.top-main {
  padding: 200px 0 100px;
  text-align: center;
  position: absolute;
  top: 0;
  width: 100%;
  height: auto;
  min-height: 100%;
  color: white;
  background-color: #3ecdc6;
  background-repeat: no-repeat;
  background-position: center 50%;
  background-size: cover;
  background-image: url("/top.jpg");
}

.top-message {
  position: relative;
}

.top-main h2 {
  font-size: 70px;
  font-weight: 500;
  line-height: 1.3;
  -webkit-font-smoothing: antialiased;
  margin-bottom: 20px;
}

.top-main p {
  font-size: 24px;
}

/* about ================================ */
.about-main {
  padding: 180px 8% 0;
  color: white;
}

.about-main h2 {
  font-size: 64px;
  font-weight: 500;
  line-height: 1.4;
}

.about-main p {
  font-weight: 200;
  font-size: 20px;
}

.about-img {
  width: 84%;
}

/* フォーム================================ */
.form {
  max-width: 600px;
  margin: 0 auto;
  background-color: white;
  box-shadow: 0 2px 6px #c1ced7;
}

.form-heading {
  font-weight: 300;
  margin: 60px 0 20px;
  font-size: 48px;
  color: #bcc8d4;
}

.form-body {
  padding: 30px;
}

.form-error {
  color: #ff4d75;
}

.form input {
  width: 100%;
  border: 1px solid #d8dadf;
  padding: 10px;
  color: #57575f;
  font-size: 16px;
  letter-spacing: 2px;
  border-radius: 2px;
}

.form textarea {
  width: 100%;
  min-height: 110px;
  font-size: 16px;
  letter-spacing: 2px;
}

.form input[type="submit"] {
  background-color: #3ecdc6;
  color: white;
  cursor: pointer;
  font-weight: 300;
  width: 120px;
  border-radius: 2px;
  margin-top: 8px;
  margin-bottom: 0;
  float: right;
}

.form-body:after {
  content: '';
  display: table;
  clear: both;
}

/* フラッシュ ================================ */
.flash {
  padding: 10px 0;
  color: white;
  background: rgb(251, 170, 88);
  text-align: center;
  position: absolute;
  top: 64px;
  z-index: 10;
  width: 100%;
  border-radius: 0 0 2px 2px;
  font-size: 14px;
}
```

# 画像の表示
画像ファイルは、`test_app/public`に配置することでフルパスを指定しなくても、画像ファイルのファイル名のみで参照可能。

```example.html
<img src="/画像名" >
```

```example.css
background-image: url("/画像名");
```

# リンク

HTMLの中でトップページや他ページへのリンクをつけたい場合。
Railsの中であればルーティングのURL部分と同じような指定で書ける。

```example.html
<a href="/">Test App</a>
<a href="/about">Test App</a>
```

# erbファイルについて

「erb」とは「Embedded Ruby（埋め込みRuby）」の略

## 変数の定義

`<% %>`で囲むことでhtml.erbファイル内でRubyのコーディングと同じように変数も定義可能。

```example.html.erb
<% post1 = "テストです" %>
```
変数を表示するには`<%= %>`とする。

```example.html.erb
<%= post1 %>
# 「テストです」を表示
```

## 配列

``` example.html.erb
<%
  posts = [
    "テストです",      <---コンマを忘れない
    "テスト2です"
  ]
%>
```

## each文

普通に書く(こんな感じ)
`<% posts.each do |post| %>`
`<% end %>`も忘れず。

```example.html
<div class="main posts-index">
  <div class="container">

    <% posts.each do |post| %>

      <div class="posts-index-item">

        <%= post %>

      </div>

    <% end %>
```

## コントローラのアクションでの変数定義
一般的には変数はViewファイルではなくアクションで定義する。
変数名の前に***"@"***をつける事でここで定義した変数が、Viewファイルでも使用できる。
Viewファイルから呼び出すときも***"@"***をつける（`<%= @post %>`）

``` example.rb
def index
  @posts = [
    "テストです",
    "テスト2です"
  ]
end
```

# Rails Console

ターミナルからインタラクティブにRubyコードを実行可能。
終了は`quit`

```sh
$ rails console
$ quit
```

# データベース

## テーブル作成の準備 - マイグレーションファイルの作成
**マイグレーションファイル**とは、データベースへの変更を指示するファイルである。
"posts"テーブルを作成する想定で、以下のコマンド。

```sh
$ rails g model Post content:text
```

**Post** = モデル名 ※後述（postsテーブルを作成する場合、単数形にする）
**content** = カラム名（データベースのテーブルで言う縦の列 -> ID、名前などの部分）
**text** = データ型（textを指定した場合は長いテキストを取り扱う宣言）

マイグレーションファイルの場所は以下
`test-app/db/migrate/<日付>_create_posts.rb`

## テーブル作成

作成したマイグレーションファイルを使って、データベースに変更を加える。
テーブルの作成は以下。

```sh
$ rails db:migrate
```

テーブルが作成できたら、マイグレーションファイル作成時に指定した**"content"**カラム以外にも、以下のカラムが自動生成されている。
- **ID**
1から順に割り当てられ重複する事はない
- **created_at**
データが作成された日時
- **updated_at**
データが更新された日時

## 注意
マイグレーションファイルを作成後、データベースに変更を加えずにどこかのページにアクセスすると**マイグレーションエラー**でエラーページが表示される。
その為、マイグレーションファイルを作成したら必ず`$ rails db:migrate`を実行する事。

## テーブル操作

作成したテーブルは、**モデル**という特殊なクラスを使用して操作する。
`$ rails g model ...`のコマンドで既にpostsテーブルを操作する為のPostモデルが生成されている。
場所は`app/models/post.rb/`

以下のように、**ApplicationRecord**というクラスを継承したクラスが**モデル**と呼ばれる。

```post.rb
class Post < ApplicationRecord
end
```

## テーブルにデータを保存する

### newメソッド
Postモデル(Postテーブル)からPostインスタンスを作成する。
インスタンスの作成時には**newメソッド**を使用する。

Contentカラムが"Hello World"であるPostインスタンスを作成し、post変数に代入

```rb
$ rails console
> post = Post.new(content: "Hello World")
```


### saveメソッド
作成したPostインスタンスをpostsテーブルに保存するために**saveメソッド**を使用する。
saveメソッドはPostモデルがApplicationRecordを継承しているから使用可能

```rb
$ rails console
> post.save
```

## テーブルからデータを取り出す
### テーブルにある最初のデータを取り出す

**Post.first**のようにする事でpostsテーブルにある最初のデータを取得できる

```example.rb
$ rails console
> post = Post.first
```
### contentカラムにの値を取り出す

**post.content**とすることでpost.firstで取得したデータから内容を取得できる

```example.rb
$ rails console
> post = Post.first
> post.content
```

```sh
$ rails console
Loading development environment (Rails 5.0.3)
[1] pry(main)> post = Post.first
  Post Load (0.1ms)  SELECT  "posts".* FROM "posts" ORDER BY "posts"."id" ASC LIMIT ?  [["LIMIT", 1]]
=> #<Post:0x000055cb4cf67c60
 id: 1,
 content: "Hello World",
 created_at: Sun, 19 Apr 2020 00:00:36 JST +09:00,
 updated_at: Sun, 19 Apr 2020 00:00:36 JST +09:00>
[2] pry(main)> post.content
=> "Hello World"
[3] pry(main)>
```

### 全てのデータを取得

テーブルに保存されている全てのデータを取得するには**Post.all**を使用する
全てのデータは配列で取得される
配列の中から一つの値を取り出す場合は**Post.all[0]**のようにインデックス番号で要素を取得する

```example.rb
$ rails console
> posts = Post.all
> Post.all[0]
```

### 配列のデータからコンテンツを取得
**Post.all[0].content**のようにする事でcontentカラムの値を取得できる

```sh
$ rails console
Loading development environment (Rails 5.0.3)
[1] pry(main)> posts = Post.all
  Post Load (1.0ms)  SELECT "posts".* FROM "posts"
=> [#<Post:0x000055ecac398390
  id: 1,
  content: "Hello",
  created_at: Sun, 19 Apr 2020 00:00:36 JST +09:00,
  updated_at: Sun, 19 Apr 2020 00:00:36 JST +09:00>,
 #<Post:0x000055ecac3900a0
  id: 2,
  content: "World",
  created_at: Sun, 19 Apr 2020 00:00:59 JST +09:00,
  updated_at: Sun, 19 Apr 2020 00:00:59 JST +09:00>]
[2] pry(main)> posts[0]
=> #<Post:0x000055ecac398390
 id: 1,
 content: "Hello",
 created_at: Sun, 19 Apr 2020 00:00:36 JST +09:00,
 updated_at: Sun, 19 Apr 2020 00:00:36 JST +09:00>
[3] pry(main)> posts[1]
=> #<Post:0x000055ecac3900a0
 id: 2,
 content: "World",
 created_at: Sun, 19 Apr 2020 00:00:59 JST +09:00,
 updated_at: Sun, 19 Apr 2020 00:00:59 JST +09:00>
[4] pry(main)> posts[0].content
=> "Hello"
[5] pry(main)> posts[1].content
=> "World"
[6] pry(main)>
```

## データをビューに表示
postsコントローラのindexアクション内の@postsに、Post.allで取得したデータを代入する
コード内に表示するテキストを埋め込んでいる場合

```posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = [
      "Hello",
      "World"
    ]
  end
end
```

@postsに代入されている配列を**Post.all**に置き換える

```posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end
```

ビューでは@postsに代入されている配列データをeach文で1つずつ変数postに代入し、**post.content**を用いて繰り返し表示させる
app/views/配下のhtml.erbファイルでは以下のように記述する

```html
<header>
  <div class="header-logo">
    <a href="/">タイトル</a>
  </div>
  <ul class="header-menus">
    <li>
      <a href="/about">ほげほげ</a>
    </li>
  </ul>
</header>

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

## 共通レイアウト

### 共有のレイアウトをまとめる

Railsでは、「views/layouts/application.html.erb」に共通のHTMLを書いておくことができます。
初期状態でも、<head>タグや<body>タグなどの共通部分はこちらに書かれています。今回は、以下の図のように、ここにヘッダーを追加し、どのページでも共通のヘッダーが表示されるようにしましょう。

以下のようなヘッダーが記述されたhtmlファイルが複数ある場合は、「views/layouts/application.html.erb」に記述する。

```html
<header>
  <div class="header-logo">
    <a href="/">タイトル</a>
  </div>
  <ul class="header-menus">
    <li>
      <a href="/about">ほげほげ</a>
    </li>
  </ul>
</header>
```

トップページなど、各ページのhtmlファイルでは以下のようにヘッダーを記述していなくてもヘッダー部分が表示されるようになる

```html
<div class="main top-main">
  <div class="top-message">
    <h2>ほげほげ</h2>
    <p>ふがふが</p>
  </div>
</div>

```
「views/layouts/application.html.erb」の全体は以下のように記述

```html
<!DOCTYPE html>
<html>
  <head>
    <title>タイトル</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<!--- Rails6.0以降は"javascript_pack_tag"、5.0以前は"javascript_include_tag"--->
  </head>

  <body>
    <header>
      <div class="header-logo">
        <a href="/">ほげほげ</a>
      </div>
      <ul class="header-menus">
        <li><a href="/about">ふがふが</a></li>
      </ul>
    </header>
    <!-- 各ビューファイルは以下のyieldに代入され、application.html.erbの一部となる -->
    <%= yield %>
  </body>
</html>
```

### レイアウトの仕組み

「views/layouts/application.html.erb」には<%= yield %>というコードがあり、top.html.erbなどの各ビューファイルは、この<%= yield %>の部分に代入され、application.html.erbの一部としてブラウザに表示される。
この仕組みによって、ヘッダーなどの共通のレイアウトを1つにまとめることができる


## link_toメソッド

Rails ではlink_toというメソッドを使うと<a>タグを作成することができるぞ。 link_to メソッドは Ruby のコードなので、「<%=%>」で囲むことに注意するのじゃ。

右の図のように、第一引数に表示する文字を、第二引数に URLを書くことでリンクが作成されるぞ。

```application.html.erb
<%= link_to("About", "/about") %>
            第一引数   第二引数
```
↓以下のaタグに変換される

```application.html
<a href="/about">About</a>
```
例えば、以下のようにaタグを付与しているhtml.erbファイルがあった場合

```html
<body>
    <header>
      <div class="header-logo">
        <a href="/">トップページ</a>
      </div>
```

以下の様に置き換えができる

```html
<body>
    <header>
      <div class="header-logo">
        <%= link_to("トップページ", "/")%>
      </div>
```

複数ページのリンクを指定する例

```html
      <ul class="header-menus">
        <li>
          <%= link_to("サービス説明", "/about")%>
        </li>
        <li>
          <%= link_to("投稿一覧", "/posts/index")%>
        </li>
      </ul>
    </header>
```


## find_byメソッド
**find_by**メソッドは、データベースから特定のIDなど条件に合致したデータを取得する
取得するデータの条件は以下の様に指定する

**モデル名.find_by(カラム名: 値)**

idが3のデータを取得し、post変数に代入してcontent、created_at、idのそれぞれの絡むの値を取得する例

```sh
$ rails console
Loading development environment (Rails 5.0.3)
[1] pry(main)> post = Post.find_by(id:3)
  Post Load (0.1ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
=> #<Post:0x000055ae42ffa8c0
 id: 3,
 content: "ほげほげ",
 created_at: Mon, 20 Apr 2020 18:20:09 JST +09:00,
 updated_at: Mon, 20 Apr 2020 18:20:09 JST +09:00>
[2] pry(main)> post.content
=> "ふがふが"
[3] pry(main)> post.created_at
=> Mon, 20 Apr 2020 18:20:09 JST +09:00
[4] pry(main)> post.id
=> 3
[5] pry(main)>
```

## データベースからデータを表示するページを作成

### ルーティング/アクション/ビューの設定

データのID毎にページを分ける設計で
詳細ページにはshowアクションを作成する
URLが**/post/1**や**/post/2**のとき、showアクションに行くようにする。
通常以下のようにルーティングを作成する

```routers.rb
get "post/1" => "posts#show"
get "post/2" => "posts#show"
get "post/2" => "posts#show"
```

上の場合だと投稿データの数だけ書かなければいけなくなるので、次の例ではURL部分「:」を使用し、「**posts/:id**」と指定する事で、「/posts/1」や「/posts/2」でもshowアクションにいくようにできる。
**routers.rb**に「posts/:id」と書くと、以下「/posts/〇〇」のような全てのURLが該当する。

`localhost:3000/posts/1`
`localhost:3000/posts/2`


```routers.rb
get "posts:/id" => "posts#show"
```
この様にルーティングが設定され、showアクションが定義されていれば、以下の様にコントローラファイルに飛ぶ

```posts_controller.rb
def show
end
```

### 注意

「**posts/:id**」というルーティングは「posts/index」より下に書かなければならない。
ルーティングは合致するURLを上から順に探すため、「posts/index」より上に書くと、`localhost:3000/posts/index`というURLは「posts/:id」というルーティングに合致してしまう。

正しい例

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

間違っている例

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

ここまでで、ルーティングの設定(`config/routes.rb`)、アクション(`app/controllers/posts_controller.rb`)の設定ができている為、最後に表示するページのビューを作成して新しいページの完成となる。

`app/views/posts/show.html.erb`として新しいページビューを作成

```show.html.erb
<div class="main posts-show">
  <div class="container">
    <div class="posts-show-item">
      <p>投稿詳細画面です</p>
    </div>
  </div>
</div>
```
新しくルーティング設定したURL(`localhost:3000/posts/1`etc)にブラウザからアクセスすると、showアクションに行くようになり、上記のページビューが表示される。


### URLからIDを取得

上の状態ではURLにどのIDを指定しても同じビューが表示される為、ID毎にページを振り分ける設定が必要になる。

コントローラのアクション内ではルーティングで設定したURLの「**:id**」の値を取得する事ができる。
その値は**params**という変数にハッシュとして入っている。
**params[;id]**とする事で、その値を取得することができる。

`localhost:3000/posts/1`でアクセスした場合、以下の**params**変数には**[id:1]**というハッシュが代入されている

```posts_controller.rb
def show
  @id = params[:id]
end
```

ビューで以下の様にした場合は、**@id**から「1」が表示される

```posts/show.html.erb
<%= @id %>
```
例

```posts/show.html.erb
<div class="main posts-show">
  <div class="container">
    <div class="posts-show-item">
      <p>
        <%= "idが「#{@id}」の投稿詳細画面です" %>
      </p>
    </div>
  </div>
</div>
```

### データベースの内容を表示

showアクションで変数@postを定義し、idカラムの値が**params[:id]**と等しいデータをデータベースから取得して代入する。
@postを`show.html.erb`で表示することで、書くURLに対応したデータが表示されるようにする。

以下例では、idカラムがparams[:id]であるデータをfind_byメソッドにより取得し、show.html.erbにより表示している。

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

```posts/show.html.erb
<div class="posts-show-item>
  <%= @post.content %>
  <div class="post-time">
    <%= @post.created_at %>
  </div>
</div>
```

### データベース参照ページへのリンクを作成

例として投稿一覧ページに、各投稿の詳細ページへのリンクを作成する。
各投稿の内容の部分をクリックすると詳細ページに移動できるように、`link_to(post.content, "/posts/#{post.id}")`とする。
**#{post.id}**部分は変数展開を用いてデータのidを指定している。

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


### ユーザーによる投稿ページを作成する

### 新規投稿ページの準備

新規投稿ページでは`localhost:3000/posts/new`というURLでアクセスできるようにする。
そのためにルーティング/アクション/ビューを追加する。
アクションは"new"アクションとする。

`ルーティング`

```routes.rb
get "posts/index" => "posts#index"
#追加
get "posts/new" => "posts#new"
#post:/idよりも上に書くことに注意
get "posts/:id" => "posts#show"
```

`アクション`

```posts_controller.rb
class PostsController < ApplicationController
 def new
  end
end
```

`ビュー`

```posts/new.html.erb
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">投稿する</h1>
  </div>
</div>
```
ここまでで`localhost:3000/posts/new`にアクセス可能となる。

投稿一覧ページに新規投稿ページのリンクを追加する

```app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>TweetApp</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <header>
      <div class="header-logo">
        <%= link_to("タイトル", "/") %>
      </div>
      <ul class="header-menus">
        <li>
          <%= link_to("ほげほげ", "/about") %>
        </li>
        <li>
          <%= link_to("投稿一覧", "/posts/index") %>
        </li>
        <li>
          <%= link_to("新規投稿", "/posts/new") %>
        </li>
      </ul>
    </header>
    <%= yield %>
  </body>
</html>
```

### 入力フォームを作成

HTMLやCSSを使用し、<textarea>タグや<input>タグを用いることで入力フォームを作成することができる。
送信ボタンにはtype="submit"と、value="投稿"を指定する。

```posts/new.html.erb
# 入力フォーム
<textarea></textarea>

# 投稿ボタン
<input type="submit" value="投稿"
```
例

```new.html.erb
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">投稿する</h1>
    <div class="form">
      <div class="form-body">
        <textarea></textarea>
        <input type="submit" value="投稿">
      </div>
    </div>
  </div>
</div>
```

### 投稿を保存する流れ

フォームの投稿ボタンを押すと、Rails側に投稿データが送信される。
例ではcreateアクションを用意して、受け取った投稿データをデータベースに保存するようにする。
createアクションのURLは「/posts/create」とする。

### createアクションのルーティング

フォームの値を受け取る場合は「get」ではなく「**post**」とする。（この「post」はPostモデルの「Post」とは関係ない。）
通常は「get」、フォームの値を受け取るときは「post」というように覚える。

```routes.rb
get "posts/new" => "posts#new"
post "posts/create" => "posts#create"
```

### フォームの送信先を指定

**form_tag**メソッドを用いると、フォームに入力されたデータを送信することができる。
form_tagは、「form_tag(送信先のURL) do」のように送信先のURLを指定する。
これによって、<input type="submit" ...>のボタンを押した時に、指定されたURLにデータが送信される。

```posts/new.html.erb
<%= form_tag("/posts/create") do %>
# "posts/create"は送信先のURL

  <textarea></textarea>
  <input type="submit" value="投稿”>
<% end %>
```


**form_tag**メソッドの注意点
- **<%= %>**で囲む
- **do**と**end**の中にフォームを作る


### 実装

フォームからデータを送信するために以下を定義する
1. ルーティング
2. アクション
3. form_tag

`ルーティング`

`localhost:3000/posts/create`というURLへ、フォームからデータを送信できるようにルーティングを追加する。
ただし、`post URL => コントローラ名#アクション名`という形で定義する。
対応するアクションはpostsコントローラのcreateアクションとする。

```config/routes.rb
Rails.application.routes.draw do
  get "posts/index" => "posts#index"
  get "posts/new" => "posts#new"
  get "posts/:id" => "posts#show"

  post "posts/create" => "posts#create"
  
  get "/" => "home#top"
  get "about" => "home#about"
end
```

`アクション`
createアクションを定義する

```controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
  
  def show
    @post = Post.find_by(id: params[:id])
  end
  
  def new
  end
  
  def create
  end
end
```

`form_tag`
データの送信先を指定

```posts/new.html.erb
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">投稿する</h1>

    <%= form_tag("/posts/create") do %>
      <div class="form">
        <div class="form-body">
          <textarea></textarea>
          <input type="submit" value="投稿">
        </div>
      </div>
    <% end %>

  </div>
</div>
```

フォームの送信先を指定をしたが、現状では「投稿」ボタンを押してもまだ何も起きない。
これは
・ createアクションに対応するビューがない
・ 保存処理を書いていないため、フォームから送信されたデータがDBに保存されない
という未実装部分があるため。

### createアクションの内容を作成
`createアクションに対応するビューがない`に対する対応として**リダイレクト**を実装する。
今回はリダイレクトを用いて投稿一覧画面に転送するようにする。

### redirect_to
他のURLに転送（リダイレクト）するには、redirect_toメソッドを使用する。
redirect_toは「redirect_to(URL)」とすることで、そのページに転送することができる。

```posts_controller.rb
def create
# 指定したURLに転送する
  redirect_to("/posts/index")
end
```

``` posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
  
  def show
    @post = Post.find_by(id: params[:id])
  end
  
  def new
  end
  
  def create
    redirect_to("/posts/index")
  end
end
```

## 投稿内容を保存する
ここまででビューを用意する代わりにredirect_toを使用する事ができたので、
`保存処理を書いていないため、フォームから送信されたデータがDBに保存されない`に対応するため、送信されてきたデータをデータベースに保存する処理を2STEPで追加していく。

1. 投稿がcreateアクションに送信される様にする
2. 送信された内容を受け取り保存する

### name属性

今の状態のフォームでは、投稿ボタンを押しても入力した内容をcreateアクションに伝えることができない。
<textarea>タグにname属性を指定すると、入力データを送信することができるようになり、name属性の値をキーとしたハッシュがRails側に送られる。

投稿フォームに「ほげほげ」と入力し、投稿ボタンを押下することで、createアクションが実行される。
以下の様な例では、「content」が**name属性**となり、「ほげほげ」入力内容となる。
`{content: "ほげほげ"}`

```posts/new.html.erb
<textarea name="content"></textarea>
# 入力データを送信できるようになる
```

`1. 投稿がcreateアクションに送信される様にする`

name属性を指定したフォームに入力されたデータは、コントローラのアクション内で受け取ることが可能になる。
フォームのデータは、変数paramsで受け取り、paramsはname属性に設定した文字列をキーとしたハッシュになっている。

```post_controller.rb
def create
  params[:content]
# => "ほげほげ"
end
```

`2. 送信された内容を受け取り保存する`

実際に保存する手順に関しては、「rails console」で実施したように実装する。
以下ように、Postインスタンスを作成する際にparams[:content]を用いてそのPostインスタンスを保存することで投稿機能の完成。

```post_controller.rb
def create

  @post = Post.new(content: params[:content])
# contentが入力データであるインスタンスを作成している

  @post.save
  redirect_to("/posts/index")
end
```

### 変数paramsのまとめ

入力データを受け取るためのparamsはURLからidの値を取得するときにも使用した。
paramsは以下の2通りの使い方があるため、整理して覚えておく。
①「:○○」を使ったルーティングのURLから値を取得する
②「name="○○"」が付いたフォームの入力内容を受け取る

```rb
get "posts/:○○" => URL

<textarea name="○○"></textarea>
```

## 投稿を並び替える

現状では新しい投稿は一番下に表示されるため、上から順に新しい投稿を表示するよう実装していく。

### orderメソッド
**order**メソッドを用いることで、投稿一覧を並び替えることができる。
order(カラム名: 並び替えの順序）のように使う。並び替えの順序には、昇順（:asc）と降順（:desc）のどちらかを指定する。
created_atを基準に降順（:desc）に並べ替えると、新しいものから順番に表示するようにできる。

```posts_controller.rb
def index
  @posts = Post.all.order(created_at: :desc)
#                         作成日時      降順
end
```

## 投稿の編集/削除

### rails consoleから試す

投稿を編集するには、以下の流れで実装する

1. 編集したい投稿を取得
2. その投稿のcontentの値を上書き
3. データベースに保存

以下のようにpost.content = "新しい値"とすることで、投稿のcontentの値を上書きすることができる。

```rb
$ rails console
> post = Post.find_by(id:1)
# データベースから編集したい投稿を取得する

> post.content = "Rails"
# contentの値を上書きする

> post.save
# データベースに保存する
```

### updated_at

created_atカラムとupdated_atカラムには投稿データ作成時の時刻が入る。
投稿データを編集してデータベースに保存すると、updated_atカラムの値がデータを更新したときの時刻に更新される。

### 投稿を削除する

次は、削除の流れを「rails console」で確認する。
投稿を削除するには、削除したい投稿を取得し、その投稿に対してdestroyメソッドを用いることで、データベースから削除することができる。
投稿を削除する手順は以下

1. データベースから削除したい投稿を取得する
2. **destroyメソッド**を使って、投稿を削除する

```rb
$ rails console

> post = Post.find_by(id:2)
> post.destroy
```

### 投稿編集ページを用意する

どの投稿の編集ページか判別するために、投稿編集ページのURLには編集したい投稿のidを入れるようにする。
そのため、showアクションと同様にルーティングにidを含むようにする。
また、postsコントローラ内に`edit`アクションを作成し、対応するビューとして、`edit.html.erb`も作成する。

```routes.rb
get "posts/:od/edit" => "posts#edit"
# 編集したい投稿のidをURLに含む
# 「localhost:3000/posts/1/edit」
# 「localhost:3000/posts/2/edit」
# などのURLに対応させる
```

```posts_controller.rb
class PostsController < ApplicationController
  def edit
  end
end
```

```posts/edit.hrml.erb
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">編集する</h1>
  </div>
</div>
```

### 投稿編集ページへのリンクを追加
投稿詳細ページから投稿編集ページにアクセスできるように、show.html.erbに以下のような「編集」リンクを追加する。

```posts/show.html.erb
<%= link_to("編集", "/posts#{@post.id}/edit") %>
# editアクションのURLを指定
```

### 実装

1. `ルーティング`
まずは投稿編集ページ用のルーティングを追加する。
`localhost:3000/posts/1/edit`
`localhost:3000/posts/2/edit`
などのURLでアクセスできるルーティングを追加する。
アクションはpostsコントローラのeditアクションとする

```routes.rb
Rails.application.routes.draw do
  get "posts/index" => "posts#index"
  get "posts/new" => "posts#new"
  get "posts/:id" => "posts#show"
  post "posts/create" => "posts#create"

# 以下追加
  get "posts/:id/edit" => "posts#edit"
  
  get "/" => "home#top"
  get "about" => "home#about"
end
```

2. `アクション`

editアクションを追加する

```posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all.order(created_at: :desc)
  end
  
  def show
    @post = Post.find_by(id: params[:id])
  end
  
  def new
  end
  
  def create
    @post = Post.new(content: params[:content])
    @post.save
    redirect_to("/posts/index")
  end
  
  # editアクションを追加
  def edit
  end
  
end
```

3. `ビュー`

app/views/postsフォルダの中に`edit.html.erb`を作成

```edit.html.erb
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">編集する</h1>
  </div>
</div>
```

次に、投稿編集ページへのリンクを投稿詳細ページに作成する

```show.hrml.erb
<div class="main posts-show">
  <div class="container">
    <div class="posts-show-item">
      <p>
        <%= @post.content %>
      </p>
      <div class="post-time">
        <%= @post.created_at %>
      </div>
      <!-- 編集ページへのリンクを作成 -->
      <div class="post-menus">
        <%= link_to("編集", "/posts/#{@post.id}/edit") %>
      </div>
    </div>
  </div>
</div>
```

以上で投稿編集ページが完成するが、編集用のフォームがまだ無い。
次に編集用フォームを追加する必要がある。

##  入力フォームを用意する

編集内容を入力できるように、<textarea>タグを使って入力フォームを作成する。

### フォームに初期値を用意する
`<textarea>`タグでは、`<textarea>`**初期値** `</textarea>`のようにタグで囲んだ部分を初期値として設定できる。

```posts/edit.html.erb
<textarea>ほげほげ</textarea>
# "ほげほげ"が初期値として入力される
```

### 投稿内容をフォームの初期値にする
フォームの初期値として、編集したい投稿内容を表示する。
editアクションで、URLのidと同じidの投稿データをデータベースから取得し、そのcontentの値（＝投稿の内容）を初期値に設定する。

`localhost:3000/posts/1/edit`

```rb
get "posts/:id/edit" => "posts#edit"
```

```posts_controller.rb
def edit
  @post = Post.find_by(id: params[:id])
end
```

```posts/edit.html.erb
<textarea><%= @post.content %></textarea>
#             ↑初期値として入力↑
```


### 実装

投稿編集ページに入力フォームを用意し、フォームに初期値が入るようにする。
まずは、editアクションで、URLに含まれるidと同じidの投稿データを取得する。

```posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all.order(created_at: :desc)
  end
  
  def show
    @post = Post.find_by(id: params[:id])
  end
  
  def new
  end
  
  def create
    @post = Post.new(content: params[:content])
    @post.save
    redirect_to("/posts/index")
  end
  
  def edit
    # 変数@postを定義
    @post = Post.find_by(id: params[:id])
  end
  
end
```

続いて入力フォームを作成

```edit.html.erb
<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">編集する</h1>
    <!-- 入力フォームを作成 -->
    <div class="form">
      <div class="form-body">
        <textarea><%= @post.content %></textarea>
        <input type="submit" value="保存">
      </div>
    </div>
  </div>
</div>
```
ここまでで編集ページの見た目が完成するが、まだ入力した内容を保存することができない。
次からデータベースに保存するためのupdateアクションを追加する必要がある。

## フォームの編集内容を保存する