2015-10-28 7 views

risposta

8

Buona domanda. Una tipica relazione molti-a-molti è costituita da due relazioni uno-a-molti:

Uno dei dettagli più importanti in qualsiasi implementazione è: Dove sono archiviate le informazioni sulla relazione ? La risposta a questa domanda determina in che modo è possibile accedere alle relazioni di un'entità . Esaminiamo alcune opzioni.

Premessa:

A hasMany B

B hasMany A

Opzione 1

Informazione sulla relazione sono memorizzati su istanze di A.

In questo scenario, una volta che hai un esempio di A si possono trovare i suoi associati istanze di B, perché gli ID degli associati B istanze vengono memorizzati su A. Questo significa anche che se hai solo un esempio di B, l'unico modo per trovare tutte le istanze di A che l'istanza B riferisce a sarebbe quello di ricercare tutte le istanze di A per coloro il cui b_ids campo contiene il id dell'istanza B.

Un esempio

var Player = store.defineResource({ 
    name: 'player', 
    relations: { 
    hasMany: { 
     team: { 
     // JSData will setup a "teams" property accessor on 
     // instances of player which searches the store for 
     // that player's teams 
     localField: 'teams', 
     localKeys: 'team_ids' 
     } 
    } 
    } 
}) 

var Team = store.defineResource({ 
    name: 'team', 
    relations: { 
    hasMany: { 
     player: { 
     localField: 'players', 
     // Since relationship information is stored 
     // on the player, in order to retrieve a 
     // team's players we have to do a O(n^2) 
     // search through all the player instances 
     foreignKeys: 'team_ids' 
     } 
    } 
    } 
}) 

Ora vediamo in azione:

var player = Player.inject({ 
    id: 1, 
    team_ids: [3, 4] 
}) 

// The player's teams aren't in the store yet 
player.teams // [ ] 

var player2 = Player.inject({ 
    id: 2, 
    team_ids: [4, 5], 
    teams: [ 
    { 
     id: 4 
    }, 
    { 
     id: 5 
    } 
    ] 
}) 

// See the property accessor in action 
player2.teams // [{ id: 4 }, { id: 5 }] 

// One of player one's teams is in the store now 
player.teams // [{ id: 4 }] 

// Access the relation from the reverse direction 
var team4 = Team.get(4) // { id: 4 } 

// The property accessor makes a O(n^2) search of the store because 
// the relationship information isn't stored on the team 
team4.players // [{ id: 1, team_ids: [3, 4] }, { id: 2, team_ids: [4, 5] }] 

Diamo caricare una relazione da uno strato di persistenza:

// To get an authoritative list of player one's 
// teams we ask our persistence layer. 
// Using the HTTP adapter, this might make a request like this: 
// GET /team?where={"id":{"in":[3,4]}} (this would be url encoded) 
// 
// This method call makes this call internally: 
// Team.findAll({ where: { id: { 'in': player.team_ids } } }) 
player.DSLoadRelations(['team']).then(function (player) { 

    // The adapter responded with an array of teams, which 
    // got injected into the datastore. 

    // The property accessor picks up the newly injected team3 
    player.teams // [{ id: 3 }, { id: 4 }] 

    var team3 = Team.get(3) 

    // Retrieve all of team3's players. 
    // Using the HTTP adapter, this might make a request like this: 
    // // GET /player?where={"team_ids":{"contains":3}} (this would be url encoded) 
    // 
    // This method call makes this call internally: 
    // Player.findAll({ where: { team_ids: { 'contains': team3.id } } }) 
    return team3.DSLoadRelations(['player']) 
}) 

Se si sta utilizzando l'HTTP adattatore, quindi spetta al tuo server analizzare la querystring e rispondere con la destra dati. Se si utilizza uno degli altri adattatori , l'adattatore sa già come restituire i dati corretti. L'utilizzo di JSData sul frontend e back-end rende tutto ciò troppo semplice.

Opzione 2

Informazione sulla relazione sono memorizzati su istanze di B.

Questo è solo l'inverso di opzione 1.

Opzione 3

"A hasMany B" informazioni relazione è memorizzato in casi A, e "B hasMany A" informazioni rapporto sono memorizzati su istanze di B.

Questa è solo opzione 1 tranne che ora funziona in entrambe le direzioni.

Un vantaggio di questo approccio è che è possibile accedere alla relazione da entrambe le direzioni senza la necessità di utilizzare l'opzione foreignKeys. Uno svantaggio di questo approccio è che quando le relazioni cambiano per dover modificare i dati in più posti.

Opzione 4

Informazione sulla relazione è memorizzato in un perno (uscita) tabella.

A hasMany C e C belongsTo A, dove l'attuale rapporto informazioni viene memorizzato in C.

B hasMany C e C belongsTo B, dove l'attuale rapporto informazioni viene memorizzato in C.

Un esempio:

var Player = store.defineResource({ 
    name: 'player', 
    relations: { 
    hasMany: { 
     membership: { 
     localField: 'memberships', 
     // relationship information is stored on the membership 
     foreignKey: 'player_id' 
     } 
    } 
    } 
}) 

var Team = store.defineResource({ 
    name: 'team', 
    relations: { 
    hasMany: { 
     membership: { 
     localField: 'memberships', 
     // relationship information is stored on the membership 
     foreignKey: 'team_id' 
     } 
    } 
    } 
}) 

e la risorsa pivot:

var Membership = store.defineResource({ 
    name: 'membership', 
    relations: { 
    belongsTo: { 
     player: { 
     localField: 'player', 
     // relationship information is stored on the membership 
     localKey: 'player_id' 
     }, 
     team: { 
     localField: 'team', 
     // relationship information is stored on the membership 
     localKey: 'team_id' 
     } 
    } 
    } 
}) 

Ora vediamo in azione:

var player = Player.inject({ id: 1 }) 
var player2 = Player.inject({ id: 2 }) 
var team3 = Team.inject({ id: 3 }) 
var team4 = Team.inject({ id: 4 }) 
var team4 = Team.inject({ id: 5 }) 

player.memberships // [ ] 
player2.memberships // [ ] 
team3.memberships // [ ] 
team4.memberships // [ ] 
team5.memberships // [ ] 

Avviso a questo punto non possiamo ancora accesso qualsiasi relazione

// The relationships stored in our pivot table 
var memberships = Membership.inject([ 
    { 
    id: 997, 
    player_id: 1, 
    // player one is on team three 
    team_id: 3 
    }, 
    { 
    id: 998, 
    player_id: 1, 
    // player one is also on team four 
    team_id: 4 
    }, 
    { 
    id: 999, 
    player_id: 2, 
    // team four also has player 2 
    team_id: 4 
    }, 
    { 
    id: 1000, 
    player_id: 2, 
    // player 2 is also on team 5 
    team_id: 5 
    } 
]) 

Ora abbiamo informazioni di appartenenza

player.memberships // [{ id: 997, ... }, { id: 998, ... }] 
player2.memberships // [{ id: 998, ... }, { id: 999, ... }] 
team3.memberships // [{ id: 997, ... }] 
team4.memberships // [{ id: 998, ... }, { id: 999, ... }] 
team5.memberships // [{ id: 1000, ... }] 

Ora, è un po 'goffo per inviare i dati della tabella pivot per il vostro front-end e richiedono il tuo JavaScript per ordinare attraverso i rapporti. Per questo si vorrebbe alcuni metodi di supporto:

var Player = store.defineResource({ 
    name: 'player', 
    relations: {...}, 
    computed: { 
    teams: { 
     get: function() { 
     return store.filter('membership', { 
      player_id: this.id 
     }).map(function (membership) { 
      return store.get('team', membership.team_id) 
     }) 
     } 
    } 
    }, 
    // Instance methods 
    methods: { 
    getTeams: function() { 
     return Player.getTeams(this.id) 
    } 
    } 
    // Static Class Methods 
    getTeams: function (id) { 
    return this.loadRelations(id, ['membership']).then(function (memberships) { 
     return store.findAll('team', { 
     where: { 
      id: { 
      'in': memberships.map(function (membership) { 
       return membership.team_id 
      }) 
      } 
     } 
     }) 
    }) 
    } 
}) 

Ti farò capire i metodi analoghi per la risorsa della squadra.

Se non si vuole andare alla difficoltà dei metodi di supporto, allora si potrebbe semplicemente implementare sul backend per rendere la vostra tabella pivot invisibile al frontend e rendere il vostro molti-a-molti sguardo più come l'opzione 1, 2 o 3.

link utili