Laravelの基本はこちらから学べます

Laravel掲示板に”いいね”機能をつけよう。多対多を使ってみよう。




こんにちは油そばです。今回は多対多を用いて、いいね機能を作成してみようと思います。

DB構造を確認

今回は上記のDBに手を加えていきます。

はい!PostUserテーブルを追加しました。
「中間テーブル」です。

いいね機能は1人のユーザーが複数のPostをいいねできて
逆もしかりです。
1つのPostに対して複数のユーザーがいます。

よって多対多の関係→中間テーブルを作成しました。

コーディング

ではまず、テーブルを作成しましょう。ターミナルで下記コマンドを打ちましょう。

php artisan make:migration post_user_table

migrationファイルの中身を記入していきましょう。

<?php

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

class PostUserTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('post_user', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id');
            $table->unsignedBigInteger('post_id');
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');

            $table->unique(['user_id', 'post_id']);
        });
    }

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

外部キーを2つもつ「post_user」テーブルを作成しました。

$table->unique(['user_id', 'post_id']);

こちらは見ての通り、1ユーザーが何度も同じ投稿をいいねすることがないようにuniqueとしています。

そしたらmigrateをしてみましょう。

php artisan migrate

いいねボタンを設置する

まずはFont AwesomeをLaravelに導入しましょう。

https://fontawesome.com/how-to-use/on-the-web/setup/using-package-managers

こちらにある「Installing the Free version of Font Awesome」npmでインストールしていきます。

ターミナルで

npm install --save @fortawesome/fontawesome-free

次にresouces/assets/sass/app.scss に以下を追記しましょう

@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import "~@fortawesome/fontawesome-free/scss/solid";

次に

npm run dev

これでFont Awesomeを使うことができるようになりました。

index.blade.phpにいいねボタンを実装していきます。

<div class="card-body">
                    <h5 class="card-title">タイトル:{{ $post->title }}</h5>
                    <p class="card-text">内容:{{ $post->body }}</p>
                    <p class="card-text">投稿者:{{ $post->user->name }}</p>
                    <a href="{{ route('posts.show', $post->id) }}" class="btn btn-primary">詳細へ</a>
                    <div class="row justify-content-center">
                        <div class="col-md-3">
                            <form action="">
                                <input type="submit" value="&#xf164;いいね" class="fas btn btn-success">
                            </form>
                        </div>
                        <div class="col-md-3">
                            <form action="">
                                <input type="submit" value="&#xf164;いいね取り消す" class="fas btn btn-danger">
                            </form>
                        </div>
                    </div>
                </div>

詳細ボタンの下に「いいね」「いいね取消」ボタンを付け加えました。
レイアウトは目を瞑ってください笑

ちなみに、inputタグの中にFontAwesomeをいれる際には少々特殊な書き方が必要となります。

classに「fas」を入れ、valueに「&#x」の後に番号を入れましょう。
以下で言うと、「f164」です。

画像のようになっていればOKです!

モデル(多対多)

以下のドキュメント「多対多」を眺めましょう。

https://readouble.com/laravel/6.x/ja/eloquent-relationships.html#many-to-many

中間テーブルなのでModelは作成しませんが、User.phpとPost.phpに追加してリレーションを記入していきましょう。

User.phpに以下を追加

public function favorites()
    {
        return $this->belongsToMany('App\Post')->withTimestamps();
    }

Post.phpに以下を追加

public function users()
    {
        return $this->belongsToMany('App\User')->withTimestamps();
    }

withTimestamps()をつけることで中間テーブルのタイムスタンプ(created_at updated_at)を自動的に保守してもらえます。

コントローラー、ルーティング

それでは「いいね」機能用のコントローラーとそれに付随してルーティングをしていきましょう。

php artisan make:controller FavoriteController --resource

リソースで作ることで雛形がすでにできましたね!

web.php に以下を追加。

Route::post('posts/{post}/favorites', 'FavoriteController@store')->name('favorites');
Route::post('posts/{post}/unfavorites', 'FavoriteController@destroy')->name('unfavorites');

今回は「いいね」の保存と削除を使用するので、storeとdestroy を追加しました。どちらもPOSTでルーティングを設定しています。

また、どの投稿にいいねをしているのかをわかりやすくするために、URLを工夫しています。

処理を書く

show.blade.phpの各「いいね」ボタン、「いいね取消し」ボタンにaction先、method、csrfを追記しましょう。

  <div class="col-md-3"> 
    <form action="{{ route('favorites', $post) }}" method="POST">
      @csrf
      <input type="submit" value="&#xf164;いいね" class="fas btn btn-success">
    </form>
  </div>
  <div class="col-md-3">
    <form action="{{ route('unfavorites', $post) }}" method="POST">
      @csrf
      <input type="submit" value="&#xf164;いいね取り消す" class="fas btn btn-danger">
    </form>

今回はいつもとは違い、$postを渡しています。こちら大変便利なので使っていきましょう。

次にFavoriteController側の実装です。(一部抜粋です)
$postを渡していたので、$postを受け取るように変更を加えています。

use App\Post;
use Auth;


public function store(Post $post)
    {
        $post->users()->attach(Auth::id());

        return redirect()->route('posts.index');
    }



public function destroy(Post $post)
    {
        $post->users()->detach(Auth::id());

        return redirect()->route('posts.index');
    }

多対多リレーションを操作時により便利なように、Eloquentはヘルパメソッドをいくつか用意しています。例としてユーザーが多くの役割を持ち、役割も多くのユーザーを持てる場合を考えてみましょう。モデルを結びつけている中間テーブルにレコードを挿入することにより、ユーザーに役割を持たせるにはattachメソッドを使います。

Laravel公式ドキュメント
https://readouble.com/laravel/6.x/ja/eloquent-relationships.html#many-to-many

attachを使用することで簡単に中間テーブルを操作することができます。
attachの逆はdetachで、中間テーブルから対応するレコードを削除することができます。

以上で「いいね」の保存の機能、「いいね」の取消し機能が実装できました。

実際にボタンを押して確かめてみましょう。
データが入ったのか確認する際には「phpMyAdmin」で確認しましょう。

※連続で「いいね」を押すとエラー画面に飛びます。
 uniqueを設定しているため。

ボタンを切り替えよう。

「いいね」がされている場合には「取消し」を出し、「いいね」をされていない場合には「いいね」を出すようにしてみましょう。

index.blade

@if($post->users()->where('user_id', Auth::id())->exists())
    <div class="col-md-3">
      <form action="{{ route('unfavorites', $post) }}" method="POST">
         @csrf
         <input type="submit" value="&#xf164;いいね取り消す" class="fas btn btn-danger">
      </form>
     </div>
@else
    <div class="col-md-3">
      <form action="{{ route('favorites', $post) }}" method="POST">
        @csrf
        <input type="submit" value="&#xf164;いいね" class="fas btn btn-success">
      </form>
     </div>
 @endif

※いいね、取消しの順番がifの関係上、前回と変更されてますのでお気をつけください。

@if($post->users()->where('user_id', Auth::id())->exists())

で場合わけをしました。exists()は存在すれば「true」、存在しなければ「false」を返します。

いいね数をカウントしよう

index.blade.phpに以下を追加しましょう。

<div class="row justify-content-center">
    <p>いいね数:{{ $post->users()->count() }}</p>
</div>

以上で「いいね」機能の実装ができました。

非同期処理などで実装できるとよりカッコよくなるので今後実装していきましょう。お疲れ様でした。




コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です