ruby-on-rails - 如何在 Rails 中建立相互的友谊

标签 ruby-on-rails ruby oop activerecord relationships

我知道之前在 Stack Overflow 上有人问过这个问题,但答案并没有以我可以解释的方式为我解决。我的一般方法受到 this 的启发。教程。

我想做的是为加好友的用户创建一个非常简单的模型,通过一条记录在两端创建同等的友谊。

在数据库级别,我只有一个“友谊”表,其中只有一个 user_id、一个 friend_id 和一个 is_pending bool 列。

在 user.rb 中,我将关系定义为:

has_many :friendships
has_many :friends, through: :friendships

在 friendship.rb 中,我将关系定义为:

belongs_to :user
belongs_to :friend, :class_name => 'User'

如果我添加好友,可以访问如下:

> a = User.first
> b = User.last
> Friendship.new(a.id, b.id)
> a.friends
=> #<User b>

这很完美,但我想要的是也能够像这样朝另一个方向前进:

> b.friends

不幸的是,按照原样定义的关系,我得到一个空集合。运行的 SQL 显示它正在搜索 user_id = b.id。我如何指定它也应该搜索 friend_id = b.id?

最佳答案

这也可以通过单个 has_many :through 关联和一些查询摆弄来实现:

# app/models/friendship.rb

class Friendship < ApplicationRecord
  belongs_to :user
  belongs_to :friend, class_name: 'User'
end

# app/models/user.rb

class User < ApplicationRecord
  has_many :friendships,
    ->(user) { FriendshipsQuery.both_ways(user_id: user.id) },
    inverse_of: :user,
    dependent: :destroy

  has_many :friends,
    ->(user) { UsersQuery.friends(user_id: user.id, scope: true) },
    through: :friendships
end

# app/queries/friendships_query.rb

module FriendshipsQuery
  extend self

  def both_ways(user_id:)
    relation.unscope(where: :user_id)
      .where(user_id: user_id)
      .or(relation.where(friend_id: user_id))
  end

  private

  def relation
    @relation ||= Friendship.all
  end
end

# app/queries/users_query.rb

module UsersQuery
  extend self

  def friends(user_id:, scope: false)
    query = relation.joins(sql(scope: scope)).where.not(id: user_id)

    query.where(friendships: { user_id: user_id })
      .or(query.where(friendships: { friend_id: user_id }))
  end

  private

  def relation
    @relation ||= User.all
  end

  def sql(scope: false)
    if scope
      <<~SQL
        OR users.id = friendships.user_id
      SQL
    else
      <<~SQL
        INNER JOIN friendships
          ON users.id = friendships.friend_id
          OR users.id = friendships.user_id
      SQL
    end
  end
end

它可能不是最简单的,但肯定是最干的。它不使用任何回调、附加记录和关联,并保持关联方法完整,包括隐式关联创建:

user.friends << new_friend

通过gist.github.com

关于ruby-on-rails - 如何在 Rails 中建立相互的友谊,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37244283/

相关文章:

ruby-on-rails - 如何配置nginx+Unicorn避免超时错误?

ruby-on-rails - Rails JOIN 模型依赖销毁

javascript - Cckeditor 仅出现在第一个字段中

ruby - Rails 3 + Devise - 登录用户无法打开页面更改密码

c# - 派生静态成员

php - 静态实例变量的析构函数方法?

python - 我可以使用该类的对象访问类变量吗

ruby-on-rails - 检查字符串是否为 base64

Ruby - 如何将字符串解析为哈希数组

c - 当 Ruby 中的 system() 调用时,GCC 无法编译