【Ruby on Rails】Rails Tutorialを全部終えたので、追加で「ストック機能」を実装してみた

#はじめに
Railsで「qiita」のような記事をストックする機能を実装したので、その方法を備忘録としてまとめておきます。
（基本的にはいいね機能と同じやり方）

※また、以下に記載する方法は前提として**「Rails tutorial」**を最後まで終えた程で記載させていただきます。


#実装すること
・記事をストックするボタンを作成する。
・記事がどれくらいストックされているか表示する。
・ストックした投稿をストック一覧ページで確認できるようにする。


#Stockモデルを作成する
ストック機能を実装するにあたって、最初のステップとして`Stockモデル`を作成します。

```terminal
$ rails g model Stock
```
カラムは2つ作成します。
・`post_id`カラム
・`user_id`カラム
マイグレーションファイルは以下のように修正します。

```ruby:xxxxxxxxxxxxx_create_stocks.rb
class CreateStocks < ActiveRecord::Migration[6.0]
  def change
    create_table :stocks do |t|
      t.references :user, foreign_key: true
      t.references :post, foreign_key: true

      t.timestamps
    end
    add_index :stocks, [:user_id, :post_id], unique: true
  end
end
```
```terminal
$ rails db:migrate
```

２つのカラムは、それぞれ外部キーとして追加しておきます。
このテーブルは中間テーブルとして活用し、**誰がどの投稿をストックしたか**という情報を格納しておきます。
また、`user_id`カラムと`post_id`カラムの組み合わせが一意になるようにインデックスを設定しておきます。

#アソシエーションを設定する
以下のようにアソシエーションを設定します。
`stock.rb`にはアソシエーションのついでにバリデーションも追加しておきます。


##user.rb

```ruby:user.rb
has_many :stocks, dependent: :destroy
```

##post.rb

```ruby:post.rb
has_many :stocks, dependent: :destroy
```

##stock.rb

```ruby:stock.rb
belongs_to :user
belongs_to :post

validates :user_id, presence: true
validates :post_id, presence: true
```

#stockコントローラを作成する
次に`stockコントローラ`を作成します。

```terminal
$ rails g controller Stocks
```
#コントローラに処理を加える

##stocks_controller.rb
`stockコントローラ`を作成したら、今回の実装に必要なアクションを定義しておきます。

```ruby:stocks_controller.rb
class StocksController < ApplicationController

  def create
    @stock = current_user.stocks.create(post_id: params[:post_id])
    redirect_back(fallback_location: root_path)
  end
  
  def destroy
    @stock = Stock.find_by(
      post_id: params[:post_id],
      user_id: current_user.id
      )
    @stock.destroy
    redirect_back(fallback_location: root_path)
  end

  def index
    @stocks = Stock.where(user_id: current_user.id)
  end
end
```

##posts_controller.rb
次に`postコントローラ`の**showアクション**の部分に変数を追加します。
post/showページにストックボタンを表示する為です。

```ruby:posts_controller.rb
  def show
    @post = Post.find_by(id: params[:id])
    @stock = Stock.find_by(
      user_id: current_user.id,
      post_id: @post.id
    )
  end
```
#ルーティングを設定する
次に必要なルーティングを追加していきます。
##routes.rb

```ruby:routes.rb
resources :stocks, only:[:create, :destroy, :index]
```
今回はストックする機能**（create)**とストックを解除する機能**(destroy)**の他にストックした記事を表示する為の**「index」**も追加しておきます。

#ストックしているかどうかの確認の処理と加える
ユーザーが記事に対して、すでにいストックをしているのかどうかを確認することができるように`stock?`メソッドを定義しておきます。
##user.rb
```ruby:user.rb
  #ユーザーがすでにストックしているか確認する
  def stock?(post)
    self.stocks.exists?(post_id: post.id)
  end
```

#Viewを追加&変更する
ストックボタンを実装する為に、まずはパーシャルを3つ作成していきます。
##shared/_stock.html.erb
```ruby:_stock.html.erb
<%= form_with(url:stocks_path, local: true) do |f| %>
  <div><%= hidden_field_tag :post_id, @post.id %></div>
 <%= f.submit "ストック", class:"btn-sm" %>
<% end %>
```
##shared/_unstock.html.erb
```ruby:_unstock.html.erb
<%= form_with(url: stock_path(@stock), html: {method: :delete}, local: true) do |f| %>
  <div><%= hidden_field_tag :post_id, @post.id %></div>
  <%= f.submit "ストックをやめる", class:"btn-sm" %>
<% end %>
```
##shared/_stock_form.html.erb
```ruby:_stock_form.html.erb
<% if current_user.stock?(@post) %>
  <%= render "shared/unstock" %>
<% else %>
  <%= render "shared/stock" %>
<% end %>
<%= @post.stocks.count %>
```
##posts/show
ボタンを追加したいところに以下パーシャルを追加します。

```ruby:show.html.erb
<%= render "shared/stock_form" %>
```

##stocks/index.html.erb
`indek`ページを新しく追加し、ストックした記事を表示させます。

```ruby:index.html.erb
<h1>ストック一覧</h1>
<h2>あなたがストックしたポートフォリオの一覧です。</h2>

<% @stocks.each do |stock| %>
  <%= link_to stock.post.name, post_path(stock.post) %>
<% end %>
```

#完成画


#おわり
以上で終了です。
機能の実装方法を記述しているので、デザインは全く気にしていません。
デザインはお好みで付け足してみてください。


#ソースコードはこちら
ポートフォリオ作成中のものですが。以下にあります。
https://github.com/iittaa/Sharing_Portfolio