Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

laravelでいいね機能を実装したい

解決したいこと

①いいねボタンを押した際、投稿されている全てのいいねボタンに反映されてしまう。
②最新の投稿しかいいねできない。

laravelでツイッターのような投稿アプリをつくっています。

仕様
・投稿のいいね機能を押すと、グレーから赤色にアイコンが変化する。(データがはいる)
・再度、押すと赤からグレーに戻る。(データが取り消される)

そこで、以下の問題があります。
①ボタンを押した時、全ての投稿に反映されてしまう。(投稿ごとに反映するようにしたい。)
②最新の投稿にしか、いいねできない。(最新ではない投稿のいいねボタンを押すと、データは入るものの、変化しない状況です。)

index.blade.php

@if( $reply )
<!-- データが入ってれば赤 -->          
        <a href="{{ route('unlike', ['id' => $record->id]) }}" class="btn btn-danger opacity-75 btn-sm" name='post'>
        <i class="fa fa-heart" aria-hidden="true"></i>    
        <span class="badge"></span></a>
@elseif(!$reply)
    <!-- データなければ灰色 -->                                                     
        <a href="{{ route('like', ['id' => $record->id]) }}" class="btn btn-secondary btn-sm"  name='post'>
        <i class="fa fa-heart" aria-hidden="true"></i>
        <span class="badge"></span></a>
@endif

Homecontroller

public function index(){

  $replies = new Reply;
  $reply = $replies->liked();

}

Reply.php

    public function liked(){

        $ids = record::orderBy('updated_at','ASC')->pluck('id');

        foreach($ids as $id){
            $like_exists = Like::select('likes.*')
             ->where( [  ['user_id','=',\Auth::id()], [ 'record_id','=', $id ] ])
             ->exists();
        }  

       //user_idとログイン中のユーザーID、likeを押したrecord_idと一致する投稿があり、
    // 一致するデータがlikesテーブルに存在すればtrue、なければfalse
       
        if($like_exists){
            return true;
        }else{
            return false;
        }

    }

自分で試したこと

index.balde.phpの@if( $reply )の判定を投稿ごとに実施するようにすれば、解決できると思うのですが、どのようにすれば良いのか浮かびません。(かなり初歩的な問題だと思うのですが…)

何かお知恵をお貸しいただけると幸いです。
また、自己流の汚いコードなので見づらかったら申し訳ごじません。

0

2Answer

少なくともLikeデータの取得は「投稿&ログインユーザ単位」になってると思われるので、その単位でindex.blade.phpのその部分を出力すればいいんじゃないでしょうか。
もちろんこれは「Likeテーブルにおいて、レコードIDとユーザIDの組が一意」が大前提です。

書いてるように「index.balde.phpの@if( $reply )の判定を投稿ごとに実施するようにすれば」いいと思いますが、$like_existsがforeachでループするたび上書きされてるので、最後の判定しか残ってないのでは。
$like_exists$replyも配列にして、それぞれで判定すればいいと思います。

    public function liked(){
        $ids = record::orderBy('updated_at','ASC')->pluck('id');

        $like_exists = []
        foreach($ids as $id){
            $like_exists[] = Like::select('likes.*')
             ->where( [  ['user_id','=',\Auth::id()], [ 'record_id','=', $id ] ])
             ->exists(); // existsはbool
        } 
        return $like_exists;
    }
1Like

Comments

  1. @m1527092

    Questioner

    ご回答ありがとうございます。

    $like_existsを配列にするところは、理解できました。
    しかし、$replyを配列にするところで少し困惑しております。
    ,,,
    $reply = [];
    $reply[] = $replies->liked();
    ,,,
    $replyを配列にしbladeに渡すと、
    ,,,
    array:1 [▼
    0 => array:16 [▼ ⇦値が存在するので判定はtrue(?)になる。(以下の判定に関わらず)
    0 => false
    1 => false
    ,,,

    $replyに配列がある時点でtrueの判定になってしまう(?)ので、うまいきません。
    ,,,
    $reply = $replies->liked();
    ,,,
    ↑のようにすると、
    ,,,
    array:16 [▼
    0 => false
    1 => false
    ,,,
    となり、個別に取り出して(?)判定できそうな雰囲気ではあるのですが、取り出し方も浮かびません。

    根本的に間違ってたらすいません。

likedメソッドの役割が「特定のrecodeにlikeしているか判定」だとすると、「特定のrecode」の部分が不明な状態です。
こんなイメージではないでしょうか?

HomeController.php
public function index(){
    $reply = new Reply;
}
Reply.php
public function liked($recodeId){
    // user_idとログイン中のユーザーID、likeを押したrecord_idと一致する投稿があり、
    // 一致するデータがlikesテーブルに存在すればtrue、なければfalse
}
index.blade.php
@if ($reply->liked($record->id))
<!-- Likeされている -->  
@else
<!-- Likeされていない -->
@endif
1Like

Comments

  1. @m1527092

    Questioner

    ご回答ありがとうございます。
    頂いたイメージ通りでございます。

    index.blade.phpで出力する際に@if ($reply->liked($record->id))ですと、1つの判定結果のみが全体に反映され(?)、うまくいかない状況です。
    なので、
    Hmoecontrollerで

    // 投稿の一覧を表示
    $records = \DB::table('users')
    ->join('records', 'records.user_id','=','users.id')
    ->join('profiles', 'profiles.user_id','=','users.id')
    ->select('records.*','users.*','profiles.*','records.id','records.updated_at')
    ->whereNull('records.deleted_at')
    ->orderBy('records.updated_at','DESC')
    ->get();

    $replies = new Reply;
    foreach($records as $record){
    $reply = $replies->liked($record->id);
    }
    return $reply

    として、index.blade.phpに$replyを渡し

    index.blade.phpでは

    @foreach($reply as $rep)
    @if( $rep )
    <!-- Likeされている -->
    @else
    <!-- Likeされていない -->
    @endif
    @endforeach

    としたところ、個別のボタンの出力には成功できたのですが、recordの数だけボタンが量産されてしまい、1件のみ出力できる方法がないか探しております。

    何か良い方法はないでしょうか?

    また、根本的に方向性が違っていたら、指摘いただけると幸いです。
  2. 申し訳ないですが断片的な情報しかないので、どういった状況になっているのかわかりません・・・
    肝心のlikedメソッドと、結果の$replyがどうなっているのか示してください。
  3. @m1527092

    Questioner

    情報不足で申し訳ございません。
    liked()に関しては、以下のようになっています。
    ```Reply.php
    public function liked($recordId){
    $ids = Record::orderBy('updated_at','DESC')->whereNull('deleted_at')->pluck('id');
    $like_exists = [];
    foreach($ids as $id){
    $like_exists[] = Like::where( [ ['user_id','=',\Auth::id()], [ 'record_id','=', $id ] ])->exists();
    }
    return $like_exists;
    }
    ```
    $reply($rep)の出力結果です。

    ![ライク量産.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530989/e3141c2d-051f-c11f-f04b-80ecfc976749.png)

    index.blade.phpは以下の構造になっています。
    ```
    @foreach($records as $record)
    @foreach($reply as $rep)
    @if( $rep )
    <!-- Likeされている -->
    @else
    <!-- Likeされていない -->
    @endif
    @endforeach
    @endforeach
    ```
    foreachで2回取りましているので、画像のように1つの投稿内で全てのボタンが出力されてしまうのは理解しております。
    なので、ボタンの出力自体を1つのみに限定することができれば、とりあえず解決しそうだと思った次第でございます。
    しかし、limit(1)やfirst()などは、SQLからのデータの抽出ではないから(?)使えないみたいです。

    また、ご指導いただいた通り、
    ```HomeController
    public function index(){
    $reply = new Reply;
    }
    ```

    ```index.blade.php
    @if ($reply->liked($record->id))
    ```
    のみにすると、Reply.phpの最後の$like_existsの判定のみの出力となり、うまく行きません。
    全ての判定を取り出すためにindex.blade.phpで2回foreachを使う方法を考えました。
  4. 私のイメージが伝えきれていないか、他の方法とごちゃごちゃになっているのだと思います。

    > @if ($reply->liked($record->id))ですと、1つの判定結果のみが全体に反映され(?)、うまくいかない状況です。

    likedメソッドに渡したidが使用されていないので、何も変化していない状況です。
    likedメソッドの中身を省略したのは、これを使用して解決するというヒントとして示したからです。

    やりたいことは「ログインユーザが対象の投稿にいいねしているか判断する」ですよね?

    - ログインユーザのIDは? => \Auth::id()
    - 対象の投稿のIDは? => $record->id(これをlikedメソッドの引数として渡す)
    - 判断の方法は? => Like::where( [ ['user_id','=',ログインユーザのID], [ 'record_id','=', 対象の投稿のID ] ])->exists();

    他に足りないものや、考慮できてないものはありますか?
  5. @m1527092

    Questioner

    再三、ご教示いただき誠にありがとうございました。
    再度、シンプルに考え直したところ、Reply.phpを以下のようにすることで、目的の動作が可能になりました。

    public function liked($recordId){
    $like_exists = Like::where( [ ['user_id','=',\Auth::id()], [ 'record_id','=', $recordId ] ])
           ->exists();
    return $like_exists;
    }

    根本的にメソッドの理解が欠如しており、複雑な思考に囚われておりました。
    何のためのメソットなのか、改めて考え直すことで、正しいコードに辿り着くことができました。
    この度は、要領の悪い私に、ここまで付き合って頂きまして、誠にありがとうございました。

Your answer might help someone💌