LoginSignup
2

posted at

updated at

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

開発環境

  • Laravel5.8
  • Vue.js 2系

完成イメージ

メルカリとかみたいにユーザーが投稿したアイテムがあり、その詳細画面で不特定多数のユーザーが
いいね出来る感じ。

mojikyo45_640-2.gif

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');
    }

ルーティング設定

web.php
//いいねを付ける
 Route::post('/like_add/{id}','LikeController@like_add')->name('like.add');

コントローラー

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

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,
                )
            );
        }
    }

}

表示用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>

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とのやり取りをかましたりするといいねを連打した時に処理が追い付かずバグります。

おまけ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;
}

大丈夫なはず。
大丈夫ではなければご指摘ください。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2