フォロー機能を実装したので実装の流れを書いていきます。vueを使って非同期で変化するように書いていったのでその部分にも触れます〜。
続きの記事ではリスト一覧からでもリアルタイムに処理できる処理を書いています!
laravel7.xです。
目次
大まかな流れ
マイグレーションファイル、モデル作成、設定
ルーティング設定
コントローラーにアクション設定
vue側の設定
って感じで実装していきます。
前提
ユーザー関連の設定は済んでいる前提で話を進めていきます。
マイグレーションファイル、モデル作成、設定
中間テーブルの役割をしてくれるfollowsテーブルを作成します。-mでマイグレーションファイルも一緒に作っていきます。
php artisan make:model Follow -m
class Follow extends Model
{
protected $fillable = ['following_id', 'followed_id'];
}
Userモデル
public function isFollowing($user_id)
{
// exists()でテーブルにレコードが存在しているかをチェック
return $this->follows()->where('followed_id', $user_id)->exists();
}
public function isFollowed($user_id)
{
return $this->followers()->where('following_id', $user_id)->exists();
}
public function up()
{
Schema::create('follows', function (Blueprint $table) {
$table->id();
$table->foreignId('following_id')->constrained('users')->onDelete('cascade')->comment('フォローする人');
$table->foreignId('followed_id')->constrained('users')->onDelete('cascade')->comment('フォローされる人');
$table->timestamps();
});
}
外部キーについては7.x系から上記の書き型でも対応できるようになりました。(多分7.x系からだったはず。。。)
Userモデルにメソッドをいくつか設定しています。
ルーティング設定
Route::post('/user/{user}/follow', 'UserController@follow_do')->name('follow');
Route::get('/user/{user}/follow_check', 'UserController@follow_check')->name('follow_check');
getの3行についてはvue.jsで使用していくもの、という理解で進めてください。
コントローラーにアクション設定
フォローの追加、解除はどちらもtoggleを使っていきます。最初はsyncでやろうと思ったんですけど配列を渡さないといけないみたいだったのでtoggleにしました。
public function show(User $user)
{
return view('users.show')->with('user', $user);
}
public function follow_do(User $user)
{
$follow_user = Auth::user();
if($follow_user->id != $user->id) {
// syncは配列で作る必要がある。今回はtoggleで。
#1 $follow_user->follows()->toggle($user->id);
return $follow_user->isFollowing($user->id);
}
}
#2 public function follow_check(User $user)
{
$check_user = Auth::user();
return $check_user->isFollowing($user->id);
}
#1, toggleについてはこちら。これでattach,detachどちらもやってくれます。
それ以外のアクションはvueから呼ばれるアクションになってます。
#2,ログイン中のユーザーが$userをフォローしているかどうかをチェックしています。
blade側の設定
まずはshow.blade.phpから見ていきます。
<div class="profile-userbuttons">
@if(Auth::check() && Auth::id() != $user->id)
<follow-button login_user_id="{{Auth::id()}}" user_id="{{$user->id}}" csrf="{{json_encode(csrf_token())}}" following="{{Auth::user()->isFollowing($user->id)}}" followed="{{Auth::user()->isFollowed($user->id)}}"></follow-button>
@endif
</div>
follow-buttonというコンポーネントを呼び込んでいます。その際にいくつか情報を渡しています。
Vuexの導入
こちらの記事を参考にVuexを導入していきましょう。
vue側の設定
まずはapp.jsに設定。これでbladeで呼び出せるようになります。
Vue.component('follow-button', require('./components/FollowButton.vue').default);
FollowButton.vueのコード
<template>
<div>
<div v-if="followed">
フォローされてます
</div>
<form @submit.prevent="send" class="mb-1">
#1 <input type="hidden" name="_token" v-bind:value="csrf">
<div v-if="following_check">
<button type="submit" class="btn btn-success btn-sm">Unfollow</button>
</div>
<div v-else>
<button type="submit" class="btn btn-success btn-sm">Follow</button>
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
url: '',
}
},
#2 props: ['login_user_id', 'user_id', 'csrf', 'following', 'followed'],
computed: {
// ...mapStateは使えない。https://www.it-swarm.dev/ja/vue.js/%E3%82%BB%E3%83%83%E3%82%BF%E3%83%BC%E3%82%92%E5%90%AB%E3%82%80mapstate/832061209/
// getとsetを使用して処理する
#3 following_check: {
get () { return this.$store.state.follow.following_check},
set () { this.check_follow() }
}
},
created() {
this.first_check()
},
methods: {
#4 first_check() {
if(this.following == 1) {
this.following_check = true
}
},
check_follow() {
#5 this.$store.dispatch('follow/follow_check', this.user_id)
},
send(){
#6 this.$store.dispatch('follow/follow_do', this.user_id)
this.check_follow()
}
}
}
</script>
#1, csrfをbladeからpropsを経由して受け取っている。formを発火させるために必要。
#2, blade側からの情報を受け取っている。script内で使う場合はthis.を使う。viewにはそのまま書ける。
#3, following_checkは現在ログインしているユーザーがこのユーザーをフォローしているかをチェックしている。getはどこの値を参照するか?setはどんな処理を実行してからfollowing_checkの値を決めるか?という事をやっている。
#4, first_checkは最初に画面が読み込まれた時に発火する。propsで受け取っている値によって与える真偽値を変えている。
#5, これはvuexの処理を呼び出している。フォローの状態を取得している。
#6, ボタンがクリックされた時の処理。vuexに飛ばしている。フォロー状態が変化するので、check_follow()メソッドを呼び出している。
store/index.jsのコード
モジュール化してます。
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const Follow = {
namespaced: true,
state: {
following_check: false
},
mutations: {
follow_action(state, id) {
const array = ["/user/", id, "/follow"];
const path = array.join('')
axios.post(path).then(res => {
}).catch(function(error) {
console.log(error)
})
},
follow_check(state, id) {
var array = ["/user/", id, "/follow_check"];
let url = array.join('')
axios.get(url).then(res => {
if(res.data == 1) {
state.following_check = true
} else {
state.following_check = false
}
}).catch(function(error) {
console.log(error)
})
}
},
actions: {
follow_do({commit}, id) {
commit('follow_action', id)
},
follow_check({commit}, id) {
commit('follow_check', id)
}
}
}
ここでは主にしていることはコンポーネントで呼ばれたものを非同期で実行している。actions->mutationsを通すことで非同期的に更新できる。
actionsはmutationsを呼び出し、mutationsはaxiosでコントローラー側とやり取りをする。その返り値を使ってstateの値を書き換えています。
ここまでで処理は終わりです!
ボタンをクリックして非同期で切り替わり、データベースの値も切り替わっているか確認しましょう!
学んだ事
attach,detachはtoggleでも代用出来る。
toggleとsyncの違いはsyncが配列を求めるという点での違い。
vueはモジュール化することによって呼び出し方が少し変わる。
get()はどこの値を参照するか。set()はどんな処理を実行した後に返り値を返すか。という事を処理している。
axios通信でgetの場合は引数を持たせることが出来ない。なので渡したい情報はリンクの中に含ませる必要がある。
まとめ
今までattach,detachはsyncでいけるって思っていたので、toggleでも出来る事に知ったのは大きいです。その違いも配列という事で理解しました。
次回の記事ではフォロー、フォロワーリストを横に表示してその部分とも非同期に変化するもの書いていこうかなと思います。
なので今回のコードに付け足していくイメージですね!
今回はそんな感じで
以上!