2011-11-20 21 views
6

Tôi đang cố gắng thiết lập hệ thống nhắn tin tương tự như facebook nơi bạn có danh sách thư được sắp xếp theo cuộc hội thoại giữa hai người dùng (điều này không quan trọng đối với nhiều người nhận vào lúc này, nhưng có lẽ nếu tôi đã sử dụng một thiết kế thông minh hơn thì có thể dễ dàng triển khai trong tương lai. Tôi không nghĩ sẽ dễ dàng thêm vào những gì tôi hiện có). Tôi có một số loại làm việc, nhưng tôi không thể thực hiện một vài tính năng. Tôi khá mới để rails/lập trình web, vì vậy bất kỳ và tất cả các mẹo/gợi ý/giải pháp được nhiều đánh giá cao.Thực hiện hệ thống nhắn tin kiểu facebook, nơi người dùng có thể ở một trong hai cột khác nhau của một mô hình khác

Vì vậy, tôi có ba mô hình có liên quan: Người dùng, Cuộc trò chuyện và Tin nhắn. Người dùng có nhiều cuộc hội thoại, Cuộc hội thoại có nhiều tin nhắn và thuộc về Người dùng và Tin nhắn thuộc về Cuộc hội thoại. Được rồi, vì vậy đây là những gì các mô hình trông giống như:

User: có liên quan lĩnh vực ID: int, tên người dùng: string

class User < ActiveRecord::Base 

    has_many :conversations, :class_name => "Conversation", :finder_sql => 
      proc { 
      "SELECT * FROM conversations " + 
      "WHERE conversations.user1_id = #{id} OR conversations.user2_id = #{id} " + 
      "ORDER BY conversations.updated_at DESC" } 

thoại: có lĩnh vực liên quan: ID: int, user1_id: int, user2_id: int, user1_deleted: boolean, user2_deleted: boolean, created_at: datetime, updated_at: datetime

class Conversation < ActiveRecord::Base 
    has_many :messages 

    belongs_to :participant_one, :class_name => "User", :foreign_key => :user1_id 
    belongs_to :participant_two, :class_name => "User", :foreign_key => :user2_id 

    private 

    def self.between(user1, user2) 
     c = Conversation.arel_table 
     Conversation.where(c[:user1_id].eq(user1).and(c[:user2_id].eq(user2)).or(c[:user1_id].eq(user2).and(c[:user2_id].eq(user1)))) 

nhắn: có lĩnh vực liên quan: id: int, conversation_id: int, AUTHOR_ID: int, nội dung: văn bản, created_at: datetime, updated_at: datetime

class Message < ActiveRecord::Base 
    belongs_to :conversation, :touch => true 

Tôi không thực sự chắc chắn nếu tôi cần participant_one và participant_two, nhưng tôi sử dụng

def conversation_partner(conversation) 
    conversation.participant_one == current_user ? conversation.participant_two : conversation.participant_one 
    end 

trong một ConversationHelper để trong quan điểm, tôi có thể hiển thị các thành viên khác.

Vì vậy, điều này về cơ bản hoạt động. Nhưng một trong những biến chứng tôi có là tôi không thực sự phân biệt người dùng rất tốt trong Cuộc trò chuyện, người dùng có thể ở trong trường user1 hoặc trường user2. Vì vậy, tôi cần phải liên tục tìm kiếm người dùng trong một hoặc một trường khác, ví dụ: trong hàm finder_sql của khai báo has_many của người dùng. Ngoài ra, khi tôi tạo một thư mới, trước hết tôi tìm kiếm xem có tham số Cuộc trò chuyện hay không, nếu có, hãy xem có cuộc trò chuyện giữa hai người dùng không và sau đó tạo cuộc trò chuyện mới. (Bạn có thể gửi tin nhắn từ chỉ mục hội thoại (như trả lời) hoặc current_user có thể xem người dùng khác và nhấp vào liên kết "gửi người dùng này một tin nhắn". Bộ điều khiển messagecontroller trông như thế này và sử dụng bản thân đó. giữa phương pháp trong mô hình hội thoại:.

class MessagesController < ApplicationController 
    before_filter :get_user 
    before_filter :find_or_create_conversation, :only => [:new, :create] 

    def new 
    @message = Message.new 
    end 

    def create 
    @message = @conversation.messages.build(params[:message]) 
    @message.author_id = current_user.id 
    if @message.save 
     redirect_to user_conversation_path(current_user, @conversation), :notice => "Message sent!" 
    else 
     redirect_to @conversation 
    end 
    end 

    private 
    def get_user 
     @user = User.find(params[:user_id]) 
    end 

    def find_or_create_conversation 
     if params[:conversation_id] 
     @conversation = Conversation.find(params[:conversation_id]) 
     else 
     @conversation = Conversation.between(@user.id, current_user.id).first or @conversation = Conversation.create!(:user1_id => current_user.id, :user2_id => @user.id) 
     end 
    end 

(tuyến đường của tôi trông như thế này :)

resources :users do 
    resources :conversations, :only => [:index, :create, :show, :destroy] do 
     resources :messages, :only => [:new, :create] 
    end 
    resources :messages, :only => [:new] 
    end 

Vì vậy, bây giờ, tôi đang gặp vấn đề cố gắng để thiết lập các cờ user1_deleted hoặc user2_deleted (và tương tự nếu/khi tôi thực hiện cờ đọc/cập nhật) Vấn đề là vì cùng một Người dùng có thể có nhiều cuộc hội thoại, nhưng anh ta có thể là user1 hoặc user2, nó trở nên khó tìm anh ta. Tôi đã suy nghĩ tôi có thể làm một cái gì đó như thế này trong mô hình hội thoại:

def self.active(user) 
    Conversation.where(which_user?(user) + "_deleted = ?", false) 
end 

def self.which_user?(user) 
    :user1_id == user ? 'user1' : 'user2' 
end 

Nhưng sau đó bạn không thể chạy nó toàn bộ cuộc nói chuyện, trừ khi bạn lặp qua từng trò chuyện một người dùng một, bởi vì đôi khi ông là user1 và đôi khi anh ta là user2. Tôi có nên mương toàn bộ cách tiếp cận này và thử thiết kế mới không? Nếu vậy, liệu có ai có thể tiếp cận tốt hơn/thực hiện tốt hơn/thực sự làm việc và vẫn đáp ứng được nhu cầu tương tự không?

Đây là một câu hỏi khá dài, vì vậy tôi đánh giá cao bất cứ ai sẵn sàng lội qua tất cả điều này với tôi. Cảm ơn.

Trả lời

13

kindofgreat,

Câu hỏi này hấp dẫn tôi một chút, vì vậy tôi đã dành một vài giờ thử nghiệm, và đây là kết quả của tôi. Kết quả là một ứng dụng mà bất kỳ số lượng người dùng nào cũng có thể tham gia vào cuộc trò chuyện.

Tôi đã đi với mô hình dữ liệu có mô hình trung gian giữa UserConversation được gọi là UserConveration; đó là mô hình tham gia và lưu trữ dữ liệu về trạng thái của người dùng và cuộc hội thoại với nhau (cụ thể là liệu cuộc hội thoại có được đọc, đã xóa hay không, v.v.)

Thực hiện trên GitHub và bạn có thể thấy mã khác Tôi đã viết (so với mã được tự động tạo ra, để giữ cho tất cả các tàu ra ngoài) tại https://github.com/BinaryMuse/so_association_expirement/compare/53f2263...master.

Dưới đây là mô hình của tôi, lột xuống để chỉ các hiệp hội:

class User < ActiveRecord::Base 
    has_many :user_conversations 
    has_many :conversations, :through => :user_conversations 
    has_many :messages, :through => :conversations 
end 

class UserConversation < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :conversation 
    has_many :messages, :through => :conversation 

    delegate :subject, :to => :conversation 
    delegate :users, :to => :conversation 
end 

class Conversation < ActiveRecord::Base 
    has_many :user_conversations 
    has_many :users, :through => :user_conversations 
    has_many :messages 
end 

class Message < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :conversation 
end 

Và đây là những gì các cơ sở dữ liệu trông giống như:

create_table "conversations", :force => true do |t| 
    t.string "subject" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
end 

create_table "messages", :force => true do |t| 
    t.integer "user_id" 
    t.integer "conversation_id" 
    t.text  "body" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
end 

create_table "user_conversations", :force => true do |t| 
    t.integer "user_id" 
    t.integer "conversation_id" 
    t.boolean "deleted" 
    t.boolean "read" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
end 

create_table "users", :force => true do |t| 
    t.string "name" 
    t.datetime "created_at" 
    t.datetime "updated_at" 
end 

Ý tưởng cơ bản là trình bày cho người dùng một "cuộc đối thoại , "khi thực tế đằng sau hậu trường, chúng tôi đang quản lý UserConversation giây cho tất cả người dùng tham gia vào cuộc trò chuyện. Xem đặc biệt là phương pháp create_user_conversations trên mô hình UserConversation, chịu trách nhiệm tạo một mục nhập trong bảng kết nối cho mọi người dùng được liên kết với cuộc trò chuyện.

Ngoài ra còn có rất nhiều các cuộc gọi has_many :throughdelegates gọi trong các mô hình để làm cho dữ liệu trở nên dễ dàng nhất có thể để lấy dữ liệu chúng tôi muốn ... ví dụ: thay vì @user_conversation.conversation.subject bạn có thể sử dụng @user_conversation.subject; cũng vậy với thuộc tính messages.

Tôi nhận ra có khá nhiều mã, vì vậy tôi khuyến khích bạn lấy nguồn và chơi với nó. Tất cả đều hoạt động, ngoại trừ "xóa" các cuộc hội thoại (tôi không bận tâm đến điều này, nhưng đánh dấu các tin nhắn là đã đọc/chưa đọc). Lưu ý rằng bạn phải "đăng nhập" với tư cách là người dùng để thực hiện một số thao tác, ví dụ: tạo cuộc trò chuyện mới, v.v. Bạn có thể đăng nhập bằng cách nhấp vào người dùng từ trang chính và chọn "Đăng nhập với tư cách người dùng này".

Một điều khác cần lưu ý là các URL nói "trò chuyện" để giữ mọi thứ tốt đẹp cho người dùng, mặc dù bộ điều khiển đang sử dụng là bộ điều khiển "UserConversations" - hãy xem tệp tuyến đường.

Nếu bạn có bất kỳ câu hỏi nào dài hơn/chuyên sâu, hãy liên hệ với tôi qua GitHub hoặc qua chi tiết liên hệ trên hồ sơ StackOverflow của tôi.

+0

Hey Brandon, nhiều nghĩa vụ cho một câu trả lời hào phóng như vậy. Tôi sẽ nghiên cứu kỹ vào ngày mai. Thật thú vị, khi tôi làm việc này, tôi đã nghĩ cả việc tạo bản sao của Cuộc hội thoại cũng như sử dụng một số loại proxy giữa người dùng và cuộc trò chuyện, nhưng tôi không thể ghép các mảnh lại với nhau. Nếu tôi có bất kỳ câu hỏi nào sau khi tôi làm việc, tôi sẽ cho bạn biết. Cảm ơn một lần nữa! – kindofgreat

+0

Xin chào Brandon, bạn có thể thêm các thuộc tính có thể truy cập vào các mô hình không? Nó sẽ giúp ích rất nhiều trong việc học bảo mật phần mềm tốt. –

+0

Michelle, tôi yêu bạn! Cảm ơn vì điều này và mã github! – kpaul

Các vấn đề liên quan