2010-09-10 15 views
7

in config/routes.rb:Qualcuno ha qualche consiglio per la gestione delle risorse nidificate polimorfiche in Rails 3?

resources :posts do 
    resources :comments 
end 

resources :pictures do 
    resources :comments 
end 

vorrei per consentire più cose per essere commentato come bene.

Attualmente sto usando mongoid (mongomapper non è ancora compatibile con Rails 3 come vorrei), ei commenti sono una risorsa incorporata (mongolo non può ancora gestire risorse relazionali polimorfiche), il che significa che lo faccio serve la risorsa genitore per trovare il commento.

Esistono eleganti modi per gestire alcuni dei seguenti problemi:

Nel mio controller, ho bisogno di trovare il genitore prima di trovare il commento:

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

che sta per ottenere disordinato se Comincio ad aggiungere altre cose per essere commentabili.

anche url_for([comment.parent, comment]) non funziona, quindi ho intenzione di avere a definire qualcosa nel mio modello Comment, ma penso che sto anche andando ad avere bisogno di definire un percorso di indice nel modello Comment così come potenzialmente un modifica e nuova definizione del percorso.

Potrebbero esserci altri problemi che devo affrontare man mano che procedo oltre.

Non riesco a immaginare che io sia la prima persona a provare a risolvere questo problema, ci sono soluzioni là fuori per renderlo più gestibile?

risposta

4

Ho dovuto fare qualcosa di simile in una mia app. Ho preso quello che mi è venuto in mente e l'ho cambiato un po ', ma non l'ho testato, quindi usalo con cura. Non è carino, ma è meglio di qualsiasi altra cosa a cui sono riuscito a pensare.

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 una prima filtro 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, bruttezza sopra. Ora è possibile aggiungere commenti a qualsiasi modello desiderato e fare semplicemente:

edit_comment_path @comment 
url_for @comment 
redirect_to @comment 

E così via.

Edit: Io non realizzare qualsiasi altri percorsi nella mia app, perché tutto quello che serviva era di modifica e aggiornamento, ma mi immagino che avevano qualcosa del tipo:

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

Le altre azioni sarà più difficile. Avrete probabilmente bisogno di fare qualcosa di simile:

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 

Farebbe quindi accedere al modello di genitore nel controller utilizzando params [: PARENT_TYPE] e params [: parent_id].Faresti anche necessario passare i parametri corretti per gli aiutanti url:

comments_path('pictures', 7) 
+0

Questo sembra davvero buono; con alcune piccole modifiche ho appena implementato questo, e per la maggior parte sembra funzionare bene. dov'è la documentazione per l'inganno di routing che hai usato nella prima parte in modo da poter capire come collegare anche i percorsi di creazione, aggiornamento, cancellazione e indicizzazione? – Ryan

+0

Ok, penso di aver capito cosa stavi facendo e perché lo trovi brutto. Il commento deve fondamentalmente essere l'ultimo percorso definito perché corrisponderà a qualsiasi cosa. Ho finito per utilizzare parte di quello che stavi facendo e parte di ciò che stavo facendo per ottenere tutto ciò che è stato implementato. Apprezzo molto il vostro aiuto. – Ryan

+0

Ah, giusto, scusate se non ero chiaro. Quelle linee splattate possono essere complicate. – PreciousBodilyFluids

2

Ryan Bates coperto associazioni polimorfiche in Railscasts #154, ma l'esempio è stato per Rails 2 e Active Record. Sono riuscito a far funzionare il suo esempio usando Rails 3 e Mongoid apportando alcune modifiche.

Nei modelli postali e l'immagine, aggiungere la seguente riga:

embeds_many :comments, :as => :commentable 

Secondo il Mongoid associations documentation, tutti embedded_in associazioni sono polimorfici. Non hai bisogno delle colonne commentable_id e commentable_type menzionate nel Railscast quando usi Mongoid, perché il commento è figlio del commentabile. Nel modello di commento, aggiungere la seguente riga:

embedded_in :commentable, :inverse_of => :comment 

Setup rotte in config/routes.rb come questo:

resources posts do 
    resources comments 
end 

resources pictures do 
    resources comments 
end 

Aggiungere il seguente metodo per il controller commenti come metodo private. Questo è identico al metodo di Ryan:

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

In ognuno dei vostri commenti azioni di controllo in cui è necessario trovare il commento, chiamare il metodo find_commentable primo a ricevere il genitore. Una volta trovato il genitore, puoi trovare il commento per ID, cercando tra i commenti del commento. Per esempio, nell'azione modificare il codice per trovare il commento sarebbe simile a questa:

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

Per ridurre la ripetizione di chiamare find_commentable all'inizio di ogni azione, si potrebbe mettere un prima filtro nella parte superiore della Controller in questo modo:

class CommentsController < ApplicationController 
    before_filter :find_commentable 
    ... 

e quindi modificare la chiamata di ritorno nel metodo find_commentable a:

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

non ho riscontrato alcun problema con questo si è riunito Hod, ma se incontri qualche problema, per favore segnalalo.

0

Sulla base della risposta di Uriptical, ho trovato che le relazioni funzionavano ma le rotte con nome non lo erano ancora.

Sono ancora piuttosto nuovo su rails 3 ma ho trovato una soluzione semplice con eval.

Ad esempio, nel mio progetto, i genitori polimorfici (rappresentati nella mia app come oggetti mongoi di prodotto e categoria) sono definiti come @imagable usando una modifica di find_comentable e il bambino che si sta modificando viene chiamato @ immagine.

un URL come product_image_path(@imagable, @image) che fa GET => products/:product_id/images/ può essere sostituito con:

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

Questo funziona per tutti i percorsi di nome. Per esempio:

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 

L'aspetto negativo di questo è che lascia invia tutto vostro punto di vista e nei controllori ovunque ci sia reindirizzamenti.

C'è una soluzione più elegante per farlo tramite percorsi?

* sostituito eval con inviare

+0

È possibile utilizzare [Polymorphic Routes] (http://api.rubyonrails.org/classes/ActionDispatch/Routing/PolymorphicRoutes.html), ad es. 'edit_polymorphic_path (imageable, image)' –

Problemi correlati