4

私は似たようなことをしようとしているので、Facebook がどのように通知システムを実装しているのか疑問に思っています。

  • FooBar があなたのステータスにコメントしました
  • Red1、Green2、Blue3 があなたの写真にコメントしました
  • MegaMan と他 5 人があなたのイベントにコメントしました

最終的には各通知にアクションが関連付けられるため、1 つのレコードに複数の通知を書き込むことはできません。また、ビューでは、単一のサブジェクトに対して特定の数の通知が存在する場合に、通知を展開可能なリストとしてレンダリングしたいと考えています。

  • FooBar があなたのステータス (アクション) にコメントしました
  • Red1、Green2、Pink5 があなたの写真にコメントしました [+]
  • MegaMan と他 3 人があなたのイベントにコメントしました [-]
    • MegaMan があなたのイベント (アクション) にコメントしました
    • ProtoMan があなたのイベント (アクション) にコメントしました
    • Bass があなたのイベント (アクション) にコメントしました
    • DrWilly があなたのイベント (アクション) にコメントしました

乾杯!

PS私はpostgresとrails BTWを使用しています。

4

3 に答える 3

4

これを実装するには、いくつかの方法があります。それは、対象とする通知の種類と、適切なユーザーに通知を表示するために通知について収集する必要がある情報に大きく依存します。投稿されたコメントに関する通知のみをカバーするシンプルなデザインを探している場合は、ポリモーフィック アソシエーションオブザーバー コールバックを組み合わせて使用​​できます。

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

ここで何が起こっているかというと、それぞれの写真、ステータス、イベントなどには多くのコメントがあります。これComment:commenter:commentable写真、ステータス、イベント、またはコメントを許可するその他のモデルのいずれかです。次にCommentObserver、モデルを監視Commentし、テーブルで何かが発生するたびに何かを行う がありCommentます。この場合、Commentが作成された後、オブザーバーはCommentNotification、コメントの id と、コメントの対象となるものを所有するユーザーの id を使用して を作成します ( 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なるか、「いいね」されたものの所有者になります。誰かが写真にコメントしたときに、写真にタグ付けされたすべての人に通知する場合のように、通知する人が複数いる場合もあります。

お役に立てれば。

于 2012-10-02T06:26:26.367 に答える
1

最初の答えがとてつもなく長くなったので、これを別の答えとして投稿することにしました。

has_one :notification例にあるように、コメント通知を展開可能なリストとしてレンダリングするには、まず、対象ユーザーへの通知を含むコメントを収集します (モデルに追加することを忘れないでくださいComment)。

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

joinshere を使用すると が生成される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を切り替えるのは簡単なことです。各ボタンとリストには、コメント グループ キーに基づく一意の ID があるため、各ボタンで正しいリストを展開できます。displayul.expand-comments-list

于 2012-10-04T16:10:53.573 に答える
0

そのため、2番目の回答が投稿される前に少し何かを作成しましたが、少し忙しすぎて作成してここに置くことができませんでした. そして、私がここで正しいことをしたかどうか、それがスケールするかどうか、または全体的にどのように機能するかについては、まだ研究中です. 私がこれを実装した方法に対するすべてのアイデア、提案、コメントを聞きたいです。ここに行きます:

そこで、最初に典型的なポリモーフィック テーブルとしてテーブルを作成しました。

# 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です。これは、特定のフィールドを配列として新しいフィールドにグループ化する集計関数です (ただし、Rails に到達すると文字列になります)。したがって、レコードを取得する方法は次のようになります。

# 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]"

そして、再び通知モデルに、基本的にそれらを配列に戻し、非一意のものを削除する次のメソッドがあります (特定の集約された通知で名前が 2 回表示されないようにするため):

# 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 %>
于 2012-10-30T11:42:19.090 に答える