Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel, many-to-many relationship to multiple models

Looking for a simple way of doing the following in Laravel 5.

Imagine these models and interface

  • Competition
  • User
  • Team
  • Participant (Interface which User and Team implements)

A competition will have a many-to-many relationship to is participants, which can be either users or teams. So I need the competition_participant pivot table to have the following columns to define the models

  • competition_id
  • participant_id
  • participant_type

But how do I write a relation on the Competition model, so it knows which model it should fetch from the database, and at the same time return a collection of mixed models or of the Interface type?

like image 425
Nikolaj Boel Jensen Avatar asked Dec 15 '22 09:12

Nikolaj Boel Jensen


1 Answers

You had some confusions about which tables should get which columns.

Your participants table needs to be a table for managing the polymorphic relations of users and teams. It receives a particpatable_id and particpatable_type as well as an id and timestamps() at the very minimum.

Then you need an additional table called competition_participant which will manage the belongs to many between competitions and your polymorphic table participants.

This will allow you to relate your participants to your competitions. This way, you can grab all participants by a competition, call the $participant->participatable attribute on it which would return either an App\User or an App\Team depending on what type the participant is.

This is all tested.

Migrations

public function up()
{
    Schema::create('competitions', function(Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });

    Schema::create('teams', function(Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });

    Schema::create('participants', function(Blueprint $table) {
        $table->increments('id');
        $table->integer('participatable_id');
        $table->string('participatable_type');
        $table->timestamps();
    });

    Schema::create('competition_participant', function(Blueprint $table) {
        $table->integer('competition_id');
        $table->integer('participant_id');
    });
}

Models

class Competition extends Model
{
    public function participants()
    {
        return $this->belongsToMany(Participant::class);
    }
}

class Participant extends Model
{
    public function participatable()
    {
        return $this->morphTo();
    }

    public function competitions()
    {
        return $this->belongsToMany(Competition::class);
    }
}

class Team extends Model
{
    public function participants()
    {
        return $this->morphMany(Participant::class, 'participatable');
    }
}

class User extends Authenticatable
{

    public function participants()
    {
        return $this->morphMany(Participant::class, 'participatable');
    }

}

Seeds

public function run()
{
    $faker = Faker\Factory::create();

    // Seed Users Table
    DB::table('users')->delete();

    $users = [];
    for($i = 0; $i < 100; $i++) {
        $users[] = [
            'name' => $faker->name,
            'email' => $faker->email,
            'password' => Hash::make($faker->password),
            'created_at' => new DateTime,
            'updated_at' => new DateTime
        ];
    }
    DB::table('users')->insert($users);


    // Seed Teams Table
    DB::table('teams')->delete();
    $teams = [];
    for($i = 0; $i < 20; $i++) {
        $teams[] = [
            'name' => 'Team ' . ucwords($faker->domainWord),
            'created_at' => new DateTime,
            'updated_at' => new DateTime
        ];
    }
    DB::table('teams')->insert($teams);


    // Seed Participants Table
    DB::table('participants')->delete();

    // Insert some of our users as participants
    $users = App\User::limit(20)->orderByRaw('rand()')->get();
    foreach($users as $user) {
        $user->participants()->create([]);
    }

    // Insert some of the teams as participants
    $teams = App\Team::limit(10)->orderByRaw('rand()')->get();
    foreach($teams as $team) {
        $team->participants()->create([]);
    }

    // Seed Competitions Table
    DB::table('competitions')->delete();

    $competitions = [];

    for($i = 0; $i < 10; $i++) {
        $competitions[] = [
            'name' => $faker->company,
            'created_at' => new DateTime,
            'updated_at' => new DateTime,
        ];
    }

    DB::table('competitions')->insert($competitions);


    // Seed Competitions Participants Relationships
    DB::table('competition_participant')->delete();

    // Sign up each participant to 3 random competitions
    $participants = App\Participant::all();
    $competitions = App\Competition::all();

    foreach($participants as $participant) {
        $participant->competitions()->sync($competitions->shuffle()->take(3));
    }
}

Usage

    $competition = App\Competition::with('participants')->has('participants')->first();

    foreach($competition->participants as $participant) {
        echo get_class($participant->participatable); // Will output either App\User or App\Team
        echo "<br />";
    }
like image 125
user1669496 Avatar answered Dec 25 '22 11:12

user1669496