Automatic invalidation can be quite tricky for Many-To-Many relationships because natively, there is not a specific event that gets triggered upon attach, detach, sync or other many-to-many variations called directly on the relation method.
For many-to-many automatic cache invalidation, a package that will enable events for the many to many relationships is needed: chelout/laravel-relationship-events
Given the Laravel documentation's example on pivots, we might have the same example with User and Role.
Note: The HasBelongsToManyEvents trait is the one that will respond to BelongsToMany. Read more about Laravel Relationship Events to use the correct trait for your relationship:
For the observer to work, \Chelout\RelationshipEvents\Traits\HasRelationshipObservables must also be implemented in the parent model (in this example, it's User).
useChelout\RelationshipEvents\Concerns\HasBelongsToManyEvents;useChelout\RelationshipEvents\Traits\HasRelationshipObservables;useRennokki\QueryCache\Traits\QueryCacheable;classUserextendsModel{useHasBelongsToManyEvents;useHasRelationshipObservables;useQueryCacheable;/** * Invalidate the cache automatically * upon update in the database. * * @varbool */protectedstatic $flushCacheOnUpdate =true;/** * The roles this user has. * * @returnmixed */publicfunctionroles() {return$this->belongsToMany(Role::class); }/** * When invalidating automatically on update, you can specify * which tags to invalidate. * * @paramstring|null $relation * @param\Illuminate\Database\Eloquent\Collection|null $pivotedModels * @returnarray */publicfunctiongetCacheTagsToInvalidateOnUpdate($relation =null, $pivotedModels =null):array {// When the Many To Many relations are being attached/detached or updated,// $pivotedModels will contain the list of models that were attached or detached.// Based on the roles attached or detached,// the following tags will be invalidated:// ['user:1:roles:1', 'user:1:roles:2', ..., 'user:1:roles']if ($relation ==='roles') { $tags =array_reduce($pivotedModels->all(),function ($tags,Role $role) {return array_merge($tags, ["user:{$this->id}:roles:{$role->id}"]); }, []);returnarray_merge($tags, ["user:{$this->id}:roles", ]); }return [// ]; }}
In the example above, the flushCacheOnUpdate should be on the model from which you call the many-to-many relation. This will trigger automatic cache invalidation for the following query:
Upon triggering the ->attach() method, the cache associated with the tag user:[user_id]:roles will be flushed.
$user->roles()->attach(1);
As you might have seen, the getCacheTagsToInvalidateOnUpdate will also pass the related model on which the many-to-many relation will take action. In the above example, it will invalidate the roles of the user by using a Selective cache invalidation for specific tags.