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 %>