環境など
Docker
laravel 5.8
npm 5.6.0
Redis latest
はじめに要件を明確化し、その後サーバーの実装、フロントの実装の順で書いていきます。
満たしたい要件
①ユーザーが投稿にいいねできる
②いいねは一人一度だけ。二回押すといいねが取り消される
③いいね数の増減が非同期で確認できる
④いいねした人を時系列で取得できるようにしておきたい
サーバー側の処理
今回はいいねした投稿とユーザーをRedisのzsetで管理します。
EloquentArticleRepository.php
//いいねした時の処理
public function like($articleId, $userId)
{
//unixtimeに変換
$date = Carbon::now();
$unixTime = $date->format('U');
//未いいねの場合
if(empty(Redis::zscore('article:'.$articleId, $userId))) {
Redis::zadd('article:'.$articleId, $unixTime, $userId);
$count = Redis::zcard('article:'.$articleId);
return $count;
}
//いいね済みの場合
Redis::zrem('article:'.$articleId, $userId);
$count = Redis::zcard('article:'.$articleId);
return $count;
}
//記事ごとにいいね数取得
public function getArticleLikes($articles)
{
$likes = [];
foreach($articles as $article) {
$likes[$article->id] = Redis::zcard('article:'.$article->id);
}
return $likes;
}
unixtimeをscore, user_idをvalueとしてzsetに持たせることでいいねした人を時系列ソートして取得できるようにしておきました。zsetについては公式ドキュメントご覧ください。
https://redis.io/commands/zadd
非同期でいいね数更新するためにcountをreturnしています。
ArticleController.php
public function like(Request $request)
{
return $this->article->like($request->article_id, $this->user->getAuth()->id);
}
このメソッドをpostでroute定義しておきます。
フロント側(Vueコンポーネント)の実装
LikeComponent.vue
<template>
<div>
<button v-on:click.prevent="like">
いいね!
</button>
{{ count }}
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'app',
props: {
articleId: {
type: Number,
required: true,
default: ''
},
count: {
type: Number,
required: true,
default: ''
}
},
data() {
return {
id: this.articleId,
fav: this.count,
};
},
methods: {
like() {
axios.post('/articles/like', {
article_id: this.id
}).then((res) => {
this.fav = res.data;
})
}
}
}
</script>
index.blade.php
<div id="app">
@foreach($articles as $article)
<div class="main-lists" id="fav">
<img src="/article_images/{{ $article->id }}.jpg" alt="img" class="article-image">
<div class="article-contents">
{{ $article->title }}<br>
{{ $article->body }}<br>
{{ Form::open(['route' => ['articles.show', $article->id], 'method' => 'get']) }}
{{ csrf_field() }}
{{ Form::input('hidden','article_id', $article->id) }}
{{ Form::submit('詳しく見る',["class"=>"button"]) }}
{{ Form::close() }}
</div>
<like-component :article-id="{{ $article->id }}" :count="{{ $likes[$article->id] }}">
</div>
@endforeach
</div>
axiosでapi叩いて、responseでいいね数受け取ってbindしてます。
最後に
Vue苦手だったんですけどいざ触ってみると楽で最高だー!となりました。これも30分くらいで実装できました。