I have the following code in a test:
$post = factory(Post::class)->create();
$first_tag = factory(Tag::class)->create();
$second_tag = factory(Tag::class)->create();
$post->tags()->attach($first_tag);
$post->tags()->attach($second_tag);
$this->assertEquals(2, $post->tags->count());
Until there everything is OK but then I add the following code under the assert line:
$post->tags()->detach($second_tag);
$this->assertEquals(1, $post->tags->count());
This second assert fails and to make it work I have to manually add a line:
$post->tags()->detach($second_tag);
$post->load('tags');
$this->assertEquals(1, $post->tags->count());
Am I missing something? Do I have to manually reload the relationship everytime I detach a model in a belongsToMany relationship? Its really frustrating having to do this. Is there some reason why this is so?
It's indeed the situation.
The detach method will remove the appropriate record out of the intermediate table; however, both models will remain in the database.
Therefore, even after detaching
the second_tag
, the $post->tags->count
isn't up-to-date and still holds the value as if the model hadn't detached.
That's why after re-loading the relation it does work.
In simpler words:
There are 2 layers: one for the relations and the other one is the database. Attach/detach functions take place on the relations layer only. While count
access the database layer which isn't aware to the modifications in the relations layer.
How to count the tags anyway?
It's not the prettiest solution but you can set a variable the its initial value would be the returned count()
value and after that maintain it by increasing/decreasing it.
$countTags = $post->tags->count();
$post->tags()->attach($first_tag);
$countTags++;
$post->tags()->attach($second_tag);
$countTags++;
$post->tags()->detach($second_tag);
$countTags--;