Laravel Polymorphic ManyToMany with Pivot – Sync

  eloquent, laravel, pivot, polymorphism, synchronization

I would like to use a ‘userables’ table to store user relationship to various other models, such as ‘Projects’ and ‘Actions’. The user relationship also has an attribute, such as "Owner" or "Contributor".

I’m having trouble syncing (and/or attaching and detaching) the relationships with this additional attribute, despite trying various similar posts and scrutinising the Laravel docs.

As an overall example of the intended functionality, a given user 1 could be an Owner of Project 55, and an Owner and Contributor to Project 66. (The user may also have similar relationships to various Actions.)

The userable up migration looks like:

public function up()
    {
        Schema::create('userables', function (Blueprint $table) {
            $table->foreignId('user_id');
            $table->integer('userable_id');
            $table->string('userable_type');
            $table->enum('usership', ['Owner', 'Contributor']);
        });
    }

The Projects model as an example looks like:

 public function contributors()
    {
        return $this->morphToMany('AppModelsUser', 'userable')->where('usership', 'Contributor');
    }

    public function owners()
    {
        return $this->morphToMany('AppModelsUser', 'userable')->where('usership', 'Owner');
    }

This all works and I can build forms to pull the data in from the same table, distinguishing Owners and Contributors for various Projects and/or Actions. My issue is that when I try to update using sync(), Laravel doesn’t distinguish on the additional attribute, ie it treats Owners and Contributors for a given Project as the same thing.

Example:
Project 55 may have say two Owners, ids 1 and 2, and three Contributors, ids 1, 10 and 11. ‘Userables’ would look like:

| user_id | userable_id | userable_type      | usership    |
|---------|-------------|--------------------|-------------|
| 1       | 55          | AppModelsProject | Owner       |
| 2       | 55          | AppModelsProject | Owner       |
| 1       | 55          | AppModelsProject | Contributor |
| 10      | 55          | AppModelsProject | Contributor |
| 11      | 55          | AppModelsProject | Contributor |

To update Contributors for Project 55 to users 2 and 10, I’ve tried the following:

  1. $project->owners()->sync([2,3]
| user_id | userable_id | userable_type      | usership    |
|---------|-------------|--------------------|-------------|
| 2       | 55          | AppModelsProject | Owner       |
| 10      | 55          | AppModelsProject | Owner       |

(assigns "Owner" as the ‘usership’, seemingly being the first/default value)

  1. $project->owners()->sync(2 => ['usership' => 'Contributor'], 3 => ['usership' => 'Contributor']);
| user_id | userable_id | userable_type      | usership    |
|---------|-------------|--------------------|-------------|
| 2       | 55          | AppModelsProject | Contributor |
| 10      | 55          | AppModelsProject | Contributor |

Obviously these both impact the Owner relationships on the same project which is not as desired.

I’ve also tried to detach and attach somewhat manually. But again the relationship attribute ‘usership’ is ignored. The below both remove all five rows in the example so fail on functionality before I even get to the attach…

  1. $project->contributors()->detach();
  2. $project->contributors()->detach()->where('usership', 'Contributor');

Any help is greatly appreciated!

Source: Laravel

Leave a Reply