2

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');
chu
  • 35
  • 1
  • 12
  • Hi...read this thread here on stack [discussion](https://stackoverflow.com/questions/25189427/global-variable-for-all-controller-and-views) – 64Bit1990 Oct 19 '21 at 12:21
  • "How can I rewrite my function into a method so that I can only choose one type of like out of two?" seems confusing to me, as your current function only handles one type of like and you are talking about it as it handles two? – mrhn Nov 01 '21 at 00:27
  • Can you be more clear with what you are trying to achieve? Do you want each user to be able to 'heart' or 'finger' only **one** post 24h? Or 'heart' or 'finger' **each** post once in 24h? – Rory Nov 01 '21 at 04:56
  • @Rory yes, I want any user every 24 hours to be able to add a finger or a heart to each article, and that the number of likes is added to the database – chu Nov 01 '21 at 11:47
  • @Rory I added to my post how I roughly see the situation – chu Nov 01 '21 at 11:52
  • @mrhn updated post – chu Nov 01 '21 at 12:04
  • added more detailed information about what I need – chu Nov 01 '21 at 12:22
  • Is there any reason for doing this in cookies, compared to doing it in the backend only? – mrhn Nov 01 '21 at 12:25
  • @mrhn did not understand the question a little – chu Nov 01 '21 at 12:33
  • I need to use cookies – chu Nov 01 '21 at 12:35
  • Why do you need to use cookies? – mrhn Nov 01 '21 at 12:40
  • @mrhn It's more convenient for me to store all this data in cookies, I don't see any other options. is it possible to implement in cookies what I added to the post? – chu Nov 01 '21 at 12:43

1 Answers1

2

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>
Ozan Kurt
  • 3,731
  • 4
  • 18
  • 32
  • 1
    Thank you very much, I will try to do it at my place) – chu Nov 01 '21 at 13:22
  • As far as I understand, in your example, only registered users can put likes, or anyone? – chu Nov 01 '21 at 13:24
  • 1
    No, I only used the User model as a place to hold the logic. Since it's cookie based, anybody can do it. – Ozan Kurt Nov 01 '21 at 13:26
  • ok, got it, thanks) – chu Nov 01 '21 at 13:27
  • Another question, I have a separate page for each article, and in the route it looks like `/blog/{slug}` (article slug field), how can I apply this to this example? – chu Nov 01 '21 at 13:42
  • 1
    Cookies are domain based not URL, you won't have to change anything, I've also updated your other questions answer to use AJAX. So that you don't have to reload the page every time you like something. Have a look there. – Ozan Kurt Nov 01 '21 at 13:44
  • and probably the last question, 4 such buttons appeared on my page, apparently due to the fact that I specify range (1, 4) in foreach – chu Nov 01 '21 at 13:59
  • 1
    Yes, that was for my article testing, you willbe having only 1 pair on your page, since the page will contain only 1 article. – Ozan Kurt Nov 01 '21 at 14:00
  • I'm sorry, but there is still a question, I have a page article.blade.php, according to your example, instead of a test I take my article page, added routes – chu Nov 01 '21 at 14:06
  • I also have a controller where I end up using return view ('article', compact ('article')); , and now when I click like I have disagreements, I get this error Possible typo $article Did you mean $articleLikesJson? – chu Nov 01 '21 at 14:06
  • I have data like $article->title on my page – chu Nov 01 '21 at 14:07
  • 1
    SomeController is my article controller as I understand it? – chu Nov 01 '21 at 14:36
  • 1
    Yes, lets continue chat here: https://chat.meta.stackexchange.com/rooms/1616/ozans-room – Ozan Kurt Nov 01 '21 at 14:40
  • sorry, I can't write in that chat, I don't have enough reputation – chu Nov 01 '21 at 14:43
  • 1
    now it turns out that my controller does not have such a postLike method – chu Nov 01 '21 at 14:49
  • 1
    Yep, you can simply copy the content of my `Route::post('test'...` to your controller and update the parameters. – Ozan Kurt Nov 01 '21 at 14:51
  • So, I figured it out, but this is for the `post`, but for the `get` the same thing to do, what should be there? – chu Nov 01 '21 at 15:01
  • 1
    get is where you show your page, you don't do anything there. When you click on the button it loads the `/article/{id}/postLike` page, process the like logic and redirect the user back to your `/article/{slug}` – Ozan Kurt Nov 01 '21 at 15:07
  • now my get looks like this `Route::get('/article'...` and `return view('article')...` and i get the error `The GET method is not supported for this route. Supported methods: POST.` – chu Nov 01 '21 at 15:10
  • and if so `Route::get('/article/{id}/like...` and `return view('article')...` I get the same as before `Possible typo $article Did you mean $articleLikesJson?` – chu Nov 01 '21 at 15:12
  • 1
    `GET /article/{id}/like`, won't return view, it will only increment the database count, set the cookie and redirect. You can see the examples from the answer. `GET /article/{slug}` will return the view. – Ozan Kurt Nov 01 '21 at 15:15
  • i dont know i get error `The GET method is not supported for this route. Supported methods: POST.` and all this because of this `Route::post('article/{id}/like', [ArticleController::class, 'postLike'])` – chu Nov 01 '21 at 15:21
  • 1
    `Route::post` means only post `Route::get` means get, you can only `POST` with ajax. If you use normal `` tag, it must be `Route::get` – Ozan Kurt Nov 01 '21 at 15:23
  • okay, did so `Route::get('article/{id}/like', [ArticleController::class, 'postLike'])` no errors, now just go to the blank page `article/1/like?type=like_heart`, but +1 like was not added to the database – chu Nov 01 '21 at 15:30
  • 1
    Did you add the `if ($like = request('like')) { ... }` part from the answer there? – Ozan Kurt Nov 01 '21 at 15:31
  • yes, I created `public function postLike()` and added everything I needed in the controller – chu Nov 01 '21 at 15:34
  • 1
    It's better to contact from some place we can screen share. – Ozan Kurt Nov 01 '21 at 15:56
  • I added to the post all the code that I use for this task, maybe the like_heart and like_finger fields just don't reach? – chu Nov 01 '21 at 16:02
  • 1
    Can you update `postLike` in your question? – Ozan Kurt Nov 01 '21 at 16:07
  • I did not understand a bit what to update, I added all the content that was necessary to `postLike()` – chu Nov 01 '21 at 16:11
  • it is there below in the controller – chu Nov 01 '21 at 16:11
  • 1
    okay so, what you're missing is: After this line: `$cookie = User::setLikeCookie($articleId, $like)` you must update the database. – Ozan Kurt Nov 01 '21 at 16:12
  • what is the best way to do this? – chu Nov 01 '21 at 16:15
  • 1
    Just add your database query after that line and its all good. – Ozan Kurt Nov 01 '21 at 16:15
  • like so? `$articles = DB::table('articles')->get();` but nothing has changed – chu Nov 01 '21 at 16:22
  • 1
    No no, just like you did before... `Article::where('id', $id)->increment('like_'.$type);` – Ozan Kurt Nov 01 '21 at 16:32
  • nothing changes, in the same place, in theory, the message should still be displayed, but I just have an empty page coming out – chu Nov 01 '21 at 16:40
  • and `article_likes` is also not shown in the cookie – chu Nov 01 '21 at 16:59
  • 1
    https://codeshare.io/6px8AO – Ozan Kurt Nov 01 '21 at 17:11
  • now problems with parameters, does not see the User class, neither $articleId, nor $like – chu Nov 01 '21 at 17:26
  • 1
    I've updated the variable names again, have a look. Don't forget to import the User class on top so that the code finds it. – Ozan Kurt Nov 01 '21 at 21:13
  • yes, thanks a lot) the rest is super last question – chu Nov 01 '21 at 21:23
  • why the time is not fully displayed? Here, in the hasLikedToday function, I have $likeDatetime and in the end it only shows Y-m-d and H:i:s are shown as 00:00:00 – chu Nov 01 '21 at 21:23
  • 1
    You can use `now()`, instead of `today()` – Ozan Kurt Nov 01 '21 at 21:25
  • Hey are you here? – chu Nov 10 '21 at 13:40