2012-09-07 3 views
1

Je souhaite créer une fonction 'chronologie' pour un modèle de carte existant. La Carte a déjà beaucoup de Notes et a de nombreux Attachements. Je voudrais pouvoir:Association polymorphe Rails représentant des modèles has_many existants

  • notes accès, les pièces jointes (et d'autres modèles éventuellement) dans une collection unifiée avec une belle méthode comme: card.timeline
  • toujours en mesure d'accéder à une carte de notes et pièces jointes comme: card.notes
  • encore être en mesure d'accéder à une carte mère de note comme: note.card
  • pouvoir ajouter des éléments à la chronologie de la carte, avec une API comme: carte . chronologie < < Note

Je pense que j'ai mon DB configuré correctement, il est la déclaration d'association, je ne peux pas sembler obtenir le droit. Voici mon schéma:

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 

Quelqu'un sait comment je peux y parvenir en utilisant ActiveRecord? Ça me fout en l'air mentalement!

Un point de départ est:

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 

Merci à l'avance ~ Stu

Répondre

0

D'accord - après avoir lutté sur et en dehors de cela, je craque dans un délai de 10 minutes de l'affichage à stackoverflow! Typique.

Pour sauver les autres de se cogner la tête contre les murs, voici ce que j'avais mal:

Remarque aurait dû être:

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

Et ce fut tout! J'essayais d'utiliser les méthodes de création utilisées dans this article, mais en fait ce n'est pas nécessaire.

Voici la sortie de la console, montrant que les instructions SQL utilisent tous la table 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 

EDIT:

Je viens de remarquer que mon exigence d'avoir card.timeline WASN Je ne l'ai pas encore rencontré. Parce que la jointure provient de plusieurs tables, je n'ai pas pu obtenir AR pour gérer la jointure pour moi (idéalement * card.timeline_items.join (: notes,: attachments) * aurait fait l'affaire).

Pour résoudre ce problème, j'ai ajouté la méthode suivante pour ma classe de carte:

def timeline 
    (self.notes + self.attachments).sort { |a,b| a.updated_at <=> b.updated_at } 
    end 
+0

Je pense avoir répondu à 90% de mes propres questions ici :) Merci d'envoyer votre solution. –

+0

Pas de problème - si j'ai raté quelque chose alors laissez-moi savoir – Stu

Questions connexes