epocsquadron
epocsquadron

Reputation: 303

Laravel 4 Eloquent ORM: How to update a pivot table entry that has composite primary keys

I have a many-to-many relationship like so:

user
 - user_id

user_blocks
 - blocker_user_id
 - blocked_user_id
 - unblocked (Timestamp)

Where blocker and blocked_user_id are a composite primary key, both existing as a foreign key to the user table's user_id. Both tables are InnoDB.

When I attempt to do the following:

$user = User::find(1)
$blocked_user = $user->blocked()->where('blocked_user_id', 2)->first();

$blocked_user->unblocked = User::freshTimestamp();
$blocked_user->save();

It seems to think I wish to update a column in the user table, rather than the user_blocks table. This is simply an sql error where the user table is attempting to be updated with the non-existent column unblocked.

I've tried adding ->pivot and ->pivot() to the assignment for $blocked_user in the example. The first gives me an error in Eloquent\Model::performUpdate, Call to a member function update() on a non-object and the latter gives me call_user_func_array() expects parameter 1 to be a valid callback, class 'Illuminate\Database\Query\Builder' does not have a method 'pivot' in Eloquent\Builder::__call.

The model for the user table defines the relationship to itself as:

public function blocked() {
    return $this->belongsToMany(
        'User', 
        'user_blocks', 
        'blocker_user_id', 
        'blocked_user_id'
    )->withPivot('unblocked');
}

There is no model for the user_blocks table as I do not know how to define an Eloquent model with a composite primary key (I've searched everywhere and that doesn't seem to be a question anyone has asked).

I realize the schema is less than optimized for Eloquent, but this is grafting eloquent onto a Codeigniter project with a pre-established schema. An otherwise pleasant experience over using Codeigniter models.

Upvotes: 2

Views: 2725

Answers (1)

epocsquadron
epocsquadron

Reputation: 303

It seems that Eloquent currently doesn't handle composite primary keys explicitly. However, I was able to solve the problem by making a model of the user_blocks table and specifying only one of the primary keys:

class Blocker extends Model {

    protected $table = "user_blocks";
    protected $primaryKey = "blocker_user_id";
    public $timestamps = false; // Don't track nonexistent created_at/updated_at columns

}

Then, in the logic to update the unblocked column I simply did the following:

$blocked_user = Blocker::where('blocker_user_id', $user->user_id)
    ->where('blocked_user_id', $user_to_be_unblocked_id)
    ->first();

$blocked_user->unblocked = Blocker::freshTimestamp();

$blocked_user->save();

It isn't the most elegant way to solve the issue, but it does work until Eloquent gains support for composite primary keys.

Upvotes: 4

Related Questions