laravel-pivot-events
laravel-pivot-events copied to clipboard
Eloquent sync() on many-to-many relationship does not invalidate model cache after 9.0.1
Describe the bug After upgrading to Laravel 9 on March 25, we noticed that sync() on an Eloquent Pivot was not invalidating the model cache. I confirmed that sync() was writing our desired pivot changes to the database correctly, but Eloquent queries for the pivot records would continue to return a stale cache record.
Manually clearing the relevant model cache would allow our Eloquent queries to return the expected/updated record, until we performed another sync() on the relationship which would cause Eloquent queries to return stale results again until we manually cleared the cache.
a modelCache:clear --model=App\\Models\\Report
We only use sync() with one model. Our initial workaround was to remove the Cachable trait from that Model, which resulted in our Eloquent queries returning the expected (current) records from the database.
If we restore your Cachable trait to the affected Model and instead downgrade laravel-pivot-events to 9.0.1, the sync() invalidates cache as expected and our Eloquent queries return the expected (current) records from cache.
The issue occurs with Model Caching 0.12.2 when paired with Pivot Events 9.0.4 or 9.0.2, but Pivot Events 9.0.1 is working as expected. See changes made between 9.0.1 and 9.0.2. The issue also occurs with Model Caching 0.12.0 and 0.12.1.
Eloquent Query Retrieve affected Model while eager loading many-to-many relationship.
$report = Report::with('groups')->find($id);
I have also tried without eager loading the relationship. The issue occurs with and without the eager loading.
$report = Report::find($id);
Use sync() method to update "group" relationships on Model
$groups = [
1 => [
'flag_highlight' = 1,
],
2 => [
'flag_highlight' = 0,
],
];
$report->groups()->sync($groups);
NOTE: $groups is NOT a list of ID integers. $groups also contains the intermediate pivot table values, as described in Syncing Associations section of the Laravel Eloquent Relationships documentation. Mentioning in case this less frequent use case might be related to the issue.
Here is our group relationship definition, in case anything here might be relevant to the issue.
public function groups()
{
return $this->belongsToMany('App\Models\Group', 'report_groups')
->withPivot(['flag_highlight'])
->withTrashed()
->withTimestamps()
->orderBy('group_id');
}
The existing Pivot Event tests do not cover sync() with intermediate pivot table values. Here are two potential tests for sync() with intermediate table values, based on your existing attach_array tests. Both of these tests are failing.
public function test_sync_array_intermediate_values()
{
$this->startListening();
$user = User::find(1);
$user->roles()->sync([1 => ['value' => 123], 2 => ['value' => 456]], ['value2' => 789]);
$this->assertEquals(2, \DB::table('role_user')->count());
$this->check_events(['eloquent.pivotSyncing: '.User::class, 'eloquent.pivotSynced: '.User::class]);
$this->check_variables(0, [1, 2], [1 => ['value' => 123, 'value2' => 789], 2 => ['value' => 456, 'value2' => 789]]);
$this->check_database(2, 123);
$this->check_database(2, 789, 0, 'value2');
}
public function test_polymorphic_sync_array_intermediate_values()
{
$this->startListening();
$video = Video::find(1);
$video->tags()->sync([1 => ['value' => 123], 2 => ['value' => 456]], ['value2' => 789]);
$this->assertEquals(2, \DB::table('taggables')->count());
$this->check_events(['eloquent.pivotSyncing: '.Video::class, 'eloquent.pivotSynced: '.Video::class]);
$this->check_variables(0, [1, 2], [1 => ['value' => 123, 'value2' => 789], 2 => ['value' => 456, 'value2' => 789]], 'tags');
$this->check_database(2, 123, 0, 'value', 'taggables');
$this->check_database(2, 789, 0, 'value2', 'taggables');
}
Stack Trace Not applicable
Environment
- PHP: 8.0.17
- OS: [e.g. Ubuntu 18.04]
- Laravel: 9.6.0
- Model Caching: 0.12.2 (NOTE: 0.12.0 and 0.12.1 are also broken)
- Pivot Events: 9.0.4 (NOTE: 9.0.1 is working, 9.0.2 is broken)
Additional context This issue appears to be specific to using the sync() method on a many-to-many relationship. We use attach() and detach() methods on other many-to-many relationships and are not aware of any other cache invalidation issues since upgrading to Laravel 9. This appears to be our only code that uses the sync() method. We could likely replace this sync() method with attach/detach methods to work around the issue instead of downgrading Pivot Events to 9.0.1 if you deem this as unsupported or won't fix.
I've looked through the relevant Model Caching and Pivot Events code, and am not sure what is wrong. If you can confirm my proposed tests are written correctly and should be passing, I'd be glad to spend more time looking at the Pivot Events changes between 9.0.1 and 9.0.2 with the intention of attempting to identify a fix so I can submit a PR.
Please let me know what other information I can provide.
Thank you for publishing these wonderful Model Caching and Pivot Events package!
HI @jason-klein, thank you so much for your detailed report! I will work on fixing this in the near future. For now, as you have been, I would recommend staying with 9.0.1 until I can research and fix this issue.
Same issue over here, downgrading from 9.0.4 to 9.0.1 also fixed the problem. Many thanks!
Environment
PHP: 8.0.13 OS: MacOS Monterey (M1) Laravel: 9.8.1 Model Caching: 0.12.4 Pivot Events: 9.0.4 not working, 9.0.1 working
Another problem on #6 (>=v9.0.2) is with pivot models events (using belongsToMany->using()
): Events are not fired on pivot model when sync()
are called on parent.
On our case, we define pivot model ID using a trait (specifically Snowflake library).