Two types of likes with Cookie

Viewed 161

I need any user to be able to like it every 24 hours

I wrote a function for this

const LIKE_HEART = 'like_heart';
const LIKE_FINGER = 'like_finger';
public static $types = [self::LIKE_HEART, self::LIKE_FINGER];

public function setLikes() {
$likes = Cookie::get($types);
$hours = 24;
    if ($likes) {
       self::where('id', $article->id)
       ->where('updated_at', '<', Carbon::now()->subHours($hours))
       ->increment($types);
}
}

But I have two fields in my database, like_heart and like_finger, these should be two types of likes. How can I rewrite my function into a method so that I can only choose one type of like out of two?

An array of the following type must be serialized in cookies:

$r = [
    '1' => [
        'like_heart' => '2021-09-28 22:02:01',
        'like_finger' => '2021-11-28 11:12:34',
    ],
    '2' => [
        'like_finger' => '2021-11-28 11:12:34',
    ],
];

where 1, 2 is the article ID. Date - the date the like was added by current users The current date is compared with the date in this cookie for a specific article, and if it is less than 24 hours old or missing, add +1 to the corresponding like in the article and add / change information in the like.

article.blade

<div class="blog-wrapper">
        <div class="container">
            <div class="article">
                <p><b>{!! $article->title !!}</b></p>
                <p><b>{!! $article->subtitle !!}</b></p>
                <picture>
                    <source srcset="{{ $article->image_list_mobile }}" media="(max-width: 576px)" alt="{{ $article->image_mobile_alt }}" title="{{ $article->image_mobile_title }}">
                    <source srcset="{{ $article->image_list }}" alt="{{ $article->image_alt }}" title="{{ $article->image_title }}">
                    <img srcset="{{ $article->image_list }}" alt="{{ $article->image_alt }}" title="{{ $article->image_title }}">
                </picture>
                <p><b>{{ date('d F Y', strtotime($article->published_at)) }}</b></p>
                <p><b>{{ $article->getTotalViews() }} Views</b></p>
                <p><b>{{ $allArticleCommentsCount }} Comments</b></p>
            </div>

            <a href="/article/{{ $article->id }}/like?type=like_heart" class="btn btn-primary">Like Heart</a>
            <a href="/article/{{ $article->id }}/like?type=like_finger" class="btn btn-primary">Like Finger</a>

            <div class="comments">
                <div class="recommend-title"><p><b>Comments ({{ $allArticleCommentsCount }})</b></p></div>
                @foreach($article_comments as $article_comment)
                    <p><b>{!! $article_comment->name !!}</b></p>
                    <p><b>{!! $article_comment->text !!}</b></p>
                    <p><b>{{ date('d F Y', strtotime($article_comment->date)) }}</b></p>
                @endforeach
            </div>

            
        </div>
    </div>

controller

public function index(Request $request, $slug)
    {
        $article = Article::where('slug', $slug)->first();

        if(!$article){
            return abort(404);
        }
        
        $viewed = Session::get('viewed_article', []);
        if (!in_array($article->id, $viewed)) {
            $article->increment('views');
            Session::push('viewed_article', $article->id);
        }

        $allArticleCommentsCount = ArticleComment::where('article_id', $article->id)->count();

        $article_comments = ArticleComment::where('article_id', $article->id)->get();

        return view('article', compact('article', 'article_comments', 'allArticleCommentsCount'));
    }

public function postLike() {
        if ($like = request('like')) {
            $articleId = request('article_id');
    
            if (User::hasLikedToday($articleId, $like)) {
                return response()
                    ->json([
                        'message' => 'You have already liked the Article #'.$articleId.' with '.$like.'.',
                    ]);
            }
    
            $cookie = User::setLikeCookie($articleId, $like);
    
            return response()
                ->json([
                    'message' => 'Liked the Article #'.$articleId.' with '.$like.'.',
                    'cookie_json' => $cookie->getValue(),
                ])
                ->withCookie($cookie);
        }
    }

Article model

class User extends Authenticatable
{
    // ...

    public static function hasLikedToday($articleId, string $type)
    {
        $articleLikesJson = \Cookie::get('article_likes', '{}');

        $articleLikes = json_decode($articleLikesJson, true);

        // Check if there are any likes for this article
        if (! array_key_exists($articleId, $articleLikes)) {
            return false;
        }

        // Check if there are any likes with the given type
        if (! array_key_exists($type, $articleLikes[$articleId])) {
            return false;
        }

        $likeDatetime = Carbon::createFromFormat('Y-m-d H:i:s', $articleLikes[$articleId][$type]);

        return ! $likeDatetime->addDay()->lt(now());
    }

    public static function setLikeCookie($articleId, string $type)
    {
        // Initialize the cookie default
        $articleLikesJson = \Cookie::get('article_likes', '[]');

        $articleLikes = json_decode($articleLikesJson, true);

        // Update the selected articles type
        $articleLikes[$articleId][$type] = today()->format('Y-m-d H:i:s');

        $articleLikesJson = json_encode($articleLikes);

        return cookie()->forever('article_likes', $articleLikesJson);
    }
}

route

Route::get('/article', function () {
    $articleLikesJson = \Cookie::get('article_likes', '{}');

    return view('article')->with([
        'articleLikesJson' => $articleLikesJson,
    ]);
});

Route::get('article/{id}/like', 'App\Http\Controllers\ArticleController@postLike');
1 Answers

First of all, you should never store any logic in the client side. A great alternative for this kind of feature would be using the Laravel Aquantances package.

https://laravel-news.com/manage-friendships-likes-and-more-with-the-acquaintances-laravel-package

Anyway, since you want to do it with cookies;

We can actually do this a lot easier than thought.

Articles.php

class User extends Authenticatable
{
    // ...

    public static function hasLikedToday($articleId, string $type)
    {
        $articleLikesJson = \Cookie::get('article_likes', '{}');

        $articleLikes = json_decode($articleLikesJson, true);

        // Check if there are any likes for this article
        if (! array_key_exists($articleId, $articleLikes)) {
            return false;
        }

        // Check if there are any likes with the given type
        if (! array_key_exists($type, $articleLikes[$articleId])) {
            return false;
        }

        $likeDatetime = Carbon::createFromFormat('Y-m-d H:i:s', $articleLikes[$articleId][$type]);

        return ! $likeDatetime->addDay()->lt(now());
    }

    public static function setLikeCookie($articleId, string $type)
    {
        // Initialize the cookie default
        $articleLikesJson = \Cookie::get('article_likes', '[]');

        $articleLikes = json_decode($articleLikesJson, true);

        // Update the selected articles type
        $articleLikes[$articleId][$type] = today()->format('Y-m-d H:i:s');

        $articleLikesJson = json_encode($articleLikes);

        return cookie()->forever('article_likes', $articleLikesJson);
    }
}

The code above will allow us to is a user has liked an article and generate the cookie we want to set.

There are a couple important things you need to care about.

  • Not forgetting to send the cookie with the response.
  • Redirecting back to a page so that the cookies take effect.

I've made a very small example:

routes/web.php

Route::get('/test', function () {
    $articleLikesJson = \Cookie::get('article_likes', '{}');

    if ($like = request('like')) {
        $articleId = request('article_id');

        if (User::hasLikedToday($articleId, $like)) {
            return redirect()->back()
                ->with([
                    'success' => 'You have already liked the Article #'.$articleId.' with '.$like.'.',
                ]);
        }

        $cookie = User::setLikeCookie($articleId, $like);

        return redirect()->back()
            ->withCookie($cookie)
            ->with([
                'success' => 'Liked the Article #'.$articleId.' with '.$like.'.',
            ]);
    }

    return view('test')->with([
        'articleLikesJson' => $articleLikesJson,
    ]);
});

resources/views/test.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">
</head>
<body>
    <div class="container">
        @if (session('success'))
            <div class="alert alert-success" role="alert">
                {{ session('success') }}
            </div>
        @endif

        <pre>{{ $articleLikesJson }}</pre>

        <div class="row">
            @foreach (range(1, 4) as $i)
                <div class="col-3">
                    <div class="card">
                        <div class="card-header">
                            Article #{{  $i }}
                        </div>
                        <div class="card-body">
                            <h5 class="card-title">Special title treatment</h5>
                            <p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
                            <a href="/test?like=heart&article_id={{ $i }}" class="btn btn-primary">
                                Like Heart
                            </a>
                            <a href="/test?like=finger&article_id={{ $i }}" class="btn btn-primary">
                                Like Finger
                            </a>
                        </div>
                        <div class="card-footer text-muted">
                            2 days ago
                        </div>
                    </div>
                </div>
            @endforeach
        </div>
    </div>

    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.min.js" integrity="sha384-+YQ4JLhjyBLPDQt//I+STsc9iw4uQqACwlvpslubQzn4u2UU2UFM80nGisd026JF" crossorigin="anonymous"></script>
</body>
</html>

Screenshot

Post Update:

1- Remove the range()

2- Update the route of these links (to where you put the logic for liking)

3- Below I've added an example for route Route::post('article/{id}/like', [SomeController::class, 'postLike'])

<a href="/article/{{ $article->id }}/like?type=heart" class="btn btn-primary">Like Heart</a>
<a href="/article/{{ $article->id }}/like?type=finger" class="btn btn-primary">Like Finger</a>
Related