2010-09-10 5 views
7

In config/routes.rb:Hat jemand Tipps zum Verwalten polymorpher verschachtelter Ressourcen in Rails 3?

resources :posts do 
    resources :comments 
end 

resources :pictures do 
    resources :comments 
end 

Ich möchte für mehr Dinge zu ermöglichen und zu kommentiert.

Ich verwende derzeit mongoid (mongomapper ist nicht so kompatibel mit Rails 3 noch, wie ich möchte), und Kommentare sind eine eingebettete Ressource (Mongoid kann noch nicht mit polymorphen relationalen Ressourcen umgehen), was bedeutet, dass ich es tue benötigen Sie die übergeordnete Ressource, um den Kommentar zu finden.

Gibt es elegante Möglichkeiten, einige der folgenden Probleme zu behandeln:

In meinem Controller, muss ich die Eltern finden, bevor Sie den Kommentar zu finden:

if params[:post_id] 
    parent = Post.find(params[:post_id] 
else if params[:picture_id] 
    parent = Picture.find(params[:picture_id] 
end 

die chaotisch geht zu erhalten, wenn Ich fange an, weitere Dinge hinzuzufügen, um kommentierbar zu sein.

auch nicht url_for([comment.parent, comment]) nicht, so werde ich etwas in meinem Comment Modell zu definieren, aber ich denke, ich werde auch sowie möglicherweise eine einen Index Route im Comment Modell definieren müssen Bearbeiten und neue Routendefinition.

Es könnte mehr Probleme geben, mit denen ich umgehen muss, wenn ich weiter komme.

Ich kann mir nicht vorstellen, dass ich die erste Person bin, die versucht, dieses Problem zu lösen, gibt es irgendwelche Lösungen, um das überschaubarer zu machen?

Antwort

4

Ich musste etwas ähnliches in einer App von mir tun. Ich nahm, was ich dachte, und änderte es ein wenig, aber ich habe es nicht getestet, also vorsichtig. Es ist nicht schön, aber es ist besser als alles, woran ich denken konnte.

In routes.rb:

resources :posts, :pictures 

controller :comments do 
    get '*path/edit' => :edit, :as => :edit_comment 
    get '*path'  => :show, :as => :comment 
    # etc. The order of these is important. If #show came first, it would direct /edit to #show and simply tack on '/edit' to the path param. 
end 

In comment.rb:

embedded_in :commentable, :inverse_of => :comments 

def to_param 
    [commentable.class.to_s.downcase.pluralize, commentable.id, 'comments', id].join '/' 
end 

In einem vor-Filter in comments_controller.rb:

parent_type, parent_id, scrap, id = params[:path].split '/' 

# Security: Make sure people can't just pass in whatever models they feel like 
raise "Uh-oh!" unless %w(posts pictures).include? parent_type 

@parent = parent_type.singularize.capitalize.constantize.find(parent_id) 
@comment = @parent.comments.find(id) 

Ok, Hässlichkeit über. Jetzt können Sie Kommentare zu irgendwelchen Modellen hinzufügen, die Sie wollen, und tun Sie einfach:

Und so weiter.

Edit: Ich habe implementieren keine anderen Wege in meinem eigenen App, weil alles, was ich war zu bearbeiten und zu aktualisieren benötigt, aber ich könnte mir vorstellen, sie so etwas wie aussehen würde:

controller :comments do 
    get '*path/edit' => :edit, :as => :edit_comment 
    get '*path'  => :show, :as => :comment 
    put '*path'  => :update 
    delete '*path'  => :destroy 
end 

Die anderen Aktionen wird kniffliger sein. Sie müssen wahrscheinlich etwas tun:

get ':parent_type/:parent_id/comments'  => :index, :as => :comments 
    post ':parent_type/:parent_id/comments'  => :create 
    get ':parent_type/:parent_id/comments/new' => :new, :as => :new_comment 

Sie dann das übergeordnete Modell in der Steuerung zugreifen würden mit params [: PARENT_TYPE] und params [: parent_id].Sie würden auch die richtigen Parameter an die URL Helfer übergeben müssen:

comments_path('pictures', 7) 
+0

senden Das sieht eigentlich wirklich gut; Mit ein paar kleinen Änderungen habe ich das einfach umgesetzt und zum größten Teil scheint es gut zu funktionieren. Wo ist die Dokumentation für die Routing-Tricks, die Sie im ersten Teil verwendet haben, damit ich herausfinden kann, wie man die Routen anlegen, aktualisieren, löschen und indexieren kann? – Ryan

+0

Okay, ich denke, ich habe herausgefunden, was du gemacht hast und warum du es hässlich findest. Der Kommentar muss im Grunde genommen die letzte definierte Route sein, da er zu allem passt. Ich habe einen Teil von dem, was du getan hast, und einen Teil von dem, was ich getan habe, benutzt, um alles umzusetzen. Ich schätze deine Hilfe sehr. – Ryan

+0

Ah, richtig, tut mir leid, wenn ich unklar war. Diese gesprenkelten Routen können schwierig sein. – PreciousBodilyFluids

2

Ryan Bates polymorphe Verbände in Railscasts #154 bedeckt, aber das Beispiel war für Rails 2 und Active Record. Es gelang mir, sein Beispiel mit Rails 3 und Mongoid zu arbeiten, indem ich einige Änderungen vornahm.

in der Post und Bildmodelle, fügen Sie die folgende Zeile ein:

embeds_many :comments, :as => :commentable 

Nach dem Mongoid associations documentation alle embedded_in Verbände polymorph sind. Sie benötigen die im Railcast verwendeten Spalten commentable_id und commentable_type nicht, wenn Sie Mongoid verwenden, da der Kommentar ein Kommentarelement ist. Im Kommentar Modell, fügen Sie die folgende Zeile:

embedded_in :commentable, :inverse_of => :comment 

Setup für die Routen in config/routes.rb wie folgt aus:

resources posts do 
    resources comments 
end 

resources pictures do 
    resources comments 
end 

Fügen Sie die folgende Methode, um Ihre Kommentare Controller als private Methode. Dies ist identisch mit Ryans Methode:

def find_commentable 
    params.each do |name, value| 
    if name =~ /(.+)_id$/ 
     return $1.classify.constantize.find(value) 
    end 
    end 
    nil 
end 

In jedem Ihrer Kommentare Controller-Aktionen, wo Sie den Kommentar finden müssen, die find_commentable Methode aufrufen, zunächst die Eltern zu bekommen. Sobald der Elternteil gefunden wurde, können Sie den Kommentar nach ID suchen, indem Sie die Kommentare des Kommentars durchsuchen. Zum Beispiel Maßnahmen im Bearbeiten Sie den Code um den Kommentar finden würde wie folgt aussehen:

@commentable = find_commentable 
@comment = @commentable.comments.find(params[:id]) 

Um die Wiederholung des Aufrufs find_commentable zu Beginn jeder Aktion zu reduzieren, könnten Sie ein, bevor der Filter an der Spitze der Put- Controller wie folgt aus:

class CommentsController < ApplicationController 
    before_filter :find_commentable 
    ... 

Und dann in der find_commentable Methode, um den Rückruf zu ändern:

return @commentable = $1.classify.constantize.find(value) 

gestoßen ich keine Probleme mit diesem getroffen hod, aber wenn Sie auf irgendwelche Probleme stoßen, weisen Sie sie bitte darauf hin.

0

In Anlehnung an die Antwort von Urobject fand ich, dass die Beziehungen funktionieren, aber benannte Routen immer noch nicht.

Ich bin immer noch ziemlich neu bei Schienen 3, aber ich fand eine einfache Lösung mit Eval. Zum Beispiel sind in meinem Projekt die polymorphen Eltern (in meiner App als die Mongoid-Objekte Produkt und Kategorie dargestellt) als @imagable definiert, wobei eine Modifikation von find_comentable verwendet wird und das Kind, das bearbeitet wird, als @image bezeichnet wird.

send("#{@imagable.class.name.downcase}_image_url", @imagable, image) 

Diese für alle genannten Pfade funktioniert:

eine URL wie product_image_path(@imagable, @image) die tut GET => products/:product_id/images/ kann ersetzt werden. Zum Beispiel:

link_to 'Edit', send("edit_#{@imagable.class.name.downcase}_image_path", @imagable, image) 
link_to 'Destroy', send("#{@imagable.class.name.downcase}_image_url", @imagable, image), :confirm => 'Are you sure?', :method => :delete 

Der Nachteil ist, dass Blätter über Ihre Ansichten und in den Controllern sendet, wo immer Sie Weiterleitungen haben.

Gibt es eine elegantere Lösung, um dies über Routen zu tun?

* ersetzt eval mit

+0

Sie könnten [polymorphe Routen] (http://api.rubyonrails.org/classes/ActionDispatch/Routing/PolymorphicRoutes.html), z. 'edit_polymorphic_path (bebilderbar, image)' –

Verwandte Themen