商品一覧ページ内のカートに入れるボタンの実装(非同期通信)

#はじめに
商品一覧ページ内の各商品に割り振られているカートに入れるボタンが押された際にuser_idとeffector_id(現在作成中のアプリケーションではエッフェクターが商品のため各所でeffectorを使用していく)にログイン中のuserのidとクリックされた商品のidを保存することを目指す。今回は非同期通信を使用したい。
#テーブル、コントローラー、APIの作成
##cartsテーブルの作成
```:ターミナル
アプリケーションのディレクトリ$ rails g model cart
```
cartモデルの作成

```(migrateファイル)create_carts.rb
class CreateCarts < ActiveRecord::Migration[5.0]
  def change
    create_table :carts do |t|
      t.references :user, foreign_key: true
      t.references :effector, foreign_key: true
      t.timestamps
    end
  end
end
```
マイグレーションファイルの編集

```:ターミナル
アプリケーションのディレクトリ$ rails db:migrate
```
マイグレーションの実行

```(modelファイル)cart.rb
class Cart < ApplicationRecord
  belongs_to :effector
  belongs_to :user
end
```
```(modelファイル)effector.rb
class Effector < ApplicationRecord
  belongs_to :genre
  has_many :users, through: :carts
  #今回はcartsコントローラーのshowアクションを使用するため上の記述は不要
  has_many :carts
end
```
```(modelファイル)user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :boards
  has_many :effectors, through: :carts
  #今回はcartsコントローラーのshowアクションを使用するため上の記述は不要
  has_many :carts
end
```
各モデル内にリレーションを記載
##APIの準備
app/controller/api/carts_controller.rbを作成

```app/controller/api/carts_controller.rb
class Api::CartsController < ApplicationController
  def create
    Cart.create(user_id: params[:user_id], effector_id: params[:effector_id])
    #json形式で送られてきたデータからparams[:user_id]をuser_idカラムにparams[:effector_id]をeffector_idカラムに保存
  end
end
```
ルーティング設定

```routes.rb
Rails.application.routes.draw do
  root to: "effectors#index"
  devise_for :users
  resources :users, only: [:show, :edit, :update]
  resources :boards
  resources :effectors do
    resources :genres
  end
  resources :carts, only: [:show, :destroy]
  namespace :api do
    resources :carts, only: :create, defaults: { format: 'json'}
  end
  #cartsコントローラーとapp/controller/api/carts_controller.rbのルーティング設定
end
```
ここまで設定が終わったら商品一覧のカートに入れるボタンが押された際にcartsテーブルにuser_idとeffector_idが保存されるようにjsを記述していく。
#商品一覧のカートに入れるボタンが押された時の処理
###カートに入れるボタンが押された時onclick:を用いてjs内の関数を発火できるように記述する。

```app/views/effectors/index.html.haml
.main
  .main__list
    - @effectors.each do |effector|
      .main__list__effector{data: {genre: effector.genre.id}}
        .main__list__effector__info
          .main__list__effector__info__visual
            %h.main__list__effector__info__visual__name
              = effector.name
            .main__list__effector__info__visual__image
              %img{alt: "image1", class: "main__list__effector__info__visual__image__file", src: "#{- effector.image1}"}
              %img{alt: "image2", class: "main__list__effector__info__visual__image__file", src: "#{- effector.image2}"}
          .main__list__effector__info__text
            %h.main__list__effector__info__text__genre
              = effector.genre.genre
            %h.main__list__effector__info__text__point
              = effector.point
              pt
            %br 
            %h.main__list__effector__info__text__detail
              = effector.text
          .main__list__effector__info__btns
            - if effector.youtube != nil
              .main__list__effector__info__btns__video
                %button.main__list__effector__info__btns__video__btn{onclick: "test(#{effector.youtube})"}
                  動画を視聴
            - if effector.link != nil
              .main__list__effector__info__btns__official
                = link_to "#{effector.link}", class: "main__list__effector__info__btns__official__btn" do
                  .main__list__effector__info__btns__official__btn__text
                    公式サイト
            - if user_signed_in?
              .main__list__effector__info__btns__cart
                %button.main__list__effector__info__btns__cart__btn{onclick: "createCart(#{current_user.id},#{effector.id})"}
                  -#クリックされた時関数名createCartを引数(current_user.id,effector.id)とし発火
                  カートに入れる
  .popup
    .popup__content
      .popup__content__youtube
        %iframe(width="560" height="315" id = "youtube_test" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen)
      %button#close
        閉じる
```
###発火される関数の記述

```effectors.js
function createCart(userId, effectorId) {
  var param = {
    user_id: userId,
    effector_id: effectorId
    //受け取った引数をキーuser_id:とキーeffector_id:にそれぞれのバリューとして変数paramに格納
  }
  ajaxRequest("api/carts",'post',param)
  //非同期通信する際の関数を複数回の利用を見越して外出しして呼び出す(引数をapi/carts,post,paramとし呼び出し)
}

function ajaxRequest(url,type,data) {
  $.ajax({
    url: url,
    //url: api/cartsと同義
    type: type,
    //type: postと同義
    dataType: 'json',
    data: data
    //data: paramと同義
    //引数で持ってきた仮引数url,type,dataを使用しajaxの記述をする
  })
  .done(function() {
    alert('商品をカートに入れました')
  })
  .fail(function() {
    alert('error');
  });
}
```
ここまできたらjson形式で送られたデータをapi側のコントローラーで保存してあげるだけである。
先ほど記述したapp/controller/api/carts_controller.rbを見てみる

```app/controller/api/carts_controller.rb
class Api::CartsController < ApplicationController
  def create
    Cart.create(user_id: params[:user_id], effector_id: params[:effector_id])
    #json形式で送られてきたデータからparams[:user_id]をuser_idカラムにparams[:effector_id]をeffector_idカラムに保存
  end
end
```
すでに記載している通りにjosn形式で送られてきたデータからcartsテーブルのuser_idカラムにparams[:user_id]をeffector_idカラムにparams[:effector_id]を保存する記載をする。
これにてカートに入れるボタンの実装が完了します。
#終わりに
今回はカートに入れるボタンというショッピングサイトには欠かせない機能を非同期通信にて行えるように実装した。
ユーザーと商品という多対多という関係性、非同期通信でのデータの保存など初学者が苦労する機能の実装を行うことで力がつくのを感じました。
この機能はいいね機能などにも流用できると思うので学習する価値ありだと感じます。
2回目の投稿のため拙い文章が眼に余るかと思いますがご精読ありがとうございました。駆け出しエンジニアのみなさん一緒に頑張っていきましょう。