【问题标题】:Many-to-Many relational support in JSDataJSData 中的多对多关系支持
【发布时间】:2015-10-28 17:40:43
【问题描述】:

有没有办法在JSData中定义多对多关系?

例如,我有这 3 个表:

实体 实体文件 文件

在“实体”上,我想要一个名为“文件”的关系,它通过 entityFile 进行连接。

【问题讨论】:

    标签: jsdata


    【解决方案1】:

    好问题。典型的多对多关系只是两个一对多关系:

    任何实现中更重要的细节之一是: 存储关系信息? 这个问题的答案决定了如何 可以访问实体的关系。让我们探索几个选项。

    前提:

    AhasMany B

    BhasMany A

    选项 1

    关系信息存储在A 的实例上。

    在这种情况下,一旦你有一个 A 的实例,你就可以找到它的关联 B 的实例,因为关联的 B 实例的 ID 存储在 A。这也意味着如果你只有一个 B 的实例,唯一的方法是 找到与B 实例相关的所有A 实例将是 在A 的所有实例中搜索b_ids 字段包含 idB 实例。

    一个例子

    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'
          }
        }
      }
    })
    

    现在让我们看看它的实际效果:

    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] }]
    

    让我们从持久层加载一个关系:

    // 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'])
    })
    

    如果您使用的是 HTTP 适配器,则由您的服务器来解析 查询字符串并使用正确的数据进行响应。如果您使用任何一种 其他适配器然后适配器已经知道如何返回正确的数据。 在前端 后端使用 JSData 让这变得太容易了。

    选项 2

    关系信息存储在B 的实例上。

    这与选项 1 正好相反。

    选项 3

    A hasMany B”关系信息存储在实例A上,并且 “B hasMany A”关系信息存储在B的实例上。

    这只是选项 1,只不过它现在可以双向使用。

    这种方法的一个优点是您可以从两者访问关系 不需要使用foreignKeys 选项的方向。一个缺点 这种方法是当关系发生变化时必须修改数据 多个地方。

    选项 4

    关系信息存储在枢轴(联结)表中。

    A有很多CC属于A,其中实际关系 信息存储在C

    B有很多CC属于B,其中实际关系 信息存储在C

    一个例子:

    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'
          }
        }
      }
    })
    

    以及枢轴资源:

    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'
          }
        }
      }
    })
    

    现在让我们看看它的实际效果:

    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 // [ ]
    

    请注意,此时我们还不能访问任何关系

    // 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
      }
    ])
    

    现在我们有了会员信息

    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, ... }]
    

    现在,将数据透视表数据发送到前端和 要求您的 JavaScript 对关系进行排序。为此你会想要 一些辅助方法:

    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
                })
              }
            }
          })
        })
      }
    })
    

    我会让你弄清楚 Team 资源的类似方法。

    如果您不想麻烦使用辅助方法,那么您可以 只需在后端实现它们,使您的数据透视表对 前端并使您的多对多关系看起来更像选项 1、2 或 3.

    有用的链接

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-21
      • 1970-01-01
      • 2017-08-13
      • 2018-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多