Search code examples
laravelmodelpolymorphismeloquent-relationship

Laravel Many To Many (Polymorphic) issue


I've been trying to create a Many To Many (Polymorphic) system that saves the state of every update done by a specific user on specific Models (for instance: Company, Address, Language models) with a note field and the updated_by. The goal is to keep track of who updated the model and when, and the note field is a text field that states where in the system the model was updated.

Below is what I created, but I'm open to getting a different solution that, in the end, allows me to accomplish the goal described above.

I've created the model Update (php artisan make:model Update -m) with the migration:

/**
 * Run the migrations.
 *
 * @access public
 * @return void
 * @since
 */
public function up()
{
    Schema::create('updates', function (Blueprint $table) {
        $table->id()->comment('The record ID');
        $table->bigInteger('updatable_id')->unsigned()->comment('THe model record id');
        $table->string('updatable_type')->comment('THe model name');
        $table->string('note')->nullable()->comment('The record note');
        $table->integer('updated_by')->unsigned()->nullable()->comment('The user ID which updated the record');
        $table->timestamps();
        $table->softDeletes();
    });
}

On the model Update all the $fillable, $dates properties are standard, and the method of the Update model:

class Update extends Model
{
    /**
     * Method to morph the records
     *
     * @access public
     */
    public function updatable()
    {
        return $this->morphTo();
    }
}

After trying several ways on the different models, my difficulty is getting the relation because when I save to the updates table, it saves correctly. For instance, in the Company model: Company::where('id', 1)->with('updates')->get(); as in the model Company I have the method:

public function updates()
{
    return $this->morphToMany(Update::class, 'updatable', 'updates')->withPivot(['note', 'updated_by'])->withTimestamps();
}

Most certainly, I'm doing something wrong because when I call Company::where('id', 1)->with('updates')->get(); it throws an SQL error "Not unique table/alias".

Thanks in advance for any help.


Solution

  • The problem I see here is using morphToMany instead of morphMany.

    return $this->morphToMany(Update::class, 'updateable', 'updates'); will use the intermediate table (and alias) updates (3rd argument) instead of using the default table name updateables. Here it will clash with the table (model) updates, so it will produce the error you are receiving.

    return $this->morphMany(Update::class, 'updateable'); will use the table updates and should work with your setup.

    Do notice that morphMany does not work with collecting pivot fields (e.g. withPivot([..]), it's not an intermediate table. Only morphToMany does.