简体   繁体   中英

Laravel ManyToMany Relationship - On Attach or Detach events

My approach to this problem may not be a good idea. I have to decided whether to use many pivot tables or just one table pivot table and filter the attach and detach events.

I have 4 models about an artist. These are; Artist, Venue, Event, Artwork. The models are there to help me identify which events, venues an artist has been to and which artwork he has exhibited. And also which artworks appear at what venues or events so that I can easily call the Venues(), Artworks(), Events(), Artists() for each model and I should get the collection. I have created a pivot table. The table contains 4 columns.

数据透视表

The pivot system works fine except as you can see in the image multiple rows are created where a single row may be appreciable. An artist may go to an event held at a venue with an artwork. So in this case you would expect a single row without any 0s. So the table above would be collapsed in about 3 rows.

THE ARTIST MODEL:

class Artist extends Model {

...

public function Artworks (){
    return $this->hasMany('App\Artwork','author_id');
}

public function Events (){
    return $this->belongsToMany('App\Event', 'exhibits', 'artist', 'event');
}

public function Venues (){
    return $this->belongsToMany('App\Venue', 'exhibits', 'artist', 'venue');
}

...
}

THE ARTWORK MODEL:

class Artwork extends Model {
...
public function Events (){
    return $this->belongsToMany('App\Event', 'exhibits', 'artwork', 'event');
}

public function Venues (){
    return $this->belongsToMany('App\Artist', 'exhibits', 'artwork', 'venue');
}
...
}

THE EVENT MODEL:

class Event extends Model {
...
public function Venue (){
    return $this->belongsTo('App\Venue', 'venue_id');
}

public function Artists (){
    return $this->belongsToMany('App\Artist', 'exhibits', 'event', 'artist');
}

public function Artworks (){
    return $this->belongsToMany('App\Artwork', 'exhibits', 'event', 'artwork');
}
...
}

THE VENUE MODEL:

class Venue extends Model {
...
public function Events (){
    return $this->hasMany('App\Event', 'venue');
}

public function Artists (){
    return $this->belongsToMany('App\Artist', 'exhibits', 'venue', 'artist');
}

public function Artworks (){
    return $this->belongsToMany('App\Artworks', 'exhibits', 'venue', 'artworks');
}
...
}

Just to clarify;

For the Artwork : I should be able to get at what events and venues the artwork has been exhibited.

For the Artist : I should be able to get what events and venues the artist has exhibit his/her artwork

For the Event : I should be able to get what artworks and artists have been at that event.

For the Venue : I should be able to get what artists, and artworks have been at the venue.

The pivot table creates multiple rows when a single row could be used. An artist can attend a venue for an event to exhibit an artwork.

EDIT: This about optimizing the table. An artwork may already be attached to an event in the table as a relationship. So now suppose I wanted to attach an artist to the same event - artwork relationship, if I was doing it manually the best way to do it would be to search for where an event has artwork but no artist and attach the artist to the event - artwork relationship instead of creating a 2 new rows for the artist to event relationship and artist to artwork relationship which Laravel is doing at the moment with attach()

You should use many pivot tables, but in your case, there only have to be two of them.

Lets write down step by step what we need and write some migrations after that:

  1. In our system, we want to store Artists
  2. Each Artist can have many Artworks
  3. An Artwork belongs to an Artist.
  4. Each Artist can visit many Events
  5. On each Event, there are many Artworks
  6. An Event is held at one Venue
  7. A Venue holds many Events

Everything bold describes our entities, everything italic describes the relationships between our entities.

Now let's design our database (the schemas of the tables are very basic):

1. Creating the artists table

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

2. Creating the artworks table

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

    // An artwork belongs to an artist.
    // Because we know this, we also know that an artist has many artworks.
    $table->integer('artist_id');

    $table->timestamps();
});

3. Creating the events table

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

    // An event is held at a venue.
    $table->integer('venue_id');

    $table->timestamps();
});

4. Creating the venues table

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

5. Many artists can visit many events

Schema::create('artist_event', function(Blueprint $table)
{
    $table->integer('artist_id');
    $table->integer('event_id');
    $table->primary(['artist_id', 'event_id']);
});

6. Many artworks are presented at many events

Schema::create('artwork_event', function(Blueprint $table)
{
    $table->integer('artwork_id');
    $table->integer('event_id');
    $table->primary(['artwork_id', 'event_id']);
});

(Not handling foreign keys in this example, but you should set them in order to speed things up.)

Our migrations are now finished and covers each requirement in our list. As you can see, we only need two pivot tables.

Let's look at the models:

Artist.php

class Artist extends Model
{

    public function artworks()
    {
        return $this->hasMany(Artwork::class);
    }

    public function events()
    {
        return $this->belongsToMany(Event::class);
    }

}

Artwork.php

class Artwork extends Model
{

    public function artist()
    {
        return $this->belongsTo(Artist::class);
    }

    public function events()
    {
        return $this->belongsToMany(Event::class);
    }

}

Event.php

class Event extends Model
{

    public function artists()
    {
        return $this->belongsToMany(Artist::class);
    }

    public function artworks()
    {
        return $this->belongsToMany(Artwork::class);
    }

    public function venue()
    {
        return $this->belongsTo(Venue::class);
    }

}

Venue.php

class Venue extends Model
{

    public function events()
    {
        return $this->hasMany(Event::class);
    }

}

We can check if everything works by inserting some dummy data in our database and using the Psy Shell to play around with our models:

$ php artisan tinker
    Psy Shell v0.6.1 (PHP 7.0.0RC3 — cli) by Justin Hileman

>>> namespace App;


>>> $artist = Artist::find(1);
=> App\Artist {#680
     id: 1,
     name: "Peter Pan",
     created_at: "2015-12-09 01:06:01",
     updated_at: "2015-12-09 01:06:01",
   }


>>> $artworks = $artist->artworks;
=> Illuminate\Database\Eloquent\Collection {#677
     all: [
       App\Artwork {#681
         id: 1,
         name: "Fancy artwork",
         artist_id: 1,
         created_at: "2015-12-09 01:06:13",
         updated_at: "2015-12-09 01:06:13",
       },
     ],
   }


>>> $events = $artist->events;
=> Illuminate\Database\Eloquent\Collection {#684
     all: [
       App\Event {#686
         id: 1,
         name: "The greatest event ever",
         venue_id: 2,
         created_at: "2015-12-09 01:05:51",
         updated_at: "2015-12-09 01:05:51",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#685
           artist_id: 1,
           event_id: 1,
         },
       },
     ],
   }


>>> $event = Event::find(1);
=> App\Event {#692
     id: 1,
     name: "The greatest event ever",
     venue_id: 2,
     created_at: "2015-12-09 01:05:51",
     updated_at: "2015-12-09 01:05:51",
   }


>>> $venue = $event->venue;
=> App\Venue {#689
     id: 2,
     name: "Venue 2",
     city: "Miami",
     created_at: "2015-12-09 01:05:30",
     updated_at: "2015-12-09 01:05:30",
   }


>>> $artworksAtEvent = $event->artworks;
=> Illuminate\Database\Eloquent\Collection {#693
     all: [
       App\Artwork {#695
         id: 1,
         name: "Fancy artwork",
         artist_id: 1,
         created_at: "2015-12-09 01:06:13",
         updated_at: "2015-12-09 01:06:13",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#694
           event_id: 1,
           artwork_id: 1,
         },
       },
     ],
   }

泰勒·奥特威尔(Taylor Otwell)拒绝添加相关事件以更改belongsToMany关系,在此处查看更多信息https://github.com/laravel/framework/pull/14988,但此程序包解决了问题https://github.com/fico7489/laravel-pivot

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM