1

我想为现有的卡片模型创建一个“时间线”功能。卡片已经有_many Notes和has_many Attachments。我希望能够:

  • 在一个统一的集合中访问笔记、附件(以及最终的其他模型),使用一种不错的方法,例如:card.timeline
  • 仍然可以访问卡片的注释和附件,例如:card.notes
  • 仍然可以访问笔记的父卡,例如:note.card
  • 能够将项目添加到卡片的时间线,API 如下:card.timeline << note

我想我的数据库设置正确,这是我似乎无法正确的关联声明。这是我的架构:

  create_table "cards", :force => true do |t|
    t.string   "name"
  end

  create_table "timeline_items", :force => true do |t|
    t.integer  "card_id",    :null => false # FK from cards table
    t.integer  "item_id",    :null => false # FK from notes or attachments table
    t.string   "item_type",  :null => false # either 'Note' or 'Attachment'
  end

  create_table "notes", :force => true do |t|
    t.text     "content"
  end

  create_table "attachments", :force => true do |t|
    t.string   "file_file_name"
  end

任何人都知道我如何使用 ActiveRecord 来实现这一点?它让我胡思乱想!

一个起点是:

class Card < ActiveRecord::Base
  has_many    :timeline_items
  has_many    :notes,       :through => :timeline_items, :source => :item, :source_type => 'Note', :order => 'updated_at DESC'
  has_many    :attachments, :through => :timeline_items, :source => :item, :source_type => 'Attachment', :order => 'updated_at DESC'
end

class TimelineItem < ActiveRecord::Base
  belongs_to :card
  belongs_to :item, :polymorphic => true
end

class Note < ActiveRecord::Base
  has_one     :card, :through => :timeline_items
  has_one     :timeline_item, :as => :item
end

提前谢谢~Stu

4

1 回答 1

0

好的 - 在为此苦苦挣扎之后,我在发布到 stackoverflow 后的 10 分钟内破解了它!典型的。

为了避免其他人将头撞到墙上,这就是我的错误:

注意应该是:

class Note < ActiveRecord::Base
  has_one     :card, :through => :timeline_item #not timeline_items
  has_one     :timeline_item, :as => :item
end

就是这样!我试图使用本文中使用的创建方法,但实际上这不是必需的。

这是控制台输出,显示 sql 语句都在使用timeline_items 表:

1.9.2-p290 :009 > c = Card.find(547)
  Card Load (0.3ms)  SELECT `cards`.* FROM `cards` WHERE `cards`.`id` = 547 LIMIT 1
 => #<Card id: 547, name: "Duplicates appearing"> 

1.9.2-p290 :010 > c.notes.count
   (0.3ms)  SELECT COUNT(*) FROM `notes` INNER JOIN `timeline_items` ON `notes`.`id` = `timeline_items`.`item_id` WHERE `timeline_items`.`card_id` = 547 AND `timeline_items`.`item_type` = 'Note'
 => 4 

1.9.2-p290 :011 > c.notes.last.card
  Note Load (2.7ms)  SELECT `notes`.* FROM `notes` INNER JOIN `timeline_items` ON `notes`.`id` = `timeline_items`.`item_id` WHERE `timeline_items`.`card_id` = 547 AND `timeline_items`.`item_type` = 'Note' ORDER BY updated_at ASC LIMIT 1
  Card Load (3.2ms)  SELECT `cards`.* FROM `cards` INNER JOIN `timeline_items` ON `cards`.`id` = `timeline_items`.`card_id` WHERE `timeline_items`.`item_id` = 620 AND `timeline_items`.`item_type` = 'Note' LIMIT 1
 => #<Card id: 547, name: "Duplicates appearing"> 

1.9.2-p290 :013 > c.notes << Note.new(:content => 'Holee Sheeet Dawg', :user_id => 1)
   (0.2ms)  BEGIN
  SQL (0.6ms)  INSERT INTO `notes` (`content`, `created_at`, `updated_at`, `user_id`) VALUES ('Holee Sheeet Dawg', '2012-09-07 11:38:55', '2012-09-07 11:38:55', 1)
   (0.1ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `timeline_items` (`card_id`, `created_at`, `item_id`, `item_type`, `updated_at`) VALUES (547, '2012-09-07 11:38:55', 625, 'Note', '2012-09-07 11:38:55')
   (0.5ms)  COMMIT
  Note Load (1.8ms)  SELECT `notes`.* FROM `notes` INNER JOIN `timeline_items` ON `notes`.`id` = `timeline_items`.`item_id` WHERE `timeline_items`.`card_id` = 547 AND `timeline_items`.`item_type` = 'Note' ORDER BY updated_at DESC
 => [#<Note id: 625, content: "Holee Sheeet Dawg", user_id: 1, created_at: "2012-09-07 11:38:55", updated_at: "2012-09-07 11:38:55">, .....]

1.9.2-p290 :014 > c.notes.count
   (0.7ms)  SELECT COUNT(*) FROM `notes` INNER JOIN `timeline_items` ON `notes`.`id` = `timeline_items`.`item_id` WHERE `timeline_items`.`card_id` = 547 AND `timeline_items`.`item_type` = 'Note'
 => 5

编辑:

我只是注意到我对card.timeline的要求还没有得到满足。因为连接来自多个表,所以我无法让 AR 为我处理连接(理想情况下 *card.timeline_items.join(:notes, :attachments)* 可以做到这一点)。

为了解决这个问题,我在我的卡片类中添加了以下方法:

  def timeline
    (self.notes + self.attachments).sort { |a,b| a.updated_at <=> b.updated_at }
  end
于 2012-09-07T12:04:24.867 回答