Ruby on Rails 第13章

[Rails Tutorial 第13章](https://railstutorial.jp/chapters/user_microposts?version=5.1#cha-user_microposts)を見ていきます。

# この章で学ぶこと
- 投稿フォームの作成


# 概念


# 組立方
1. Micropostデータモデル作成
2. Userモデルと関連づけ
3. 関連付けに伴う細かなこと
4. 投稿内容の一覧表示
5. 投稿フォームのview作成
6. 画像の投稿
7. 画像のバリデーション

#1. Micropostデータモデル作成
![image.png](https://qiita-image-store.s3.amazonaws.com/0/180679/595c6a79-ae6d-8e38-ba7a-7095ddf208c7.png)

`rails generate model Micropost content:text user:references`

これによって、Micropostと紐づける必要のあるuserに関する項目が色々追加されます。

```ruby:app/models/micropost.rb
class Micropost < ApplicationRecord
 belongs_to :user
end
```
=> MicropostがUserと1対1の関係であることを示すコード

```ruby:db/migrate/[timestamp]_create_microposts.rb
class CreateMicroposts < ActiveRecord::Migration[5.0]
 def change
  create_table :microposts do |t|
   t.text :content
   t.references :user, foreign_key: true
   
   t.timestamps
  end
  add_index :microposts, [:user_id, :created_at] #この行は追加してください
 end
end
```
=> UserとMicropostを関連づける下準備が完了

indexをuser_idとcreated_atにつけているのは、
micropostsをuser_idに紐づくcreated_atの降順で取得することを前提にしているからです。

なお、このindexが配列になっているのは両方を使って検索することを前提としているからです。
user_idとcreated_atがセットで検索されることが前提の場合はできるだけまとめてあげたほうが高速化するようです。

#2. Userモデルと関連づけ
1.でMicropostモデルを作成するのと合わせてUserモデルとの関連づける下準備をしたので、
ここではもっとその関連を強めてアクセスしやすくします。
具体的には下記のようにしたい。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/180679/f1645564-0b79-3d42-3280-e22124d39a27.png)

必要なのはuser.rbとmicropost.rbに`has_many`と`belongs_to`を追加してあげること。

```ruby:app/models/user.rb
class User < ApplicationRecord
 has_many :microposts
 ...
end
```

#3. 関連付けに伴う細かなこと
##▼ user.micropostsでcreated_atの降順に取り出すようにしたい
```ruby:app/models/micropost.rb
...
 default_scope -> { order(created_at: :desc) }
 #SQL得意な人は下記のような書き方も可能
 #default_scope -> { order('created_at DESC') }
...
```

矢印みたいなのはラムダ式という文法らしく、深い内容を探りすぎると時間かかりすぎそうなので、
ここでは「ブロックを引数に取り、Procオブジェクトというものを生成している」と理解しておきます。
ちなみに、callメソッドでProcオブジェクトの中身は見れるようです。

##▼ userが削除されたらuser.micropostsも削除する
```ruby:app/models/user.rb
class User < ApplicationRecord
 has_many :microposts, dependent: :destory
 ...
end
```

has_manyのオプションに加えてあげるだけで終わりです。

#4. 投稿内容の一覧表示
今回は、 `users#show` のページにmicropostsを表示するということをします。
大枠としては下記のような感じです。

- `users_controller.rb`の`show`メソッドが呼び出される。
- `users_controller.rb`の中で`@microposts = user.microposts` のインスタンスを作成する。
    - 3までで既にuserとmicropostを関連づけてるので実現できる。
- `users/show.html.erb`内で`@microposts`をレンダリングする。
    - そうすると、`microposts/_micropost.html.erb`が呼び出されて描画される。
- paginationを入れる。

ポイントは**MicropostのデータをUsers_controllerで表示する**ということです。

## ▼ 投稿一覧を表示
```ruby:controllers/users_controller.rb
def show
 @user = User.find(params[:id])
 @microposts = @user.microposts
end
```
=> ユーザーに紐づく投稿を全て取得

```erb:views/users/show.html.erb
<%= render @microposts %>
```
=> _micropost.html.erbを投稿の数だけ呼び出して描画
=> その時に `@microposts.each { |micropost| render micropost }` が内部ではおきている。

```erb:views/microposts/_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
 <%= link_to gravatar_for(micropost.user, size:50), user_path(micropost.user) %>
 <span class="user"><%= link_to micropost.user.name, user_path(micropost.user) %></span>
 <span class="content"><%= micropost.content %></span>
 <span class="timestamp">
  Posted <%= time_ago_in_words(micropost.created_at) %> ago.
 </span>
</li>
```
=> あくまで`show.html.erb`のパーシャルなので、変数はそのまま利用できる。

## ▼ ページング
まずは必要なGemを追加。

```text:Gemfile
gem 'will_paginate'
gem 'bootstrap-will_paginate'
```

あとは、ページングしたいページで表示件数を制御し、ページングのビューを出してあげる。

```ruby:controllers/users_controller.rb
@microposts = @user.microposts.paginate(page: params[:page])
```
=> params[:page]でページを取得している。一律30件で制御されている。

```erb:views/users/show.html.erb
<%= will_paginate @microposts %>
```
=> `will_paginate`だけでは`users`の一覧だと思ってしまうので引数で明示してあげる。

#5. 投稿フォームのview作成
- `routes.rb`の設定
- ログイン状態の確認
- createアクションの作成
- フォームの作成

```ruby:routes.rb
resources(:microposts, only:[:create, :destroy])
```
=> Micropostではviewを持たないのでcreateとdestroyだけに制御する

```ruby:controllers/application_controller.rb
def logged_in_user
 unless logged_in?
  flash[:danger] = "Please Login"
  redirect_to login_url
 end
end
```
=> まずはログイン状態を確かめるメソッドをどこからでもアクセス可能なように作成する

```ruby:controllers/micropost_controller.rb
before_action(:logged_in_user, only: [:create, :destroy])
```
=> 投稿に関するコントーラーが動き出すタイミングでログイン状態をチェックする

```ruby:controllers/micropost_controller.rb
def create
 @micropost = current_user.microposts.build(micropost_params)
 if @micropost.save
  flash[:success] = "Micropost created"
  redirect_to root_url
 else
  render 'static_pages/home'
 end
end

private
 def micropost_params
  params.require(:micropost).permit(:content)
 end
```
=> Strong Parametersで投稿のオブジェクトを作成し、DBに保存できるのかどうかで投稿が成功したかどうかを判別

```erb:_micropost_form.html.erb
<%= form_for(@micropost) do |f| %>
 <div class="field">
  <%= f.text_area :content, placeholder: "Compose new micropost..." %>
 </div>
 <%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
```
=> テキストを入力するtext_areaと投稿するsubmitボタンをform_forで作成

#6. 画像の投稿
- 必要なGemのインストール
- 画像アップローダーの生成
- Micropostにカラムを生成
- CarrierWaveに画像と関連づけたモデルを伝える
- ファイルアップロードボタンの作成

```ruby:Gemfile
...
gem 'carrierwave'
gem 'mini_magick'
...
group :production do
 ...
 gem 'fog'
end
...
```
=> 画像アップローダーと、画像圧縮と本番にあげるように必要なgemのインストール

`rails generate uploader Picture`
=> carrierwaveによってuploaderが使えるようになります。

`rails generate migration add_picture_to_microposts picture:string`
`rails db:migrate`
=> DBにカラムの追加

```ruby:models/micropost.rb
...
mount_uploader :picture, PictureUploader
...
```
=> アップローダーの設定をモデルのカラムと紐づける

```ruby:controllers/microposts_controller.rb
@micropost = current_user.microposts.build(micropost_params)

private
 def micropost_params
  params.require(:micropost).permit(:content, :picture)
 end
```
=> 画像がアップロードされるときに :picture も許可する

```erb:_micropost_form.html.erb
<span class="picture">
 <%= f.file_field :picture %>
</span>
```
=> 画像をアップロードするボタンを設置

```erb:views/microposts/_micropost.html.erb
<%= image_tag micropost.picture.url if micropost.picture? %>
```
=> 画像を表示する領域

# 7. 画像のバリデーション