LaravelとVue.jsで「いいね」機能を作る

# [<font color="#333">開発環境</font>](#-開発環境)
* Laravel5.8
* Vue.js 2系
# [<font color="#333">完成イメージ</font>](#-完成イメージ)
メルカリとかみたいにユーザーが投稿したアイテムがあり、その詳細画面で不特定多数のユーザーが
いいね出来る感じ。

<img width="200" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920917/523eff06-5bf6-2926-4680-5c4e145e7b17.gif">

# [<font color="#333">Modelとマイグレーションを作る</font>](#-Modelを作る)
```
$ php artisan make:model Like　-m
```

マイグレーションは以下の通り。
```migration.php
<?php

  public function up()
    {
        Schema::create('likes', function (Blueprint $table) {
            $table->bigIncrements('id');
            //紐づくユーザーが削除（退会等）されたらいいねも削除
            $table->integer('user_id')->references('id')->on('users')->onDelete('cascade');
　　　　　　　//紐づくアイテムが削除されたらいいねも削除
            $table->integer('item_id')->references('id')->on('items')->onDelete('cascade');
            $table->timestamps();
        })
```

***モデル***
```Like.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Like extends Model
{
    protected $fillable =['user_id','item_id'];

   //いいねは一人のユーザーに属する
    public function users(){
        return $this->belongsTo('App\User');
    }
　//一つのアイテムに属する
    public function ideas(){
        return $this->belongsTo('App\Item');
    }
}
```

その他、UserモデルとItemモデルのリレーションは以下の通り

```User.php

//一人のユーザーが複数のアイテムを投稿する可能性があります
 public function Items() {
        return $this->hasMany('App\Models\Item');
    }
 
//一人のユーザーが複数のアイテムに対していいねを行う可能性があります。
    public function likes() {
        return $this->hasMany('App\Models\Like');
    }
```

```Item.php
//一つのアイテムは一人のユーザーに属します。
   public function user() {
        return $this->belongsTo('App\Models\User');
    }
//アイテムには複数いいねが付くことが想定されます。
    public function likes() {
        return $this->hasMany('App\Models\Like');
    }
```

# [<font color="#333">ルーティング設定</font>](#-ルーティング設定)
```web.php
//いいねを付ける
 Route::post('/like_add/{id}','LikeController@like_add')->name('like.add');
```
# [<font color="#333">コントローラー</font>](#-コントローラー)

こちらはアイテム詳細画面用のコントローラー。
皆さんの状況に合わせてください。
```MyController.php

<?php

namespace App\Http\Controllers;

use App\Item;
use App\Like;
use App\User;
use Illuminate\Support\Facades\Auth;

class MyController extends Controller
{


    //アイテム詳細画面表示
    //=======================================================
    public function item_detail($id)
    {

   
        $user = Auth::user();
        $user_id = $user->id;

        //アイテム詳細ページ用にidでアイテム取得
        $item = Item::find($id);

        //自分は気になるを押してあるアイテムか判別
        $my_like_check = Like::where('item_id', $id)->where('user_id', $user->id)->get()->count();

        //アイテムの気になるを押されている総数取得
        $item_likes = Like::where('item_id', $id)->get()->count();

        //vueで気になるを押した時の処理用ルーティング
        $axios_path = route('like.add', ['id' => $id]);

    

        return view('views.item_detail', compact('item', 'user_id', 'my_like_check', 'item_likes', 'axios_path', ));
    }



}

```

いいね処理を行うコントローラー
```LikeController.php

<?php

namespace App\Http\Controllers;

use App\Item;
use App\Like;
use Illuminate\Support\Facades\Auth;

class LikeController extends Controller
{
     //気になるを押した時の処理
    //=======================================================
    public function like_add($id)
    {
        //引数$idの中にはアイテムのidが入ってくる
        $user = Auth::user();
        $idea = Item::find($id);
        //自分が気になるを押したアイデアかどうか。最初のハートの色判別用
　　　　//Likesテーブルの中に自分のidかつ該当のアイテムのidの情報があるかどうか（つまりその商品に自分がいいねをしてるかどうか）
        $my_like = Like::where('user_id', $user->id)->where('item_id', $id)->get();
        //もし自分が気になる追加ずみならDBから消す（いいね解除）
        if ($my_like->count() > 0) {
            Like::where('user_id', $user->id)->where('item_id', $id)->delete();
        } else {
            //まだならDB追加
            Like::firstOrCreate(
                array(
                    'user_id' => $user->id,
                    'item_id' => $item->id,
                )
            );
        }
    }

}
```
# [<font color="#333">表示用viewファイル</font>](#-表示viewファイル)

```like.php
 <div id="like">
        <like-component :item_id="{{$item->id}}" :my_like_check="{{$my_like_check}}" :like_num="{{$item_likes}}" :user_id="{{$user_id}}" axios_path="{{$axios_path}}">
        </like-component>
 </div>
```



# [<font color="#333">Vue.jsで実装</font>](#-Vue.jsで実装)
```like.js
Vue.component('like-component', require('./components/Like.vue').default);

const like = new Vue({
    el: '#like',
});
```


```like.vue
<template>
  <div class="like-component">
   <div class="p-like-area" > 
  <!-- Font Awesome使います -->
    <i
      class="fas fa-heart fa-2x c-icon__like"
　　　　<!-- クリックしていれば赤くするクラス「u_liked」付与 -->
      :class="{ u_liked: this.clickFlg }"
      @click="addLike"
    ></i>
　　<!-- いいね総数表示 -->
    <span class="like-num" :class="{ liked: this.clickFlg }">{{ likeNumber }}</span>
  </div>
</div>
</template>

<script>
export default {
  props: ['item_id','my_like_check','like_num','user_id','axios_path'],
  data: function () {
    return {
      likeNumber: this.like_num,
      clickFlg: false,
    };
  },
//自分が気になるを押してるアイデアなら最初から色つける
  created() {
    if(this.my_like_check){
      this.clickFlg = true;
    }
    
  },
  methods: {
 　　//いいね追加または解除処理(いいね総数の増減と色の付け外しのみ)
    addLike() {
      this.clickFlg = !this.clickFlg;
      if (this.clickFlg) {
        ++ this.likeNumber;
      } else {
        -- this.likeNumber;
      }
     //axiosでLikeController内の処理を行う
      axios
        .post(this.axios_path)
        .then((response) => {
    
        })
        .catch(function (err) {
          console.log(err);
        });
    },
   
  },
};
</script>
```

いいねの色の付け外しやいいね数の増減は完全にフロントのみの処理で行いましょう。
ここにDBとのやり取りをかましたりするといいねを連打した時に処理が追い付かずバグります。

# [<font color="#333">おまけCSS</font>](#-おまけCSS)
```like.scss

//ハートマークスタイリング
.c-like{
  padding: 8px 4px;
  cursor: pointer;
  &icon{
    cursor: pointer;
    color: red;
  }
}

//気になる押した時に文字色変える処理
.c-like__add{
  color: red;
  cursor: pointer;
}

//気になる解除押した時に文字色変える処理
.c-like__delete{
  color: rgb(22, 131, 194);
  cursor: pointer;
}


//気になるボタンエリア
.p-like-area{
  text-align: center;
  padding: 8px;
}

//気になる押した時のアニメーション設定
@keyframes heart {
  0% {
    transform: scale(0.8);
  }
  50% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(1);
  }
}

.u_liked {
  color: red;
  animation: heart 0.4s ease-in alternate;
}
```
大丈夫なはず。
大丈夫ではなければご指摘ください。