【问题标题】:Mutual relationships in rails 4Rails 4 中的相互关系
【发布时间】:2016-02-13 05:03:23
【问题描述】:

我试图让我的应用程序中的用户通过好友请求彼此成为共同的朋友,但我对这种关系的运作方式有点困惑......当一个用户创建友谊并被一个用户接受时另一方面,我希望两个用户都能看到友谊(显然)。

我想实现一个允许我执行类似于以下操作的实现:

user1 friend requests user2
user2 accepts
user1.friends now contains user2
user2.friends now contains user1

这是我到目前为止所拥有的,但我已经阅读了一些关于嵌套 has_many 的奇怪内容:通过关系

class User < ActiveRecord::Base
 has_many :friendships
 has_many :friends, :class_name => "User", :through => :friendships
end

class Friendship < ActiveRecord::Base
 has_many :users, :limit => 2
end

这是一个可行的实现吗?如果没有,我可以改变/改进什么?如果可能的话,我想避免 2 行代表一种关系。

【问题讨论】:

    标签: ruby-on-rails ruby rails-activerecord rails-models


    【解决方案1】:

    您正在寻找的是 has_and_belongs_to_many 关系,但与同一个表,有点像 Many-to-many relationship with the same model in rails? 详细描述的那样。但是,由于您希望关系是双向的(“我的朋友也是我的朋友”),您有两种选择:

    1. 使用单个连接表,其中每一行链接两个 user_id,但为每个友谊插入两行。

      # no need for extra columns on User
      class User < ActiveRecord::Base
        has_many :friendships
        has_many :friends, through: :friendships
      end
      
      # t.belongs_to :user; t.belongs_to :friend
      class Friendship < ActiveRecord::Base
        belongs_to :user
        belongs_to :friend, class_name: "User"
      end
      
      u1 = User.create!
      u2 = User.create!
      u3 = User.create!
      
      # make users 1 and 2 friends
      u1.friendships.create(friend: u2)
      u2.friendships.create(friend: u1)        
      
      # make users 2 and 3 friends
      u2.friendships.create(friend: u3)
      u3.friendships.create(friend: u2)        
      
      # and now, u1.friends returns [u1],
      # u2.friends returns [u1, u3] and
      # u3.friends returns [u2].
      
    2. 使用单一记录,但很容易找到您的朋友:

      # no need for extra columns on User
      class User < ActiveRecord::Base
        has_many :friendships_as_a, class_name: "Friendship", foreign_key: :user_a_id
        has_many :friendships_as_b, class_name: "Friendship", foreign_key: :user_b_id
      
        def friends
          User.where(id: friendships_as_a.pluck(:user_b_id) +          friendships_as_b.pluck(:user_a_id))
        end
      end
      
      # t.belongs_to :user_a; t.belongs_to :user_b
      class Friendship < ActiveRecord::Base
        belongs_to :user_a, class_name: "User"
        belongs_to :user_b, class_name: "User"
      end
      

    这不是最干净的方法,但我想你会发现当这样设置时并没有特别干净的方法(使用非规范化表)。选项 1 是一个更安全的选择。您还可以使用 SQL 视图通过自动为每个友谊生成镜像条目来达到中间立场。

    编辑:API 中的迁移和使用

    根据下面 OP 的评论,要完全使用选项 1,您需要执行以下操作:

    rails g migration CreateFriendships
    

    将该文件编辑为如下所示:

    class CreateFriendships < ActiveRecord::Migration
      create_table :friendships do |t|
        t.belongs_to :user
        t.belongs_to :friend
        t.timestamps
      end
    end
    

    创建友谊模型:

    class Friendship < ActiveRecord::Base
      belongs_to :user
      belongs_to :friend, class_name: "User"
    end
    

    然后在您的用户模型上:

    class User < ActiveRecord::Base
      # ...
    
      has_many :friendships
      has_many :friends, through: :friendships, class_name: 'User'
    
      # ...
    end
    

    在你的 API 中,说一个新的 FriendshipsController:

    class FriendshipsController < ApplicationController
      def create
        friend = User.find(params[:friend_id])
    
        User.transaction do # ensure both steps happen, or neither happen
          Friendship.create!(user: current_user, friend: friend)
          Friendship.create!(user: friend, friend: current_user)
        end
      end
    end
    

    您的路线看起来像(config/routes.rb):

    resource :friendships, only: [:create]
    

    请求如下所示:

    POST /friendships?friend_id=42
    

    然后,您可以随时参考current_user.friends 查找用户的好友。

    【讨论】:

    • 真的很有帮助,但这是针对 API 的,鉴于我正在使用设计,而 current_user 指的是当前用户,我只是有点困惑控制器的创建操作是什么样的。 . 我知道如何建立友谊,但我如何正确地将它与用户和即将成为朋友的人联系起来 -> 也不确定迁移会是什么样子
    • @Riptyde4:请参阅我对答案的编辑。我为选项 1 添加了具体的代码示例,这是我推荐的解决方案。
    • 你是圣人!谢谢,这很好地解决了问题
    【解决方案2】:

    你会使用has_many :through:

    #app/models/user.rb
    class User < ActiveRecord::Base
      has_many :friendships
      has_many :friends, through: :friendships, -> { where(status: "accepted") }
    end
    
    #app/models/friendship.rb
    class Friendship < ActiveRecord::Base
      belongs_to :user
      belongs_to :friend, class_name: "User"
    
      enum status: [:pending, :accepted]
      validates :user, uniqueness: { scope: :friend, message: "You can only add a friend once" }
    
      def decline
        self.destroy
      end
    
      def accept
        self.update status: "approved"
      end
    end
    

    上面是self-referential join,允许以下:

    @user   = User.find params[:id]
    @friend = User.find params[:friend_id]
    
    @user.friends << @friend
    

    --

    这将为用户添加一个新的friendship,其默认status 设置为pending@user.friends 关联已设置为 accepted 朋友出现在通话中。

    因此,您将能够执行以下操作:

    #config/routes.rb
    resources :users do
      resources :friendships, only: [:index, :destroy, :update], path_names: { destroy: "remove", update: "accept" }
    end
    
    #app/controllers/Frienships_controller.rb
    class FriendshipsController < ApplicationController
      def index
        @user       = User.find params[:user_id]
        @friendship = @user.friendships
      end
    
      def update
        @user       = User.find params[:user_id]
        @friendship = @user.friendships.find params[:id]
        @friendship.accept
      end
    
      def destroy
        @user       = User.find params[:user_id]
        @friendship = @user.friendships.find params[:id]
        @friendship.decline
      end
    end
    
    #app/views/friendships/index.html.erb
    <%= @friendships.pending.each do |friendship| %>
      <%= link_to "Accept", user_friendships_path(user, friendship), method: :put %>
      <%= link_to "Decline", user_friendships_path(user, friendship), method: :delete %>
    <% end %>
    

    【讨论】:

    • 我只是有点迷茫这个怎么用,create方法和迁移怎么办???
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    • 2011-06-26
    • 2020-04-18
    • 2015-09-02
    相关资源
    最近更新 更多