Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

KalebClark's avatar

Three way pivot - A guide

Well, it's not a guide yet. But I have seen this question asked several times without a definitive answer. I am also seeking a final answer for this, so I will outline it as best I can and then lets get a good thread going to provide a great guide for this.

My scenario: I have three tables with models which I would like to join together with a pivot. Users, Messages and responses.

Messages are posted and there are three possible "responses" to that message. It is very important that the response be tied to the user and the message itself. Sounds simple!

Schema

users
    user_id
    name
    ...

messages
    message_id
    subject
    ...

responses
    response_id
    name
    ...

message_response_user
    message_id
    user_id
    response_id
    

My Code so far: (several fields omitted for simplicity)

NOTE: This code does not work.


User Migration: Simple user table. Contains demographics and credentials.

...
Schema::create('users', function (Blueprint $table) {
    $table->increments('user_id');
    $table->string('name');
    $table->string('password', 60);
    $table->timestamps();
});

User Model: Contains belongsToMany for responses and messages

...
protected $table = 'users';
protected $primaryKey = 'user_id';

public function responses() {
    return $this->belongsToMany('App\Response')->withTimeStamps();
}

public function messages() {
    return $this->belongsToMany('App\Response')->withTimeStamps();
}

Message Migration: The actual message for the system.

...
Schema::create('messages', function (Blueprint $table) {
    $table->increments('message_id');
    $table->string('subject');
    $table->text('body');
    $table->timestamps();
});

Message Model: Contains belongsToMany for responses and users

...
protected $table = 'messages';
protected $primaryKey = 'message_id';

public function responses() {
    return $this->belongsToMany('App\Response')->withTimeStamps();
}

public function users() {
    return $this->belongsToMany('App\User')->withTimeStamps();
}

Response Migration: Response table AND the pivot table.

...
Schema::create('responses', function (Blueprint $table) {
    $table->increments('response_id');
    $table->string('name');
    $table->text('description');
    $table->timestamps();
});

Schema::create('message_reponse_user', function (Blueprint $table) {
    $table->integer('message_id')->unsigned()->index();
    $table->foreign('message_id')->references('message_id')->on('messages');
    
    $table->integer('user_id')->unsigned()->index();
    $table->foreign('user_id')->references('user_id')->on('users');

    $table->integer('response_id')->unsigned()->index();
    $table->foreign('response_id')->references('response_id')->on('responses');
});

Response Model: Contains belongsToMany for users and messages.

...
protected $table = 'responses';
protected $primaryKey = 'response_id'

public function users() {
    return $this->belongsToMany('App\User')->withTimeStamps();
}

public function messages() {
    return $this->belongsToMany('App\Message')->withTimeStamps();
}

So my questions are:

  1. Is this the right approach? Using a pivot table named as it is "message_response_user", and configured as it is?

  2. In many of the examples I have seen (and practice) belongsToMany works just fine, but should I be using hasMany in the inverse or anywhere, or should the belongsToMany work throughout?

  3. How do we insert for the pivot table to maintain the relation between all three tables? Should they not be foreign keys? One of the errors I get when I try to "attach" is that the FK constraint is missing because I can only (as far as I know) perform ONE attach at a time. Either user or response.


Thanks in advance. -Kaleb Clark

@JeffreyWay - Would you like to take a shot at this? =)

0 likes
1 reply

Please or to participate in this conversation.