Reputation: 12082
I struggle to get this right with Laravel 5. However, in Rails, I can easily do this:
A User can be a part many Teams. We know that tables User
and Team
could not have a relation id so I wont post those schema here but instead I have a TeamUser
table:
Schema::create('team_users', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id');
$table->integer('team_id');
$table->timestamps();
});
TeamUser.php:
public function user()
{
return $this->belongsTo(User::class);
}
public function team()
{
return $this->belongsTo(Team::class);
}
Team.php
public function users()
{
return $this->hasManyThrough(User::class, TeamUser::class);
}
User.php
public function teams()
{
return $this->hasManyThrough(Team::class, TeamUser::class);
}
It seems as if laravel's doc and other tutorials I have seen are different. Im new to Laravel, again, and I need this setup.
Tinker:
$tu = App\TeamUser::create([ 'user_id' => 1, 'team_id' => 1 ]);
Illuminate\Database\Eloquent\MassAssignmentException with message 'user_id'
Expected:
$tu->users // is_array($tu) to be true
Could someone please explain what is wrong here? Thanks much.
Upvotes: 0
Views: 2488
Reputation: 7916
As far as I know, hasManyThrough()
only substitutes for chaining 2 hasMany
relationships. In your case we have to deal with a single many-to-many relationship, which is defined by using belongsToMany()
on both models involved.
For that to work you should create a join-table. In Laravel, the naming convention for join tables is the names of both models involved, singular, snake_cased, and in alphabetical order, and separated by a _
. In this case the table name would be team_user
.
You'll want to create a migration through the artisan CLI:
$ php artisan make:migration create_team_user_pivot_table
And put the contents below inside the up()
and down()
methods of the new class that was created for you in YOUR_PROJECT/database/migrations/
.
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('team_user', function (Blueprint $table) {
$table->integer('team_id')->unsigned()->index();
$table->foreign('team_id')->references('id')->on('teams')->onDelete('cascade');
$table->integer('user_id')->unsigned()->index();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->primary(['team_id', 'user_id']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('team_user');
}
When all set you can run the new migration with the following console command:
$ php artisan migrate
Then, in your Team
model:
public function users()
{
return $this->belongsToMany(User::class);
}
and in your User
model:
public function teams()
{
return $this->belongsToMany(Team::class);
}
This should be all you need to get the relations working.
Note that nothing stops you from creating an extra model called TeamUser
to access your pivot/join table directly. You do have to specify the table name in that model to be team_user
, since Eloquent assumes the table name to be the plural snake-cased version of the model name: team_users
. This is as easy as:
protected $table = 'team_user';
If the sole purpose of the extra table is storing relations, however, I can not see why defining a Model class for that table can be considered useful. Laravel comes with a bunch of extra features that let you define and access additional columns on join tables through relationships.
(scroll down a bit to Saving Additional Data On A Pivot Table)
Upvotes: 0
Reputation: 2780
What I do when working out Eloquent relationships is say them out loud:
This says that you'll need a many-to-many
relationship. So, what we do in our User.php
:
public function teams() {
return $this->belongsToMany(Team::class);
}
Then, in our Team.php
, we do this;
public function users() {
return $this->hasMany(Users::class);
}
This should automatically make a pivot table, and set you up to the point where you could request e.g. a users teams by doing: Users::find(1)->teams
. Note that i didn't use parentheses because I want the result, not the query builder object.
Upvotes: 0
Reputation: 2533
You can implement many-to-many relation with middle table by using belongsToMany
For example in Team.php:
public function users() {
return $this->belongsToMany(User::class, 'team_user', 'team_id', 'user_id');
}
In that case you don't need to create model TeamUser
Upvotes: 1
Reputation: 2254
To avoid seeing the pasted error message you would have to add the following line to your TeamUser.php class:
protected $fillable = ['user_id', 'team_id'];
Upvotes: 0