Microposts機能（rails）

この記事では、Micropostsにおける投稿機能について、簡単に理解したのでその確認として理解を深めることを目的としている。

投稿機能は以下の手順デコーディングを行う

1. Micropostモデル、contentカラム、user_idカラム（外部キー）を作る
2. UserテーブルとMicropostテーブルを関連付ける
3. UsersコントローラーのshowアクションにMicropostの中身を表示させるための「@microposts」を記載する
4. Usersフォルダーのviewのshow.html.erbにも@micropostsを記載する
5. Micropostsコントローラーを作り、_micropost.html.erbにmicropostテーブルのcontentカラムの中身を表示させるファイルを作る
6. Homeページをアップデートする
7. 


#1.Micropostモデル、contentカラム、user_idカラム(外部キー)を作る
 
```ruby:
$ rails g model Micropost content:string user_id:integer
↓
$ rails db:migrate:reset db:seed
```
↑データベースをいじったので一度migrateファイルをリセットしておく

## Microposは誰の投稿化を知るために必ずuser_idを持たなければいけないので以下のコードを打ち込む

```ruby:
class Micropost < ActiveRecord::Base
***    validates :user_id, presence: true
end
```

#UserテーブルとMicropostテーブルを関連付けをする

```ruby:
models/micropost.rb
class Micropost < ApplicationRecord
***  belongs_to :user
  validates :user_id, presence: true
end
```
```ruby:
models/user.rb
class User < ApplicationRecord
***  has_many :microposts, *dependent: :destroy
  ...
end
```

## 最新の投稿が投稿欄の一番上に表示されるようにする

```ruby:
class Micropost < ApplicationRecord
 belogns_to :user
*** default_scope -> { order('created_at DESC')}
 validates :user_id, presence: true
end
```

*dependent: :destroyを使うことによりuserが退会した場合にそのuserが投稿した内容も同時に削除されるようにする

##投稿文字数をTwitterのように140文字以内でしか投稿できないようにする

```ruby:
class Micropost < ApplicationRecord
 belongs_to :user
 default_scope -> {order('created_at DESC')}
*** validates :content, presence: true, length: { maximum: 140 }
    validates :user_id, presence: true
end
```

#3. UsersコントローラーのshowアクションにMicropostの中身(content)を表示させるための「@microposts」を記載する

```ruby:
class UsersController < 
ApplicationController

///
   def show
        @user = User.find(params[:id])
        @microposts = @user.microposts.paginate(page: params[:page], per_page: 12)
        #userに紐づくすべての投稿をpaginateで取得する。
   end
```

```ruby:views/users/show.html.erb

<% provide(:title, @user.name)%>
<div class="container pt-4">
<div class="row">
    <aside class="span">
        <section>
            <h1>
                <%= gravatar_for @user %>
                <%= @user.name%>
            </h1>
        </section>
    </aside>

<div class="col-9">
    <div>
        <% if @user.microposts.any?%>
   #モデルにデータが存在する→true/存在しない→falseを返すメソッド

        <h3>Microposts (<%= @user.microposts.count %>)</h3>
        <ol class="microposts">
            <%= render @microposts%> 
        </ol>
        <%= will_paginate @microposts%>
        <% end %>
    </div>
    </div>

```

#5. Micropostsコントローラーを作り、_micropost.html.erbにmicropostテーブルのcontentカラムの中身を表示させるファイルを作る

```ruby:terminal
rails g controller Microposts
```
=>createアクションとdestroyアクションを作るためにmicropostsコントローラーを作る

```ruby:views/microposts/_micropost.html.erb
<li>
   <h4><%= micropost.content%></h4>
   #micropostテーブルのcontentカラムの内容を表示
   <span class="timestamp">
        Posted <%= time_ago_in_words(micropost.created_at) %> ago.
    #created_atで作成日時を表示する際に、Twitterのように「～前」と表示する方法　※ex:約19時間前
   </span>
</li>
```
=> showページでmicroposts投稿内容と作成日を表示させる
![microposts.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/e6cc7194-0252-f4e4-0dab-4ac640f41c4b.jpeg)


##▼Fakerでダミーデータを作成する

```ruby:db/seeds.rb
  users = User.order(:created_at).take(7)
  50.times do
    content = Faker::Lorem.sentence(3)
    users.each { |user| user.microposts.create!(content:content) }
  end
```

```ruby:
$rails db:migrate:reset db:seed
```
=> データベースの中身を変更したのでリセットする

![ダミー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/8982baf0-52b7-c41a-a3ce-21a5e440ea7f.jpeg)

##createとdestroyのルーティングをresourcesメソッドを使って定義する
```ruby:config/routes.rb
///
resources :users

resources :sessions, only: [:new, :create, :destroy]

resources :microposts, only: [:create, :destroy]
///
```

#6. Homeページをアップデートする
```ruby:views/static_pages/home.html.erb
<% provide(:title, 'Home')%>
<% if logged_in?%>
    <div class="row">
        <aside class="col-3">
            <section>
                <%= render 'shared/user_info' %>
            </section>
            <section>
                <%= render 'shared/micropost_form' %>
            </section>
        </aside>


     <!-- Microposts here -->

    </div>
    <% else %>
    <h1 style="text-align:center;">Awesome Blog</h1>
        <p>
            Welcome to Awesome Blog!<br>
        </p>
        <p>
            This Blog is made for all people who want to get good shape of the body!!
        </p>
```
=>userがログインしているときは投稿機能とユーザーの情報を表示するがログインしていないときはAwesome Blogを表示するようにする

```ruby:views/shared/_user_info.html.erb
   <%= link_to gravatar_for(@user, size: 52), @user%>
   <h1>
      <%= @user.name%>　#usersデーブルのnameカラム
   </h1>
   <span>
        <%= pluralize(@user.microposts.count, "micropost")%>
     #pluralizeヘルパー...最初の引数に整数があると、それに基づいて2番目の引数である英単語が複数形に変化したものが渡される。
   </span>
```
```ruby:views/shared/_micropost_form.html.erb
 <%= form_for(@micropost) do |f| %> #特定のモデルに特化したフォームを作りたい　→ form_for
    <div>
        <%= f.text_area :content, placeholder: "Compose new micropost...", class: "w-100"%>
    </div>
    <%= f.submit "Post", class: "btn btn-block btn-primary" %>
 <% end %>
```
=>form_forメソッドを使うとモデルのインスタンスを渡すだけで自動的にRailsが処理してくれる（RESTfulなリソースを使っている場合）
form_for(@micropost)の@micropostがすでに保存されていた場合にはupdateアクション、未保存の時はcreateアクションを、Railsは自動的に選んでくれる※@micropostは後でhomeコントローラーで定義する

![user_info.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/3fa8294f-4643-f897-5101-a5ed255d63f2.jpeg)


#micropostsコントローラーのcreate,destroyアクションにアクセスできるのはログインを正しくしたユーザだけに制限する

```ruby:controllers/microposts_controller.rb
 before_action :only_loggedin_users, only: [:create, :destroy]
 
 def create
 end

 def destroy
 end
```
=>before_actionをすることによりURLから直接アクセスされることを防ぐ（ログインしていなければログインページにとぶ）

##micropostsコントローラーの中身を記載していく

```ruby:controllers/microposts_controller.rb
 before_action :only_loggedin_users, only: [:create, :destroy]
 
 def create
    @micropost = current_user.microposts.build(micropost_params)
     #microposts_paramsとは下のprivateで定義したもの
     #.build≒.new
    if @micropost.save
        flash[:success] = "Successfully saved!"
        redirect_to root_url
    else
        flash[:danger] = "Invalid content. Try again."
        redirect_to root_url
    end
 end

 def destroy
 end

 private
 def micropost_params
    params.require(:micropost).permit(:content)
 end
```
=>投稿内容を@micropostに格納し保存するに成功したらflashメッセージを表示させ(flash[:success]=)homeページに投稿を反映させる(redirect_to root_url)

```ruby:controllers/static_pages_controller
 class StaticPagesController < ApplicationController
    def home
        @micropost = current_user.microposts.build if logged_in?　#1対多の場合では、「関連付けメソッド名.build」となる
    end

    def about
    end

    def contact
    end
 end
```
=>現在ログインしているユーザーモデルに紐づく投稿のオブジェクトを作成する



##▼すべての投稿を表示する
```ruby:views/static_pages/home.html.erb
<% if logged_in?%>
    <div class="row">
        <aside class="col-3">
            <section>
                <%= render 'shared/user_info' %>
            </section>
            <section>
                <%= render 'shared/micropost_form' %>
            </section>
        </aside>


     <!-- Microposts here -->
     <div class="col-9">
        <h3>Micropost Feed</h3>
***        <%= render 'shared\feed'%>
     </div>

    </div>
    <% else %>
```
=>_feed.html.erbに投稿一覧を表示するようにする

```ruby:controllers/static_pages_controller
 def home
     #@micropost = current_user.microposts.build if logged_in?
     if logged_in?
          @micropost = current_user.microposts.build
          @feed_items = current_user.feed.paginate(page: params[:page], per_page: 12)
          #user.rbで定義したfeedメソッドをの取得結果を@feed_itemsに格納する
          @user = current_user
     end
 end
```
=>homeアクションのリファクタリングを行う。

```ruby:models/user.rb#feed
///
 def feed
      Micropost.where("user_id = ?", id)
 end
///
```
=>この部分がいまいち理解できない



```ruby:views/shared/_feed.html.erb
    <% if @feed_items.any? %>
       <ol>
           <%= render partial: 'shared/feed_item', collection: @feed_items %>
       </ol>
       <%= will_pagenate @feed_items %>
    <% end %>
```

```ruby:views/shared/_feed_item.html.erb
   <li class="col-12 my-4">
      <%= link_to gravatar_for(feed_item.user), feed_item.user %>
      <span class="user">
          <%= link_to feed_item.user.name, feed_item.user %>
      </span>
      <h4><%= feed_item.content %></h4>
      <span class="timestamp">
          Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
      </span>
   </li>
```
![feed.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/7aea941b-d790-8ada-68c0-11718ed46167.jpeg)
👇
![feed_item.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/965206/8327f01b-e87d-2344-46a2-9497226df6f3.jpeg)

```ruby:controllers/microposts_controller.rb#create
///
def create
    @micropost = current_user.microposts.build(micropost_params)
    if @micropost.save
        flash[:success] = "Successfully saved!"
        redirect_to root_url
    else
***        @feed_items = []
        flash[:danger] = "Invalid content. Try again."
        redirect_to root_url
    end
 end
///
```
=>@feed_items = []を加えることで@feed_itemsの中に何も入っていない（nil）の時に発生するエラーを防ぐ


#削除機能を作る

```ruby:views/microposts/_microposts.html.erb
<li>
   <h4><%= micropost.content%></h4>
   <span class="timestamp">
        Posted <%= time_ago_in_words(micropost.created_at) %> ago.
   </span>
***
   <% if current_user?(micropost.user) %>
       <%= link_to "delete", micropost, method: :delete,
                                        data: { confirm:"Are you sure?"} 
                                        title: micropost.contentn %>
   <% end %>
***
</li>
```
=>ログインしているユーザーのshowページにだけ削除機能があらわれるようにする

```ruby:views/shared/_feed_item.html.erb
<li class="col-12 my-4">
      <%= link_to gravatar_for(feed_item.user), feed_item.user %>
      <span class="user">
          <%= link_to feed_item.user.name, feed_item.user %>
      </span>
      <h4><%= feed_item.content %></h4>
      <span class="timestamp">
          Posted <%= time_ago_in_words(feed_item.created_at) %> ago.
      </span>
***
      <% if current_user?(feed_item.user) %>
          <%= link_to "delete", feed_item, method: :delete,
                                           data: { confirm: "You sure?"}
                                           title: feed_item.content %>
       <% end %>
***
 </li>
```

###destroyアクションの中身を記載する
```ruby:controllers/microposts_controller.rb#destroy
 def destroy
     Micropost.find(params[:id]).destroy
     redirect_to root_url
 end
```