1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【API】Laravelでフォロー機能を作る

Posted at

テーブル作成

フォロー・フォロワーの状態管理をするため、followsテーブルを作成します。
マイグレーションを作成します。

php artisan make:migration create_follows_table

マイグレーションファイルを以下のように編集します。

2024_02_04_005729_create_follows_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('follows', function (Blueprint $table) {
            $table->id();
            $table->foreignId('following')->constrained('users')->onDelete('cascade');
            $table->foreignId('followed')->constrained('users')->onDelete('cascade');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('follows');
    }
};

followingとfollowedの2つのカラムを追加しただけですが、ここがポイントとなります。
foreignIdを使用した外部キーなので本来は命名法則に従ったカラム名にする必要があります。
usersテーブルに紐づくので、命名法則(テーブル名_id)に従えば"user_id"とすべきです。
しかし、今回フォロー(following)とフォロワー(followed)はどちらもuserテーブルの情報が
紐付きます。となると、命名法則に従えばフォロー&フォロワーのカラム名がどちらも"user_id"となり
重複することになります。
そこで、今回はfollowingとfollowedという命名法則を無視したユニークな名前にしています。
ここで、ユニークな名前のカラム名でリレーションをするためにconstrained()が活躍します。
大抵は引数は何も書かないことが多いですが今回は引数に"users"を指定しています。
constrained()の引数にリレーションしたいテーブル名を指定することで指定したテーブルと
リレーションが可能になります。
このうよにして今回はユニークな名前のカラム名でリレーションを実現しています。

マイグレーションファイルの編集が済んだらマイグレートしましょう。

php artisan migrate

モデルの設定

まずはモデルを作成します。

php artisan make:model Follow

モデルファイルをを作成できたら、以下のように編集する。
Followモデル特に編集はしませんが、後にコントローラーで使用しますので準備しておきます。

続いて、Userモデルを編集します。
ユーザー情報を返す際に、フォロー・フォロワーの情報をリレーションするようにします。

User.php

class User extends Authenticatable 

    //省略

    //フォローしているユーザー
    public function following()
    {
        return $this->belongsToMany(User::class, 'follows','following', 'followed');
        
        
    }

    //フォローされているユーザー
    public function followed()
    {
        
        return $this->belongsToMany(User::class, 'follows','followed','following');
        
    }


}


コントローラー編集

プロフィール画面でユーザーの情報を返すコントローラーと
フォローの追加・解除のDB操作を行うコントローラーを別々に作成します。

ProfileController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;//登録ユーザーのDBを使用

class ProfileController extends Controller
{
    //
    public function get_user($user_id){

        $user = User::with('following')->with('followed')->findOrFail($user_id);
        return response()->json($user);
    }
}

FollowController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Follow;//Followモデルをインポート
use Illuminate\Support\Facades\Auth; // Authファサードを読み込む

class FollowController extends Controller
{
    //フォローしているかどうかの状態確認
    public function check_following($id){

        //自分がフォローしているかどうか検索
        $check = Follow::where('following', Auth::id() )->where('followed', $id);

        if($check->count() == 0):
            //フォローしている場合
            return response()->json(['status' => false]);
        elseif($check->count() != 0):
            //まだフォローしていない場合
            return response()->json(['status' => true]); 
        endif;
            

    }

    //フォローする(中間テーブルをインサート)
    public function following(Request $request){

        //自分がフォローしているかどうか検索
        $check = Follow::where('following', Auth::id())->where('followed', $request->user_id);

        //検索結果が0(まだフォローしていない)場合のみフォローする
        if($check->count() == 0):
            $follow = new Follow;
            $follow->following = Auth::id();
            $follow->followed = $request->user_id;
            $follow->save();
        endif;

    }


    //フォローを外す
    public function unfollowing(Request $request){

        //削除対象のレコードを検索して削除
        $unfollowing = Follow::where('following', Auth::id())->where('followed', $request->user_id)->delete();


    }
}

routeの定義

routeを定義します。下記のように追記して下さい。
また、今回はAPIでの利用という前提ですのでapi.phpに書いてますが
フロントエンドはbladeテンプレート実装したい場合は同じ内容をweb.phpに追記すればOKです。

api.php

//省略


use App\Http\Controllers\ProfileController;//追加
use App\Http\Controllers\FollowController;//追加


//省略


//プロフィール閲覧で使用するユーザー情報の取得
Route::get('/profile/{id}',[ProfileController::class,'get_user']);

//フォロー状態の確認
Route::get('/follow/status/{id}',[FollowController::class,'check_following']);

//フォロー付与
Route::post('/follow/add',[FollowController::class,'following']);

//フォロー解除
Route::post('/follow/remove',[FollowController::class,'unfollowing']);


//省略

参考:フロントエンド

最後におまけでフロントエンドの実装例です。
自分の場合は、React(TypeScript)で実装しています。
パラメータ付きURlでユーザーのidを送り、それを受け取りプロフィールを表示します。
ユーザー情報にlaravel側でリレーションしているため、フォロー数とフォロワー数を取得できます。
フォロー/フォロー解除のボタンは別ファイルでコンポーネントを作り、Profile.tsxで使用しています。

Profile.tsx
import { FC } from "react";
import axios,{AxiosRequestConfig, AxiosResponse, AxiosError} from 'axios';
import { useParams } from 'react-router-dom';
import { useState, useEffect, useContext } from "react";
import { Following } from "./Following";


export const Profile:FC =()=>{

    const { user_id } = useParams();
    const [user,setUser] = useState<any>();
    const [update,setUpdate] = useState<boolean>(false);

    useEffect(()=>{

        // パラメータ(暗号化されたid)付きでアクセスし、該当データをDBより取得
        axios.get('/api/profile/' + user_id).then((response:any) => { 

            setUser(response.data);
            console.log(response.data);

        }).catch((error) => { 
            console.log('通信エラーが発生しました');
        });
    },[]);

    return(
        <>
            <div className="w-full mt-1 mb-10 p-5 rounded-3xl bg-white text-slate-600">
                {user && (
                    <div className="w-full rounded-3xl bg-white text-slate-600">

                        
                        <div className="text-2xl">{user.name}</div>
                        <Following id={user.id} />
                        <div className="flex">
                            <div className="p-1">
                                <b>{user.following.length}</b> フォロー
                            </div>
                            <div className="p-1">
                                <b>{user.followed.length}</b> フォロワー
                            </div>
                        </div>

                       
                    </div>
                )}
            </div>
        </>
    );
}
Following.tsx
import { FC } from "react";
import axios,{AxiosRequestConfig, AxiosResponse, AxiosError} from 'axios';
import { useParams } from 'react-router-dom';
import { useState, useEffect, useContext } from "react";





export const Following:FC<{id:any}> =({id})=>{

    const [status,setStatus] = useState<boolean>();
    const [toggle,setToggle] = useState<boolean>(false);

    //フォロー状態確認
    useEffect(()=>{
        // パラメータidでアクセスし、該当データをDBより取得
        axios.get('/api/follow/status/'+ id).then((response) => { 
            console.log(response.data.status);
            setStatus(response.data.status);
            
        }).catch((error) => { 
            
        });

    },[toggle]);


    //フォロー付与
    const Add = () =>{

        // パラメータ(暗号化されたid)付きでアクセスし、該当データをDBより取得
        axios.post('/api/follow/add',{user_id:id}).then((response) => { 
        
            alert('フォローしました');
            setToggle(true);

        }).catch((error) => { 
            alert('エラー');
        });
    }

    //フォロー付与
    const Remove = () =>{

        // パラメータ(暗号化されたid)付きでアクセスし、該当データをDBより取得
        axios.post('/api/follow/remove',{user_id:id}).then((response) => { 
        
            alert('フォロー解除');
            setToggle(false);

        }).catch((error) => { 
            alert('エラー');
        });
    }

    return(
        <>
            {status ?

                <button 
                    className="bg-gray-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
                    onClick={Remove}>
                    フォロー解除
                </button>

            :


                <button 
                    className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
                    onClick={Add}>
                    フォロー
                </button>
            }

        </>
    );
}

1
1
0

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
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?