Wenn Sie genug von Modell-IDs in der URL haben, kann die Überschreibung von to_param nur begrenzt helfen. Das FriendlyId-Plugin kann Ihnen helfen, indem es das Generieren eines URL-Slugs vereinfacht und eine Verlaufshistorie beibehält.
Vorbereitung
Eine einfache Rails-Blogging-Anwendung erstellen
In diesem Tutorial verbessern wir die URLs einer einfachen Rails-Blogging-Anwendung. Standardmäßig verwendet Rails die interne ID eines Datensatzes in der URL. Ein Artikel kann dadurch beispielsweise über folgende Adresse erreichbar sein:
http://localhost:8080/articles/1
Technisch funktioniert diese URL problemlos. Sie verrät jedoch nichts darüber, welcher Artikel sich dahinter verbirgt.
Unser Ziel ist es, daraus später eine aussagekräftigere URL zu machen, beispielsweise:
http://localhost:8080/articles/superman
Dafür sehen wir uns zunächst die Rails-eigene Methode to_param an und verwenden anschließend das Gem FriendlyId, um richtige Slugs für unsere Artikel zu erzeugen.
Zunächst erstellen wir eine neue Rails-Anwendung:
$ rails new friendlyid_app
$ cd friendlyid_app
Anschließend erzeugen wir mit dem Scaffold-Generator unser Article-Model zusammen mit Controller, Views und den entsprechenden RESTful-Routen:
$ rails g scaffold Article name content:text published_at:datetime
Unser Artikel besitzt damit die drei Attribute name, content und published_at.
Nun führen wir die erzeugte Datenbankmigration aus:
$ rails db:migrate
Damit steht das Grundgerüst unserer Blogging-Anwendung zur Verfügung. Wir können Artikel erstellen, bearbeiten, anzeigen und löschen.
Auf der Übersichtsseite werden die vorhandenen Artikel angezeigt. Öffnen wir einen einzelnen Artikel, verwendet Rails zunächst dessen interne Datenbank-ID als URL-Parameter.
Beispielsweise:
http://localhost:8080/articles/1
Die 1 identifiziert den Datensatz eindeutig, beschreibt den Inhalt des Artikels aber nicht. Eine URL, die zusätzlich den Namen des Artikels enthält, wäre für Menschen wesentlich aussagekräftiger.
Im nächsten Schritt sehen wir uns deshalb zunächst an, wie wir dieses Verhalten mit Rails selbst und der Methode to_param beeinflussen können.
1.webp
202 KB
Aussagekräftigere URLs
Die Rails-Methode to_param überschreiben
Standardmäßig verwendet Rails die ID eines Datensatzes als Parameter in der URL. Unser erster Artikel ist deshalb beispielsweise unter folgender Adresse erreichbar:
http://localhost:8080/articles/1
Die ID ist für Rails praktisch, für den Besucher sagt sie jedoch nichts über den Inhalt des Artikels aus. Eine URL wie:
http://localhost:8080/articles/1-superman
ist wesentlich aussagekräftiger.
Rails verwendet intern die Methode to_param, wenn ein Active-Record-Objekt in einen URL-Parameter umgewandelt wird. Dieses Verhalten können wir direkt in unserem Model überschreiben.
Dazu öffnen wir das Article-Model und ergänzen die Methode to_param:
Wir erzeugen zunächst einen String aus der ID und dem Namen des Artikels:
"#{id} #{name}"
Bei einem Artikel mit der ID 1 und dem Namen Superman entsteht zunächst:
1 Superman
Anschließend rufen wir darauf parameterize auf:
"#{id} #{name}".parameterize
Rails wandelt den String dadurch in eine Form um, die sich besser für eine URL eignet. Leerzeichen werden beispielsweise durch Bindestriche ersetzt und Großbuchstaben entsprechend normalisiert.
Unser Artikel ist anschließend über folgende URL erreichbar:
http://localhost:8080/articles/1-superman
Die ID steht dabei bewusst weiterhin am Anfang des Parameters.
Der von einem normalen Scaffold erzeugte Controller sucht einen Artikel üblicherweise mit:
Article.find(params[:id])
Active Record kann einen Parameter wie "1-superman" weiterhin anhand der führenden numerischen ID dem Datensatz 1 zuordnen.
Damit erhalten wir bereits mit Rails selbst besser lesbare URLs, ohne die grundlegende Suche nach der Datenbank-ID verändern zu müssen.
Möchten wir allerdings URLs vollständig ohne interne ID verwenden, beispielsweise:
http://localhost:8080/articles/superman
reicht diese einfache to_param-Lösung nicht mehr aus.
Genau dafür verwenden wir im nächsten Schritt FriendlyId.
Einführung von FriendlyId
FriendlyId installieren und im Model einrichten
Mit unserer überschriebenen to_param-Methode haben wir bereits eine besser lesbare URL erhalten:
http://localhost:8080/articles/1-superman
Die interne Datenbank-ID ist allerdings weiterhin Bestandteil der URL. Möchten wir stattdessen einen Artikel ausschließlich über einen sprechenden Namen beziehungsweise einen Slug aufrufen, können wir das Gem friendly_idverwenden.
Zunächst ergänzen wir FriendlyId in unserer Gemfile:
# Gemfile
...
gem "friendly_id"
Anschließend installieren wir das neue Gem:
$ bundle install
Da FriendlyId nun die Erzeugung unserer URL-Parameter übernehmen soll, entfernen wir die zuvor angelegte to_param-Methode wieder aus dem Article-Model.
Stattdessen erweitern wir das Model zunächst um FriendlyId:
stellen wir dem Model die Funktionen von FriendlyId zur Verfügung.
Anschließend legen wir fest, welches Attribut für die URL verwendet werden soll:
friendly_id :name
In unserem Fall verwenden wir dafür den Namen des Artikels.
Ein Artikel mit dem Namen Superman kann damit anstelle seiner numerischen ID über seinen Namen angesprochen werden.
Eine entsprechende URL sieht beispielsweise so aus:
http://localhost:8080/articles/Superman
Damit haben wir die interne ID zunächst aus der URL entfernt.
Diese einfache Variante hat allerdings noch einen Nachteil. Enthält der Artikelname Leerzeichen oder Sonderzeichen, erhalten wir noch keinen sauber erzeugten Slug.
Bei einem Artikel mit dem Namen:
Batman & Robin
kann der Name in der URL entsprechend codiert werden, anstatt eine typische Slug-Form wie batman-robin zu erhalten.
Für dauerhaft saubere und URL-freundliche Werte verwenden wir deshalb im nächsten Schritt die Slug-Funktion von FriendlyId.
Verwendung von Slugs
URL-freundliche Slugs für unsere Artikel erzeugen
Bisher verwendet FriendlyId direkt den Wert aus name für unsere URL. Bei einfachen Namen wie Superman funktioniert das bereits, bei Namen mit Leerzeichen und Sonderzeichen möchten wir jedoch einen richtigen URL-freundlichen Slug erzeugen.
Dafür aktivieren wir in unserem Article-Model das FriendlyId-Modul slugged:
weisen wir FriendlyId an, aus dem Namen einen URL-freundlichen Slug zu erzeugen.
Aus einem Artikelnamen wie:
Batman & Robin
wird dadurch beispielsweise:
batman-robin
Damit FriendlyId diesen Wert dauerhaft speichern kann, benötigt unsere articles-Tabelle eine zusätzliche Spalte namens slug.
Wir erzeugen dafür eine Migration:
$ rails g migration AddSlugToArticles slug:string:uniq
Zusätzlich führen wir den FriendlyId-Generator aus:
$ rails g friendly_id
Der FriendlyId-Generator legt die grundlegende FriendlyId-Konfiguration an. Je nach verwendeter FriendlyId-Version können dabei auch weitere Dateien beziehungsweise Migrationen für zusätzliche Funktionen von FriendlyId erzeugt werden.
Unsere Migration für die slug-Spalte sieht anschließend folgendermaßen aus:
Der Zeitstempel am Anfang des Dateinamens wird von Rails automatisch erzeugt und ist deshalb bei jedem Projekt unterschiedlich.
Der eindeutige Index:
add_index :articles, :slug, unique: true
ist wichtig, da der Slug zur Identifikation unserer Artikel verwendet wird und nicht mehrfach vergeben werden soll.
Anschließend führen wir die Migrationen aus:
$ rails db:migrate
Damit besitzt die Tabelle articles nun eine slug-Spalte.
Slugs für bereits vorhandene Artikel erzeugen
Neu angelegte Artikel erhalten ihren Slug automatisch. Unsere bereits vorhandenen Artikel wurden jedoch erstellt, bevor die slug-Spalte existierte. Für diese Datensätze müssen wir die Slugs einmal erzeugen.
Dazu öffnen wir die Rails-Konsole:
$ rails console
Dort speichern wir alle vorhandenen Artikel erneut:
Article.find_each(&:save)
Anschließend verlassen wir die Konsole:
exit
FriendlyId kann beim Speichern nun aus den vorhandenen Artikelnamen die entsprechenden Slugs erzeugen.
Aus:
Batman & Robin
wird beispielsweise:
batman-robin
und damit eine URL wie:
http://localhost:8080/articles/batman-robin
Artikel über FriendlyId finden
Eine Änderung benötigen wir noch in unserem ArticlesController.
Der von Rails erzeugte Scaffold-Controller sucht einen Artikel standardmäßig anhand seiner numerischen ID. In set_article steht deshalb zunächst sinngemäß:
@article = Article.find(params[:id])
Da unsere URLs jetzt Slugs enthalten, verwenden wir stattdessen den FriendlyId-Finder:
# app/controllers/articles_controller.rb# Use callbacks to share common setup or constraints between actions.defset_article@article = Article.friendly.find(params[:id])
end
Article.friendly.find kann den von FriendlyId erzeugten Slug auflösen und den dazugehörigen Artikel laden.
Damit können unsere Artikel nun über lesbare URLs ohne vorangestellte Datenbank-ID aufgerufen werden.
Umgang mit geänderten Artikelnamen
Alte Slugs mit FriendlyId History weiterhin erreichbar machen
Unsere Artikel besitzen nun lesbare URLs auf Basis ihres Namens. Ändern wir allerdings später den Namen eines Artikels, kann sich dadurch auch sein Slug und damit seine URL ändern.
Nehmen wir als Beispiel unseren Artikel:
Batman & Robin
mit dem Slug:
batman-robin
Wird der Artikel später in:
Batman & Robin 2
umbenannt und dadurch ein neuer Slug erzeugt, lautet dieser beispielsweise:
batman-robin-2
Die neue URL lautet entsprechend:
http://localhost:8080/articles/batman-robin-2
Das kann problematisch sein, wenn die bisherige URL bereits verlinkt, als Lesezeichen gespeichert oder von einer Suchmaschine erfasst wurde.
FriendlyId bietet dafür das Modul history. Es speichert frühere Slugs und ermöglicht es dadurch, einen Artikel weiterhin über einen alten Slug zu finden.
Den benötigten FriendlyId-Generator haben wir bereits im vorherigen Abschnitt ausgeführt:
$ rails g friendly_id
Die dabei erzeugte FriendlyId-Migration stellt die Tabelle zur Verfügung, die unter anderem für die Slug-Historie benötigt wird. Da wir anschließend bereits:
$ rails db:migrate
ausgeführt haben, müssen wir an dieser Stelle keine weitere Migration erstellen.
Nun aktivieren wir zusätzlich history in unserem Article-Model:
Nun verwenden wir mehrere FriendlyId-Module. Deshalb geben wir sie als Array an:
use: %i[slugged history]
slugged kümmert sich weiterhin um die Erzeugung unserer URL-freundlichen Slugs. history sorgt zusätzlich dafür, dass vorherige Slugs gespeichert werden, wenn sich ein Slug ändert.
Einen geänderten Slug testen
Wir können das Verhalten beispielsweise mit einem Artikel namens:
Hello World
testen.
Nach dem Erstellen besitzt dieser Artikel zunächst den Slug:
hello-world
Ändern wir anschließend den Namen so, dass FriendlyId einen neuen Slug erzeugt, kann der vorherige Slug in der History erhalten bleiben.
Dadurch kann FriendlyId den Artikel später nicht nur über seinen aktuellen Slug, sondern auch über einen gespeicherten früheren Slug finden.
Wichtig ist dabei: Die History kann natürlich nur Slug-Änderungen speichern, die ab dem Zeitpunkt stattfinden, an dem die History-Funktion eingerichtet ist. Slugs, die bereits vorher geändert und nirgendwo gespeichert wurden, können nachträglich nicht automatisch rekonstruiert werden.
Alte URLs auf die aktuelle URL umleiten
FriendlyId kann einen Artikel über einen historischen Slug finden. Für eine saubere URL möchten wir den Besucher anschließend jedoch auf die aktuelle URL des Artikels weiterleiten.
Dafür passen wir die show-Action in unserem ArticlesController an:
# app/controllers/articles_controller.rb
# GET /articles/1 or /articles/1.json
def show
return unless request.path != article_path(@article)
redirect_to @article, status: :moved_permanently
end
Zunächst vergleichen wir den aktuell aufgerufenen Pfad:
request.path
mit dem Pfad, den Rails für den aktuell geladenen Artikel erzeugt:
article_path(@article)
Sind beide Pfade identisch, passiert nichts und die normale show-View wird angezeigt.
Unterscheiden sich die Pfade dagegen, wurde der Artikel beispielsweise über einen alten Slug aufgerufen. In diesem Fall leiten wir auf seine aktuelle URL weiter:
redirect_to @article, status: :moved_permanently
Mit :moved_permanently sendet Rails dabei den HTTP-Status 301 Moved Permanently.
Aus einem Aufruf der alten URL:
http://localhost:8080/articles/batman-robin
kann damit automatisch eine Weiterleitung auf die aktuelle URL erfolgen:
http://localhost:8080/articles/batman-robin-2
Der Artikel bleibt dadurch über seinen alten Slug erreichbar, während Besucher anschließend die aktuelle URL verwenden.
Weitere Möglichkeiten mit FriendlyId
FriendlyId um zusätzliche Funktionen erweitern
Damit haben wir die grundlegenden Funktionen von FriendlyId für unsere Blogging-Anwendung eingerichtet. Unsere Artikel besitzen nun lesbare Slugs, können über diese gefunden werden und frühere Slugs lassen sich über die History weiterhin auflösen.
FriendlyId bietet darüber hinaus weitere Module, mit denen wir das Verhalten der Slugs an die Anforderungen einer Anwendung anpassen können.
Bestimmte Slugs reservieren
Mit dem Modul reserved können bestimmte Begriffe von der Verwendung als Slug ausgeschlossen werden.
Das ist beispielsweise für Begriffe sinnvoll, die bereits eine besondere Bedeutung innerhalb unserer Rails-Routen besitzen. Typische Beispiele sind:
new
edit
Dadurch können wir verhindern, dass ein Artikel einen Slug erhält, der mit einer vorhandenen Route kollidieren könnte.
Das Modul kann zusammen mit unseren bisherigen FriendlyId-Funktionen verwendet werden:
Welche Begriffe reserviert werden, lässt sich über die FriendlyId-Konfiguration festlegen.
Slugs auf einen bestimmten Bereich beschränken
Mit scoped können Slugs innerhalb eines bestimmten Zusammenhangs eindeutig gehalten werden, anstatt sie für die gesamte Tabelle eindeutig machen zu müssen.
Das ist beispielsweise praktisch, wenn Artikel zu unterschiedlichen Kategorien gehören und derselbe Slug innerhalb verschiedener Kategorien vorkommen darf.
Ein konzeptionelles Beispiel wäre:
news/hello-world
tutorials/hello-world
Beide Einträge verwenden hello-world, befinden sich jedoch in unterschiedlichen Bereichen.
Für solche Datenstrukturen kann FriendlyId das scoped-Modul verwenden. Dafür ist allerdings eine entsprechende Assoziation beziehungsweise Spalte im Datenmodell erforderlich.
Mehrsprachige Slugs
Auch bei mehrsprachigen Anwendungen kann FriendlyId erweitert werden. Abhängig von der eingesetzten FriendlyId-Version und der gewählten Internationalisierungslösung können Slugs für unterschiedliche Sprachen beziehungsweise Locales verwaltet werden.
Das ist beispielsweise interessant, wenn eine Anwendung ihre Inhalte in mehreren Sprachen anbietet und auch die URLs entsprechend lokalisiert werden sollen.
Fazit
Mit FriendlyId können wir die standardmäßigen numerischen Rails-URLs durch besser lesbare und aussagekräftigere Slugs ersetzen.
In unserer Blogging-Anwendung haben wir dazu:
FriendlyId installiert und in unser Article-Model eingebunden,
mit slugged URL-freundliche Slugs erzeugt,
die Artikel über Article.friendly.find anhand ihres Slugs geladen,
mit history frühere Slugs gespeichert und
alte URLs dauerhaft auf die aktuelle Artikel-URL weitergeleitet.
Damit erhalten wir URLs wie:
http://localhost:8080/articles/batman-robin-2
anstelle einer rein numerischen URL wie:
http://localhost:8080/articles/1
FriendlyId bietet darüber hinaus noch weitere Konfigurationsmöglichkeiten. Welche davon sinnvoll sind, hängt von der Struktur und den Anforderungen der jeweiligen Rails-Anwendung ab.
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.