14

I am facing some difficulties while developing an app on Laravel. I want to use Event and Listener to delete and rebuild the cache of an object.

Here is the code:

app\Events\CampaignEvent.php

namespace App\Events;

use Illuminate\Queue\SerializesModels;

class CampaignEvent extends Event
{
    use SerializesModels;
    public $user_id;
    public $cache_keys;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($user_id, $cache_keys)
    {
        $this->user_id = $user_id;
        $this->cache_keys = $cache_keys;
    }
}

app\Listenters\CampaignListener.php

<?php

namespace App\Listeners;

use App\Events\CampaignEvent;
use Cache;
use Log;
use App\BrandCampaign;

class CampaignListener
{

    /**
     * Handle the event.
     *
     * @param  CampaignEvent  $event
     * @return void
     */
    public function handle(CampaignEvent $event)
    {
        /**
         * Remove cache
         */
        if(is_array($event->cache_keys)){
            foreach($event->cache_keys as $index => $cache_key){
                \Cache::forget($cache_key);
                Log::debug("[CACHE] Deleted cache for: " . $cache_key);
            }
        } else {
            \Cache::forget($event->cache_keys);
            Log::debug("[CACHE] Deleted cache for: " . $event->cache_keys);
        }

        /**
         * Rebuild cache for BrandCampaigns
         */
        $campaigns = BrandCampaign::with(['influencers' => function($query){
            $query->with(['influencer' => function($query){
                $query->select('id','profile_picture');
            }])->latest();
        }])->where('user_id', $event->user_id )->latest()->get();

        $total_influencers = [];
        foreach($campaigns as $campaign){
            foreach ($campaign->influencers as $influencer) {
                if(!in_array($influencer->influencer_id, $total_influencers))
                    $total_influencers[] = $influencer->influencer_id;
            }
        }
        $total_influencers = count($total_influencers);

        $campaigns = collect($campaigns)->toArray();

        \Cache::forever('@suppliers_campaigns('.$event->user_id.')', $campaigns);
        \Cache::put('@suppliers_total_campaigns('.$event->user_id.')', $total_influencers, 10);

        Log::debug("[CACHE] Cache rebuilt successfully!");
        return $event;
    }
}

I want to cache an array "forever", but in my campaign controller, after the event is fired, when I pull the array from cache it is returning null

Thanks!

Gufran Hasan
  • 8,910
  • 7
  • 38
  • 51
  • What is your cache driver ? Are you sure your key `'@suppliers_campaigns('.$event->user_id.')'` and value `$campaigns` are not empty and correct ? Does the suppliers_total_campaigns` cache works ? – Mtxz Aug 06 '18 at 01:58
  • 1
    Can you show how do you retrieve the data in your controller? On the other hand, why you don't import Cache as `use Illuminate\Support\Facades\Cache;`? Maybe you are using different cache classes that point to different configurations. – Adrian Hernandez-Lopez Aug 16 '18 at 21:32
  • 1
    Did you register your event & listener couple in the `EventServiceProvider.php` file? – Taha Paksu Dec 20 '18 at 09:53
  • Sorry if this is too obvious, but did you verify in your log that the key referenced as having been deleted actually matches the one you were expecting? The code you presented here looks good, so I'm inclined to think your issue in in what's feeding it. Also, a few off-topic suggestions: "Cache" is aliased, so you don't need the whole namespace as Adrian suggested, what you have there is fine. You also don't need the SerializeModels trait, as there are no Models in that event. And if you type-force $event->cache_keys to an array, you can nix the if/else block and just go with the loop. – kmuenkel Feb 27 '20 at 16:05
  • 1
    This is a little unrelated, but you might want to rethink your design a little bit. Theoretically, Listeners can be asynchronous, and may be executed in separate instances of your app by a Queued Job handler, that may not even live on the same server, rendering the target cache out of scope. That may not be the case for your specific use-case, but considering the nature and purpose of decoupled Events and Listeners, this feels like an anti-pattern. – kmuenkel Feb 02 '21 at 06:52
  • 1
    have you added the event to your event service provider or enabled event discovery? https://laravel.com/docs/8.x/events#event-discovery add a log to the event just to be sure its firing properly. – Cameron Jul 02 '21 at 21:45

2 Answers2

0

Works in Laravel 5 (based on the question) & Laravel 7 (latest) as well.

use Illuminate\Support\Facades\Cache;

// Remove cache
Cache::forget('brandCampaigns');

// Rebuild cache for BrandCampaigns. Here, when the cache key doesn't exists, the function will be called and the returned value will be stored in the cache
$campaigns = Cache::rememberForever('brandCampaigns', function () {
    return BrandCampaign::with(['influencers' => function ($query) {
        $query->with(['influencer' => function ($query) {
            $query->select('id', 'profile_picture');
        }])->latest();
    }])->where('user_id', $event->user_id)->latest()->get();
});
Digvijay
  • 7,836
  • 3
  • 32
  • 53
  • How do you fire the event? – Snapey Jul 24 '22 at 07:16
  • @Snapey You [dispatch an event](https://laravel.com/docs/9.x/events#dispatching-events) using the static `dispatch` method of the event class. And while you reading the docs, read the rest please, from top to bottom .. – dbf Jan 12 '23 at 21:40
  • I was asking _their_ method of dispatch. And as for homework please look at Laracasts forum Leaderboard (only the first row) – Snapey Jan 13 '23 at 21:34
0

It is important to enable discovery in EventServiceProvider class.

-> app/Providers/EventServiceProvider.php

public function shouldDiscoverEvents()
{
    return true;
}

make sure this function return true, otherwise events and listeners don't find together.

H.Rad
  • 69
  • 6