Eine polymorphe Beziehung ermöglicht es einem Modell, zu verschiedenen Typen anderer Modelle zu gehören. Hier zeige ich, wie man ein einzelnes Kommentarmodell dazu bringt, Artikeln, Fotos und Veranstaltungen zugeordnet zu werden.
Folgende Anwendung
Die folgende Anwendung verfügt über drei verschiedene Modelle: Artikel, Fotos und Veranstaltungen.
$ rails new polimorphic_app
$ cd polimorphi_app
Nun erstellen wir die Modelle.
$ rails g scaffold Article name content:text
$ rails g scaffold Photo name filename
$ rails g scaffold Event name starts_at:datetime ends_at:datetime description:text
$ rails db:migrate
Jetzt noch den Basis-Code, erst fügen wir die gems in das Gemfile und dann erstellen wir _main.scss.
// app/controllers/photos_controller.rb
defphoto_params
params.require(:photo).permit(:name, :filename, :image)
end
So jetzt haben wir ein Grundgerüsst mit dem wir arbeiten können. 1.png967 KB
Wir möchten Benutzern die Möglichkeit geben, Kommentare zu allen drei davon hinzuzufügen. Eine Option wäre, für jeden Typ ein separates Kommentarmodell hinzuzufügen, was zu einem Artikelkommentarmodell, einem Fotokommentarmodell und einem Veranstaltungskommentarmodell führen würde. Dies würde jedoch viel Arbeit bedeuten und Duplikation in unserer Anwendung schaffen, insbesondere da die drei Arten von Kommentaren das gleiche Verhalten und die gleichen Attribute haben sollten. In einer solchen Situation sollten wir die Verwendung einer polymorphen Beziehung in Betracht ziehen. In dieser Episode zeigen wir Ihnen, wie Sie dies tun.
Erstellen eines einzelnen Kommentarmodells
Zunächst werden wir ein einzelnes Modell für Kommentare erstellen, das wir Kommentar nennen und dem wir ein Inhaltsfeld geben werden. Um eine polymorphe Beziehung einzurichten, müssen wir uns fragen, was die anderen Modelle, zu denen dieses in Beziehung steht, gemeinsam haben. In diesem Fall sind sie alle kommentierbar, und daher werden wir diesem Modell zwei weitere Felder hinzufügen, die commentable_id und commentable_type genannt werden.
$ rails g model comment content:text commentable_id:integer commentable_type:string
Der Klassenname wird in commentable_type gespeichert, und Rails wird dies zusammen mit commentable_id verwenden, um den Datensatz zu bestimmen, mit dem der Kommentar verknüpft ist. Da diese beiden Spalten oft gemeinsam abgefragt werden, haben wir für sie einen Index hinzugefügt. Eine polymorphe Beziehung kann auf eine andere Weise angegeben werden, indem Sie belongs_to aufrufen, wie folgt:
Standardmäßig werden die Felder commentable_id und commentable_type zur Liste der attr_accessible hinzugefügt, aber da wir nicht möchten, dass auf diese Felder über Massenzuweisung zugegriffen werden kann, haben wir sie entfernt. Als nächstes müssen wir in jedes der anderen Modelle gehen und die andere Seite der Beziehung festlegen. Es ist wichtig, die Option as hier zu spezifizieren und sie auf den anderen Namen der Beziehung zu setzen, in diesem Fall commentable.
Wenn der Kommentar erstellt wird, setzt Rails automatisch das Attribut commentable_type auf "Article", damit es weiß, mit welchem Typ von Modell der Kommentar verknüpft ist. Wenn wir den umgekehrten Weg gehen und herausfinden möchten, zu welchem Artikel ein Kommentar gehört, können wir nicht einfach comment.article aufrufen, da dies völlig dynamisch ist. Stattdessen müssen wir commentable aufrufen.
Diese Methode kann auch ein Foto, eine Veranstaltung oder jedes andere Modell zurückgeben, mit dem wir den Kommentar verknüpfen möchten.
Verwendung einer polymorphen Beziehung in unserer Anwendung
Nun, da wir wissen, wie polymorphe Beziehungen funktionieren, wie verwenden wir sie in unserer Anwendung? Insbesondere wie verwenden wir verschachtelte Ressourcen, damit wir einen Pfad wie /articles/1/comments verwenden können, um die Kommentare zu einem bestimmten Artikel zu erhalten? Zuerst erstellen wir einen CommentsController, damit wir einen Ort zum Auflisten von Kommentaren haben. Wir geben ihm auch eine neue Aktion, damit wir Kommentare erstellen können.
$ rails g controller comments index new
Wir möchten, dass Kommentare eine verschachtelte Ressource unter Artikeln, Fotos und Veranstaltungen sind, daher müssen wir unsere Routendatei ändern.
// config/routes.rb
Rails.application.routes.draw do
resources :photosdo
resources :commentsend
resources :eventsdo
resources :commentsend
resources :articlesdo
resources :commentsend
root to: 'articles#index'
get 'up' => 'rails/health#show', as: :rails_health_checkend
Diese Routen werden zum generierten CommentsController führen. In der Index-Aktion möchten wir die Kommentare für das jeweilige commentable-Modell abrufen, das übergeben wurde. Um dies zu tun, müssen wir den commentable-Datensatz abrufen, der die Kommentare besitzt, aber vorerst nehmen wir an, dass die übergebene ID zu einem Artikel gehört.
// app/controllers/comments_controller.rb
classCommentsController < ApplicationControllerdefindex@commentable = Article.find(params[:article_id])
@comments = @commentable.comments
enddefnew; end
private
# Only allow a list of trusted parameters through.defevent_params
params.require(:comment).permit(:content)
endend
Im View-Template durchlaufen wir die Kommentare und zeigen sie an.
// app/views/comments/index.html.erb
<h1>Comments</h1><divid="comments"><%@comments.each do |comment| %><divclass="comment"><%= simple_format comment.content %></div><%end%></div>
Wenn wir jetzt /articles/1/comments besuchen, sehen wir den einen Kommentar, den wir zuvor zu diesem Artikel hinzugefügt haben. (Wir haben bereits einige CSS-Stile zur _comments.scss-Datei hinzugefügt.)
Wenn wir versuchen, die Kommentarseite für ein Foto zu besuchen, funktioniert dies nicht, da der Code versucht, einen Artikel zu finden, obwohl keine article_id übergeben wurde. Um dies zu beheben, müssen wir die Suche im Controller dynamischer gestalten. Wir verschieben den Code zum Finden des zugehörigen Modells in einen before_filter. Wir müssen den Namen der commentable-Ressource und ihre ID bestimmen. Diese erhalten wir von request.path, indem wir es bei jedem Schrägstrich aufteilen und die zweiten und dritten Elemente abrufen. Wenn also der Pfad /photos/1 ist, werden diese beiden Elemente verwendet. Wir können diese verwenden, um @commentable zu setzen, indem wir singlularize.classify.constantize aufrufen, um die Klasse des Modells zu erhalten, und dann find darauf aufrufen, um die Instanz nach der ID zu erhalten.
// app/controllers/comments_controller.rb
classCommentsController < ApplicationController
before_action :load_commentabledefindex@comments = @commentable.comments
enddefnew; end
private
defload_commentable
resource, id = request.path.split('/')[1, 2]
@commentable = resource.singularize.classify.constantize.find(id)
end# Only allow a list of trusted parameters through.defevent_params
params.require(:comment).permit(:content)
endend
Dies ist der einfachste Weg, dies zu tun, führt jedoch zu einer engen Verknüpfung zwischen dem Controller und dem Format der URL. Wenn wir benutzerdefinierte URLs verwenden, könnten wir eine andere Technik verwenden, wie diese:
def load_commentable
klass = [Article, Photo, Event].detect { |c| params["#{c.name.underscore}_id"]}
@commentable = klass.find(params["#{klass.name.underscore}_id"])
end
Dies wird jede der commentable-Klassen nehmen und in den Parametern nach einer Übereinstimmung mit dem Klassennamen gefolgt von _id suchen. Dann verwenden wir die übereinstimmende Klasse, um eine Übereinstimmung mit der ID zu finden. Wenn wir jetzt versuchen, die Kommentare für ein Foto anzuzeigen, funktioniert die Seite. 3.png597 KB
Adding Links
Als nächstes sehen wir uns an, wie wir mit Links umgehen. Wie erstellen wir einen Link zum Hinzufügen eines Kommentars zu einem kommentierbaren Element? Wenn die Kommentarseite nur für Fotos wäre, könnten wir den Pfad new_photo_comment_path verwenden und ein Foto übergeben. Wir müssen jedoch auch Artikel und Veranstaltungen unterstützen, sodass dieser Ansatz hier nicht funktioniert. Was wir tun können, ist, ein Array zu übergeben, sodass Rails den Aufruf dynamisch basierend darauf generiert, was wir übergeben, wie folgt:
// app/views/comments/index.html.erb
<p><%= link_to "New Comment", [:new, @commentable, :comment] %></p>
Rails wird den richtigen Pfad jetzt dynamisch basierend auf dem Typ der Variable @commentable generieren. Wenn wir auf den Link klicken, gelangen wir zur new-Aktion des CommentsControllers. Als nächstes schreiben wir den Code, um das Hinzufügen von Kommentaren zu behandeln. Der Code für die Aktionen new und create ist ziemlich standardmäßig. In new erstellen wir einen Kommentar über die comments-Assoziation, während wir in create, wenn der Kommentar erfolgreich gespeichert wird, zur Index-Aktion umleiten und die Array-Technik verwenden, die wir im View verwendet haben, um zum Artikelkommentarpfad, zum Fotokommentarpfad oder zum Veranstaltungskommentarpfad zu gelangen, abhängig davon, gegen was der neue Kommentar gespeichert wird.
Als nächstes schreiben wir die Ansicht für das Hinzufügen eines Kommentars.
// app/views/comments/new.html.erb
<h1>New Comment</h1><%= form_for [@commentable, @comment] do |f| %><%if@comment.errors.any? %><divclass="error_messages"><h2>Please correct the following errors.</h2><ul><%@comment.errors.full_messages.each do |msg| %><li><%= msg %></li><%end%></ul></div><%end%><divclass="field"><%= f.text_area :content, rows: 8%></div><divclass="actions"><%= f.submit %></div><%end%>
Dieser Code ist ebenfalls ziemlich standardmäßig mit einem Formular zum Bearbeiten des Kommentarinhalts. Ein wesentlicher Unterschied ist das Array, das wir an form_for übergeben, damit es die URL korrekt für die polymorphe Beziehung generiert. Wenn wir die Seite für den neuen Kommentar jetzt neu laden, sehen wir das Formular, und wenn wir einen Kommentar hinzufügen, werden wir zurück zur richtigen Seite umgeleitet. 4.png616 KB
Aus der Benutzeroberflächensicht wäre es wahrscheinlich besser, wenn alle Kommentarfunktionen inline auf der jeweiligen Show-Seite des Modells wären. Dies ist einfach zu erreichen, wenn wir das, was wir erstellt haben, in ein paar Partialansichten verschieben. Wir werden das Formular von der Seite "neuer Kommentar" in ein Partial unter /app/views/comments/_form.html.erb verschieben und es dann in der neuen Vorlage verwenden.
Alternativ könnten wir einige Umbenennungen vornehmen und die Partials so ändern, dass wir nicht alle diese Instanzvariablen angeben müssen. Unabhängig von der gewählten Vorgehensweise müssen wir dieselben Änderungen auch an den Controllern und Ansichten für Fotos und Veranstaltungen vornehmen.
Schließlich müssen wir im CommentsController das Umleitungsverhalten ändern, damit der Benutzer nach erfolgreicher Erstellung eines Kommentars zur entsprechenden Show-Aktion des kommentierbaren Elements umgeleitet wird, anstatt zur Index-Aktion.
Lass es uns ausprobieren. Wenn wir jetzt einen Artikel besuchen, sehen wir seine Kommentare zusammen mit dem Formular zum Erstellen eines neuen Kommentars. Wenn wir einen Kommentar hinzufügen, werden wir zur gleichen Seite zurückgeleitet, auf der der neue Kommentar angezeigt wird. 5.png711 KB
Wir können diese Schritte problemlos auf Fotos und Veranstaltungen anwenden, um eine Inline-Kommentarfunktion hinzuzufügen.
Wir verwenden Cookies, um Inhalte und Anzeigen zu personalisieren, Funktionen für soziale Medien anbieten
zu können und die Zugriffe auf unsere Website zu analysieren.
Sie akzeptieren unsere Cookies, wenn Sie fortfahren diese Webseite zu nutzen.
Datenschutzerklärung.