如何在书架中建立自引用多对多关系

Posted

技术标签:

【中文标题】如何在书架中建立自引用多对多关系【英文标题】:How to do a Self-Referencing Many-to-Many Relationship in Bookshelf 【发布时间】:2016-06-05 16:09:19 【问题描述】:

我正在尝试在Bookshelf.Js 中找出最清晰和“正确”的方式,即为用户模型设置多对多关系以构建存在于许多社交应用。不过,Bookshelf 中的文档让我陷入了困境。

我可以看到两种可能的方法:1) 单独使用 belongsToMany(),或 2) 将 belongsToMany()throught() 一起使用。两者都没有为我工作,但让我首先向您展示我正在为(1)做什么。我从这个模型设置开始:

const User = bookshelf.Model.extend(
  tableName: 'users',
  initialize: function() 
    this.on('creating', this.encryptPassword);
  ,
  hasTimestamps: true,
  posts: function() 
    return this.hasMany(Posts, 'author');
  ,
  comments: function() 
    return this.hasMany(Comments);
  ,
  following: function() 
    return this.belongsToMany(UserFollow, 'users_users', 'follower_id', 'user_id');
  ,
  followers: function() 
    return this.belongsToMany(UserFollow, 'users_users', 'user_id', 'follower_id');
  ,
);

为了匹配,我有一个 knex 迁移,它添加了一个 users_users 表,如下所示:

exports.up = function(knex, Promise) 
  return knex.schema.createTable('users_users', (tbl) => 
    tbl.integer('user_id').references('users.id');
    tbl.integer('follower_id').references('users.id');
    tbl.unique(['user_id', 'follower_id']);  
      // This b/c I read a few places that Bookshelf might require a composite key.
  );
;

exports.down = function(knex, Promise) 
  return knex.schema.dropTable('users_users');
;

然后我有以下测试:

it('User model can record a follower', (done) => 
    const saveUsr = (data) => 
      return User.forge().save(data, transacting: transaction);
    ;
    Promise.all([saveUsr(mockUser), saveUsr(anotherMockUser)])
      .then((results) => 
        let usr1 = results[0];
        let usr2 = results[1];
        return usr2.followers().attach(usr1.id, transacting: transaction);
      )
      .then((usrWithFollower) => 
        return usrWithFollower.fetch(
          withRelated: ['followers'],
          transacting: transaction
        );
      )
      .then((usr) => 
        console.log(JSON.stringify(usr));
        console.log('followers: ', usr.get('followers'));
        done();
      )
      .catch((err) =>  done(err); );
   );

我在这里并没有真正测试太多,因为我无法得到这个东西,工作,但JSON.stringify(user) 在最后一个承诺然后回调的结果如下:

[
   
      "id":1,
      "name":"Sally Low",
      "username":"sally",
      "email":"sally@example.org",
      "created_at":"2016-06-05T15:24:41.835Z",
      "updated_at":"2016-06-05T15:24:41.835Z",
      "password":"$2a$10$613tL/baML9obsRN4Yg7MeU/2RiH7oK/nFUwfpQ1GYuA15VUFrFZu",
      "followers": [],
      "_pivot_user_id":2,
      "_pivot_follower_id":1
   
]

如您所见,鉴于有这些 _pivot_ 道具,看起来好像在这里设置了某种关系,但我不清楚它们是什么,我不确定为什么 @987654332 @ 字段返回为空。

如果有人可以在这里直截了当或以任何方式阐明情况,我将不胜感激。

【问题讨论】:

【参考方案1】:

我想那些 UserFollow 模型是 User 别名。这不是必需的。

尝试添加Registry Plugin 并将您的User 模型写为:

const User = bookshelf.Model.extend(
  tableName: 'users',
  // ...
  following: function() 
    return this.belongsToMany('User', 'users_users', 'follower_id', 'user_id');
  ,
  followers: function() 
    return this.belongsToMany('User', 'users_users', 'user_id', 'follower_id');
  ,
);
bookshelf.model('User', User);

attach()代码可以直接使用模型,不需要使用id

usr2.followers().attach(usr1);

我制作了您的架构的最小版本并尝试了:

new User(name: 'Bia').
  fetch(withRelated: 'followers').
  then((m) => console.log(m.toJSON()););

这让我回来了:

 name: 'Bia',
   id: 3,
   followers: 
    [  id: 1, name: 'Ana', _pivot_user_id: 3, _pivot_follower_id: 1 ,
       id: 2, name: 'Mia', _pivot_user_id: 3, _pivot_follower_id: 2  ] 

【讨论】:

以上是关于如何在书架中建立自引用多对多关系的主要内容,如果未能解决你的问题,请参考以下文章

sqlalchemy中的多对多自引用关系

如何在 SQLAlchemy ORM 上实现对同一属性的自引用多对多关系?

通过联结表进行多对多自连接

多对多(自相关)特定订单实体框架

多对多自引用关系

在 Entity Framework 7 中创建自引用多对多关系