Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attributes collision using BelongsToMany + cursor() #22144

Closed
PGBI opened this issue Nov 20, 2017 · 9 comments
Closed

Attributes collision using BelongsToMany + cursor() #22144

PGBI opened this issue Nov 20, 2017 · 9 comments
Labels

Comments

@PGBI
Copy link
Contributor

PGBI commented Nov 20, 2017

  • Laravel Version: 5.5.21
  • PHP Version: 7.1
  • Database Driver & Version: mysql 5.7

Description:

When looping through a belongsToMany association using cursor(), the models aren't correctly hydrated: they get filled with values of the pivot table. If the pivot table have columns with the same name as columns in the related model table, pivot values are used to hydrate the related model.

Steps To Reproduce:

DB setup

  • users table: id, name, timestamps
  • groups table: id, name, timestamps
  • group_user table: id, name, timestamps, user_id, group_id, approved (boolean)

Relationship defined as such:

class User extends Model
{
    public function groups()
    {
        return $this->belongsToMany(Group::class)
            ->withTimestamps()
            ->withPivot('name', 'approved');
    }
}

Actual code showing bug:

$john = User::create(['name' => 'John']);
$group = Group::create(['name' => 'subscribers']);

$john->groups()->attach($group, ['name' => 'john_subscription', 'approved' => true]);

foreach ($john->groups as $group) {
    dump(get_class($group));
    dump($group->getAttributes());
}

foreach ($john->groups()->cursor() as $group) {
    dump(get_class($group));
    dump($group->getAttributes());
}

Outputs:

"App\Group"
array:4 [
  "id" => "1"
  "name" => "subscribers"
  "created_at" => "2017-11-20 11:17:51"
  "updated_at" => "2017-11-20 11:17:51"
]
// ^ using $john->groups as $group produces a correct $group model.

"App\Group"
array:7 [
  "id" => "1"
  "name" => "john_subscription" // <-- wrong, that's the pivot name
  "created_at" => "2017-11-20 11:17:52" // <-- wrong, that's the pivot creation date
  "updated_at" => "2017-11-20 11:17:52" // <-- wrong, that's the pivot update date
  "user_id" => "1" // <-- should be in pivot, not in $group attributes
  "group_id" => "1" // <-- should be in pivot, not in $group attributes
  "approved" => "1" // <-- should be in pivot, not in $group attributes
]
// ^ using $john->groups()->cursor as $group produces a corrupted $group model.

I had actually proposed a PR to fix this (#22126), but it was rejected because: "breaking change". So instead of attempting to fix it myself, I figured I would post a bug report :)

@sisve
Copy link
Contributor

sisve commented Nov 20, 2017

Related:
laravel/ideas#347
#16704
#21774
#22127

@sylouuu
Copy link

sylouuu commented Feb 16, 2018

A dirty workaround:

foreach ($john->groups()->cursor() as $group) {
    $group->id = $group->attributesToArray()['group_id'];
    dump($group->getAttributes());
}

@taylorotwell Is it possible to consider a fix for a future version?

@sylouuu
Copy link

sylouuu commented Jul 31, 2018

@iceheat I don't understand why you ref this issue in #25015 if your PR is not related to #22144?

@PGBI has already reported an issue with BelongsToMany & cursor here with the steps to reproduce.

Could you try it on a fresh install? :)

Best

@AmmarRahman
Copy link

@sylouuu My apologies for referencing this issue on PR that does not fix it. I blindly assumed that the issue you were complaining about was about hasManyThrough and cursor() since you were asking about it in a pr addressing hasManyThrough and chunk().

I had a quick look at the code for BelongsToMany. I believe I have a grasp of what is causing the issue. I will try to dedicate some time over the evenings/weekend to write a few integration tests before introducing changes because this can easily introduce breaking changes.

Cheers,

@staudenmeir
Copy link
Contributor

@PGBI Can you resubmit your PR for Laravel 5.8?

@PGBI
Copy link
Contributor Author

PGBI commented Sep 30, 2018

@staudenmeir Feel free to cherry-pick my commits and resubmit the PR. Given how my PR was rejected without the slightest start of explanation, I'm not willing to submit it again.

@driesvints driesvints added bug lts and removed lts labels Nov 22, 2018
@mpyw
Copy link
Contributor

mpyw commented Jan 30, 2019

[5.7] Fix each() on BelongsToMany relationships by staudenmeir · Pull Request #25832 · laravel/framework

This is exactly the same type of issue and has been already merged. So the cherry-picked PR must be going to be merged.

@Werelds
Copy link
Contributor

Werelds commented Sep 18, 2019

We just ran into this issue and fortunately in our case, we didn't modify related models' attributes whilst using a cursor. But should one do so, it could very well update nothing or completely unrelated records.

I don't understand why the original PR was rejected by @taylorotwell, because while it is technically a BC breaking change, the current behaviour is not only broken, it is outright dangerous and this should be considered a critical issue.

I've set up a quick and dirty example of this in a PHPUnit test case; the test case actually tests the current, broken behaviour on purpose. See https://github.com/Werelds/laravel-broken-cursor-demo

I'm going to create a fresh PR for this, because this has to be fixed. In its current implementation, one can not use cursors on at least BelongsToMany because it is inherently broken. It seems like it was fixed on some of the other relationship types, but for my own peace of mind I will test this against any relationship type that uses pivot tables.

@driesvints driesvints removed the lts label Nov 14, 2019
@driesvints
Copy link
Member

This seems to be fixed in the current release. Thanks @mpyw and @PGBI for the original pr.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants