4

我想知道Facebook如何實現他們的通知系統,因爲我正在尋找類似的東西。通知ala Facebook(數據庫實施)

  • FooBar的人在你的狀態
  • RED1,Green2的以及Blue3你的照片發表了評論
  • 洛克人和5個人對你的活動發表了留言

我不能有多個通知寫進單個記錄,因爲最終我會有與每個通知相關的操作。另外,在視圖中,如果某個主題存在一定數量的通知,則會將通知呈現爲可展開列表。

  • FooBar的人在你的狀態(動作)
  • RED1,Green2的以及Pink5上你的照片[+]
  • 洛克人和3個人對你的活動發表了留言評論[ - ]
    • 洛克人評論您的活動(行爲)
    • ProtoMan人在你的事件(行動)
    • 低音在您的事件(行動)
    • DrWi LLY人在你的事件(行動)

乾杯!

PS我正在使用postgres和rails BTW。

回答

4

有很多方法可以執行此操作。這實際上取決於您想要涵蓋的通知類型以及您需要收集哪些通知以顯示給正確的用戶。如果你正在尋找一個簡單的設計,只是佔地約評論張貼通知,你可以使用的polymorphic associationsobserver callbacks組合:

class Photo < ActiveRecord::Base 
# or Status or Event 
    has_many :comments, :as => :commentable 
end 

class Comment < ActiveRecord::Base 
    belongs_to :commenter 
    belongs_to :commentable, :polymorphic => true # the photo, status or event 
end 

class CommentNotification < ActiveRecord::Base 
    belongs_to :comment 
    belongs_to :target_user 
end 

class CommentObserver < ActiveRecord::Observer 
    observe :comment 

    def after_create(comment) 
     ... 
     CommentNotification.create!(comment_id: comment.id, 
      target_user_id: comment.commentable.owner.id) 
     ... 
    end 
end 

這裏發生的事情是,每一張照片,狀態,事件等,有很多評論。 A Comment顯然屬於:commenter,但也是:commentable,它可以是照片,狀態,事件或任何其他您希望允許評論的模型。然後您有一個CommentObserver,它將觀察您的Comment模型,並在Comment表發生某些事情時執行某些操作。在這種情況下,創建Comment之後,觀察者將創建一個CommentNotification,其中包含評論的標識和擁有評論所關注內容的用戶的標識(comment.commentable.owner.id)。這需要你爲每個你想要評論的模型實現一個簡單的方法:owner。因此,例如,如果評論是一張照片,則所有者將是張貼照片的用戶。

這個基本的設計應該足以讓你開始,但是請注意,如果你想爲評論以外的內容創建通知,你可以在更一般的Notification模型中使用多態關聯來擴展這個設計。

class Notification < ActiveRecord::Base 
    belongs_to :notifiable, :polymorphic => true 
    belongs_to :target_user 
end 

有了這個設計,你會那麼「觀察」所有notifiables(要創建通知型號),並做一些像你after_create回調如下:

class GenericObserver < ActiveRecord::Observer 
    observe :comment, :like, :wall_post 

    def after_create(notifiable) 
     ... 
     Notification.create!(notifiable_id: notifiable.id, 
          notifiable_type: notifiable.class.name, 
          target_user_id: notifiable.user_to_notify.id) 
     ... 
    end 
end 

唯一棘手的部分在這裏是user_to_notify方法。所有型號notifiable都必須以某種方式實施,具體取決於型號。例如,wall_post.user_to_notify只是牆的所有者,或者like.user_to_notify將是「喜歡」的東西的所有者。您甚至可能需要多人通知,例如在有人對其進行評論時通知照片中標記的所有人。

希望這會有所幫助。

+3

這將爲我節省很多麻煩,所以我添加了賞金作爲讚賞的象徵,但是我不能將它分配到24小時之後。 – Duopixel

+0

@Duopixel非常感謝!讓我知道你是否需要擴展任何東西。 – cdesrosiers

+0

謝謝。我也成功了,但沒有觀察員的一部分。我現在只需要像after_create一樣。但是,我怎樣才能展示我所給出的例子。就像發佈用戶的前幾個名字一樣,發表評論? – index

1

我決定發佈這個作爲另一個答案,因爲第一個得到了可笑的長。

要渲染留言通知可擴展列表,你在你的 例子注意,首先收集有一些目標用戶通知的意見(不要忘了添加has_one :notificationComment模型)。

comments = Comment.joins(:notification).where(:notifications => { :target_user_id => current_user.id }) 

注意的是,使用joins這裏產生INNER JOIN,所以你正確地排除沒有通知(如一些通知可能已被用戶刪除)任何評論。

接下來,您要將這些評論按他們的評論分組,以便您可以爲每個評論者創建一個可擴展列表。

@comment_groups = comments.group_by { |c| "#{c.commentable_type}#{c.commentable_id}"} 

這將產生像

`{ 'Photo8' => [comment1, comment2, ...], 'Event3' => [comment1, ...], ... }` 

散列,你現在可以在您的視圖中使用。

some_page_showing_comment_notifications.html.erb

... 
<ul> 
    <% @comment_groups.each do |group, comments| %> 
    <li> 
     <%= render 'comment_group', :comments => comments, :group => group %> 
    </li> 
    <% end %> 
</ul> 
... 

_comment_group.html.erb

<div> 
    <% case comments.length %> 
    <% when 1 %> 
     <%= comments[0].commenter.name %> commented on your <%= comment[0].commentable_type.downcase %>. 
    <% when 2 %> 
     <%= comments[0].commenter.name %> and <%= comments[1].commenter.name %> commented on your <%= comment[0].commentable_type.downcase %>. 
    <% when 3 %> 
     <%= comments[0].commenter.name %>, <%= comments[1].commenter.name %> and <%= comments[2].commenter.name %> commented on your <%= comment[0].commentable_type.downcase %> 
    <% else %> 
     <%= render 'long_list_comments', :comments => comments, :group => group %> 
    <% end %> 
</div> 

_long_list_comments.html.erb

<div> 
    <%= comments[0].commenter.name %> and <%= comments.length-1 %> others commented on your <%= comments[0].commentable_type %>. 
    <%= button_tag "+", class: "expand-comments-button", id: "#{group}-button" %> 
</div> 
<%= content_tag :ul, class: "expand-comments-list", id: "#{group}-list" do %> 
    <li> 
    <% comments.each do |comment| %> 
     # render each comment in the same way as before 
    <% end %> 
    </li> 
<% end %> 

最後,它應該是一個簡單的事情,一些JavaScript添加到button.expand-comments-button來切換displayul.expand-comments-list的財產。每個按鈕和列表都有基於註釋組鍵的唯一ID,因此您可以使每個按鈕展開正確的列表。

+0

嗨!我很抱歉遲到的回覆。這將從對象本身開始(評論),但我不認爲我已經提到,但也有其他人需要通知。我其實已經做了一些事情,你能檢查一下,看看是否可以(發佈爲新答案)? – index

0

所以我在發佈第二個答案之前已經做了一些小事,但有點太忙而無法撰寫並放在這裏。如果我在這裏做了正確的事情,如果它能夠擴展或整體表現如何,我仍在研究。我想聽聽你們所有的想法,建議和評論。這裏有:

所以我首先創建表作爲典型的多態表。

# migration 
create_table :activities do |t| 
    t.references :receiver 
    t.references :user 
    t.references :notifiable 
    t.string :notifiable_type # 
    t.string :type    # Type of notification like 'comment' or 'another type' 
    ... 
end 

# user.rb 
class User < ActiveRecord::Base 
    has_many :notifications, :foreign_key => :receiver_id, :dependent => :destroy 
end 

# notification.rb 
class Notification < ActiveRecord::Base 
    belongs_to :receiver, :class_name => 'User' 
    belongs_to :user 
    belongs_to :notifiable, :polymorphic => true 

    COMMENT = 'Comment' 
    ANOTHER_TYPE = 'Another Type' 
end 

所以就是這樣。然後插入通常就像用戶做某種類型的通知一樣。所以,我發現psql的新功能是ARRAY_AGG,它是一個聚合函數,它將某個字段作爲數組(而在字符串到達​​軌道時)組合到一個新字段中。因此,如何我得到了現在的記錄是這樣的:

# notification.rb 

scope :aggregated, 
    select('type, notifiable_id, notifiable_type, 
    DATE(notifications.created_at), 
    COUNT(notifications.id) AS count, 
    ARRAY_AGG(users.name) AS user_names, 
    ARRAY_AGG(users.image) as user_images, 
    ARRAY_AGG(id) as ids'). 
    group('type, notifiable_id, notifiable_type, DATE(notifications.created_at)'). 
    order('DATE(notifications.created_at) DESC'). 
    joins(:user) 

這個輸出是這樣的:再次

type  | notifiable_id | notifiable_type | date | count | user_names      | user_images     | id 
"Comment"| 3    | "Status"  |[date]| 3  | "['first', 'second', 'third']" |"['path1', 'path2', 'path3']" |"[1, 2, 3]" 

然後在我的通知模式,我有這樣的方法,基本上只是把他們帶回到一個數組並刪除非唯一身份(這樣一個名字將不會在一定的聚合的通知顯示兩次):

# notification.rb 

def array_of_aggregated_users 
    self.user_names[1..-2].split(',').uniq 
end 

def array_of_aggregated_user_images 
    self.user_images[1..-2].split(',').uniq 
end 

然後在我看來,我有這樣的事情

# index.html.erb 
<% @aggregated_notifications.each do |agg_notif| %> 
    <% 
    all_names = agg_notif.array_of_aggregated_users 
    all_images = agg_notif.array_of_aggregated_user_images 
    %> 

    <img src="<%= all_images[0] %>" /> 

    <% if all_names.length == 1 %> 
    <%= all_names[0] %> 
    <% elsif all_names.length == 2 %> 
    <%= all_names[0] %> and <%= all_names[1] %> 
    <% elsif all_names.length == 3 %> 
    <%= all_names[0] %>, <%= all_names[1] %> and <%= all_names[2] %> 
    <% else %> 
    <%= all_names[0] %> and <%= all_names.length - 1 %> others 
    <% end %> 

    <%= agg_notif.type %> on your <%= agg_notif.notifiable_type %> 

    <% if agg_notif.count > 1 %> 
    <%= set_collapsible_link # [-/+] %> 
    <% else %> 
    <%= set_actions(ids[0]) %> 
    <% end %> 
<% end %>