5.5 CRUD - Create - Read - Update - Delete
Grundoperationen
Im letzten Abschnitt haben wir ein ActiveRecord-Model für den objektorientierten Zugriff auf unsere Datenbanktabelle bookmarks erstellt. Mit einem Objekt dieses Models können wir die vier Grundoperationen durchführen:
- Create
einen neuen Datensatz erstellen - Read
einen Datensatz lesen - Update
einen vorhandenen Datensatz ändern - Delete
einen Datensatz löschen
Bis jetzt haben wir all das nur in der ruby script/console eingesetzt. Wir möchten das jetzt auch in unserer Bookmarkverwaltung nutzen.
Zunächst sollen unsere Bookmarks, die wir bis jetzt im Controller manuell in einem Array gesetzt haben, aus der Datenbank gelesen werden. In den darauffolgenden Schritten wollen wir dann in der Lage sein, einen neuen Bookmark hinzuzufügen, einen vorhandenen zu ändern oder zu löschen. Dazu müssen wir die entsprechenden Funktionalitäten des Models sowohl im Controller als auch in den Views einsetzen.
Beginnen wir mit unserem Bookmarks-Controller:
Listing app/controllers/bookmarks_controller.rb
class BookmarksController < ApplicationController def index @bookmarks = ["http://www.rubyonrails.com", "http://www.ruby-lang.org"] end def edit end def new end end
Die index-Action
Als Erstes ersetzen wir das Array, das wir manuell mit zwei Bookmarks gesetzt haben, durch einen Model-Aufruf, um die vorhandenen Bookmarks aus der Datenbank auszulesen:
def index @bookmarks = Bookmark.find(:all) end
Objektattribute ausgeben
Diese Änderung bedingt, dass auch im View index.html.erb Änderungen vorgenommen werden müssen. Vorher haben wir einfach nur jedes einzelne Element des Arrays in dem View ausgegeben. Das Array @bookmarks enthält aber jetzt Objekte vom Typ Bookmark, das heißt, im View können die einzelnen Attribute der Objekte angezeigt werden. Wir entscheiden uns zunächst dafür, für jeden Bookmark die URL auszugeben:
Listing app/views/bookmarks/index.html.erb
<% @title = "liste" %> <h2>Liste der Favoriten</h2> <ul> <% @bookmarks.each do |bookmark|%> <li><%= h bookmark.url %></li> <% end %> </ul>
Schöner wäre es, wenn wir nicht nur die URL zu jedem Bookmark ausgeben würden, sondern wenn die URL auch anklickbar wäre. Dazu müssen wir die Ausgabe der URL aus unserem letzten Listing wie folgt anpassen:
<li> <a href = «%= h bookmark.url %>»<%= h bookmark.title%> </li>
link_to
Da der eingebettete Ruby-Code innerhalb des HTML-Codes nicht so gut aussieht und auch etwas umständlich zu tippen ist, stellt Rails uns ActionView-Helper zur Verfügung. ActionView-Helper sind Hilfsmethoden, mit denen wir HTML-Code generieren können. Den Link hätten wir auch mit dem Einsatz des ActionView-Helpers link_to realisieren können:
<li><%= link_to h(bookmark.title), h(bookmark.url) %></li>
Unsere neue Index-Seite können wir im Browser testen:
Abbildung Verlinkte Titel - index.html.erb
show
Neben der Anzeige des Titels unserer Bookmarks und der Verlinkung mit der jeweiligen URL könnten wir auch noch das Kommentarfeld und das Erstellungs- und Änderungsdatum unserer Bookmarks anzeigen. Da dafür auf der Index-Seite zu wenig Platz ist oder weil wir das auch einfach nicht auf der Index-Seite anzeigen möchten, erstellen wir für diese Zusatzinformationen eine Detailseite. Diese Detailseite wird üblicherweise show genannt. Das heißt, wir müssen im Bookmarks-Controller eine neue Action show hinzufügen, und zur Ausgabe müssen wir einen neuen View, show.html.erb, anlegen. Beginnen wollen wir mit dem Controller.
Die show-Action
Datensatz laden
Die Action show soll einen bestimmten Datensatz laden, der dann im View angezeigt werden soll. Es soll also ein konkretes Modelobjekt bzw. ein Datensatz geladen werden. Das können wir mit der Methode find realisieren, der wir die ID des Datensatzes, den wir anzeigen möchten, übergeben:
Bookmark.find(2)
Die Action show kennt die ID des Datensatzes aber nicht, weil diese über die URL übergeben wird:
http://localhost:3000/controller/action/id
Der Aufbau der URL wird durch eine Konvention in Rails bestimmt, über die Sie in Kapitel kap_actioncontroller ab Seite kap_actioncontroller mehr erfahren. Für uns ist jetzt nur wichtig, dass das so ist und dass wir den Parameter ID aus der URL auslesen müssen:
Bookmark.find(params[:id]
Daraus folgt unsere Action show:
Listing app/controllers/bookmarks_controller.rb
def show @bookmark = Bookmark.find(params[:id]) end
@bookmark
In der Instanzvariablen @bookmark steht dem View show.html.erb dann der Datensatz zur Verfügung, dessen ID über die URL an die Action show übergeben wurde.
Als Nächstes erstellen wir also im Verzeichnis app/views/bookmarks eine neue Datei show.html.erb, in der wir den Titel, die URL, den Kommentar, das Erstellungs- und das Änderungsdatum eines Bookmarks ausgeben:
Listing app/views/bookmarks/show.html.erb
<h2>Detail zum Favorit</h2> <p>Titel: <%= h @bookmark.title %></p> <p>URL: <%= h @bookmark.url %></p> <p>Kommentar: <%= h @bookmark.comment %></p> <p>Erstellt am: <%= @bookmark.created_at %></p> <p>Geändert am: <%= @bookmark.updated_at %></p>
Testen können Sie das durch Aufruf der URL:
http://localhost:3000/bookmarks/show/2
Abbildung http://localhost:3000/bookmarks/show/2
strftime
Um das Erstellungs- und Änderungsdatum im deutschen Datumsformat mit Angabe der Uhrzeit zu formatieren, können wir die Methode strftime nutzen:
<p> Erstellt am: <%= @bookmark.created_at.strftime("%d.%m.%Y %H:%M") %> </p> <p> Geändert am: <%= @bookmark.updated_at.strftime("%d.%m.%Y %H:%M") %> </p>
Abbildung http://localhost:3000/bookmarks/show/2
Was uns jetzt noch fehlt, ist ein Link von der Index-Seite auf die Detailseiten der einzelnen Bookmarks und von der Detailseite ein Link zurück zur Index-Seite. Dazu müssen wir in den Views index.html.erb und show.html.erb einen Link hinzufügen:
Listing app/views/bookmarks/index.html.erb
<% @title = "liste" %> <h2>Liste der Favoriten</h2> <ul> <% @bookmarks.each do |bookmark|%> <li> <%= link_to h(bookmark.title), h(bookmark.url) %> (<%= link_to "Details", :action => "show", :id => bookmark.id %>) </li> <% end %> </ul>
Listing app/views/bookmarks/show.html.erb
... <p><%= link_to "Zurück zur Liste", :action => "index" %></p>
Abbildung http://localhost:3000/bookmarks
Formular zur Erstellung eines neuen Datensatzes
new
Jetzt erstellen wir ein Formular, um neue Bookmarks hinzufügen zu können. Dazu steht uns im Controller die Action new zur Verfügung. In dieser Action wird ein neues, leeres Bookmark-Objekt erstellt, das wir im View über das Formular mit Werten füllen können:
def new @bookmark = Bookmark.new end
form_for
In dem View app/views/bookmarks/new.html.erb werden wir das Formular zum Anlegen eines neuen Bookmarks hinterlegen. Auch zur Formularentwicklung stellt Rails einen ActionView-Helper zur Verfügung: form_for . Bei form_for handelt es sich um einen Block, dem Sie übergeben, auf welches Objekt sich die Felder des Formulars beziehen, in unserem Fall ist das das Bookmark-Objekt, das beim Aufruf des Formulars in der Action new erzeugt wurde, und an welche Action die Formularwerte geschickt werden, in unserem Fall ist das die Action create, die wir nach der Erstellung des Formulars noch im Controller entwickeln müssen:
Listing app/views/bookmarks/new.html.erb
<% form_for :bookmark, :url => {:action => "create"} do |f| %> <% end %>
Formular Helper
In dem Formular fehlen jetzt noch die einzelnen Felder. Hier legen Sie alle die Felder an, die in der Datenbanktabelle bookmarks gesetzt werden können. Das heißt, alle außer id, created_at und updated_at, weil diese automatisch von Rails gesetzt werden. Wichtig ist, dass die Felder genauso heißen wie in der Datenbanktabelle. Auch für die Erzeugung der einzelnen Formularelemente stehen Helper zur Verfügung: text_field, text_area und submit_tag . Unterhalb des Formulars fügen wir noch einen Link zurück zur Index-Seite ein:
<% @title = "neu" %> <h2>Neuen Favorit erstellen</h2> <% form_for :bookmark, :url => {:action => "create"} do |f| %> <p> <label for="bookmark_title">Titel</label> <%= f.text_field :title %> </p> <p> <label for="bookmark_url">URL</label> <%= f.text_field :url %> </p> <p> <label for="bookmark_comment">Kommentar</label> <%= f.text_area :comment %> </p> <p> <%= submit_tag "speichern" %> </p> <% end %> <p><%= link_to "Zurück zur Liste", :action => "index" %></p>
Formular aufrufen
Über die URL http://localhost:3000/bookmarks/new können Sie das Formular im Browser aufrufen:
Abbildung http://localhost:3000/bookmarks/new
Abschicken können Sie es noch nicht, weil wir im Controller die Action create noch nicht definiert haben.
create
Die Action create soll aus den Formularwerten ein neues Bookmark-Objekt erzeugen und speichern, um in der Datenbank einen neuen Datensatz zu erzeugen. Da die Formularfelder genauso heißen wie in der Datenbank, können wir sie als Hash übergeben. In Rails liest man die Parameter eines Formulars über params[:Formularname] . Daraus folgt für unsere Action create:
def create @bookmark = Bookmark.new(params[:bookmark]) end
save
Das Objekt @bookmark muss dann noch gespeichert werden. Da die Methode save entweder true oder false zurückliefert, können wir das auch abfragen und entsprechend reagieren. Wenn das Speichern erfolgreich war, soll zur Index-Seite weitergeleitet werden. Wenn der Datensatz nicht gespeichert werden kann, zum Beispiel weil nicht alle Pflichtfelder gesetzt sind, soll wieder der View new.html.erb angezeigt werden, aber die Formularfelder, die ausgefüllt waren, sollen erhalten bleiben. Das heißt, in diesem Fall erfolgt keine Weiterleitung, sondern es wird wieder das Formular, also der View new.html.erb, angezeigt, ohne die Seite neu zu laden:
def create @bookmark = Bookmark.new(params[:bookmark]) if @bookmark.save redirect_to :action => "index" else render :action => "new" end end
Das können Sie jetzt testen, indem Sie einmal ein vollständig ausgefülltes Formular abschicken und einmal nur den Titel ausfüllen und dann das Formular abschicken.
Formular zum Ändern eines Datensatzes
edit
Das Ändern eines vorhandenen Bookmarks ist relativ einfach. Dazu nutzen wir die Action edit im Controller. edit ist ähnlich wie new . Der Unterschied besteht darin, dass edit einen vorhandenen Bookmark lädt, dessen ID wie bei der Action show über die URL übergeben wird:
def edit @bookmark = Bookmark.find(params[:id]) end
Das Formular für den View edit.html.erb übernehmen wir von dem View new.html.erb . Wir werden später zeigen, wie man diese Duplizierung vermeiden kann. Das Formular zum Ändern eines Bookmarks senden wir aber nicht an die Action create, sondern an die Action update, die wir noch erstellen müssen, und wir übergeben die ID des Bookmarks, der editiert werden soll, an die Action update:
Listing app/views/bookmarks/edit.html.erb
<% @title = "bearbeiten" %>
<h2>Favorit bearbeiten</h2>
<% form_for :bookmark, :url => {:action => "update",
:id => @bookmark.id} do |f| %>
<p>
<label for="bookmark_title">Titel</label>
<%= f.text_field :title %>
</p>
<p>
<label for="bookmark_url">URL</label>
<%= f.text_field :url %>
</p>
<p>
<label for="bookmark_comment">Kommentar</label>
<%= f.text_area :comment %>
</p>
<p>
<%= submit_tag "speichern" %>
</p>
<% end %>
<p><%= link_to "Zurück zur Liste", :action => "index" %></p>
update
Damit wir das testen können, müssen wir noch im Controller die Action update entwickeln. Die Action update lädt über die übergebene ID den zu editierenden Bookmark, und dann ändert sie alle Werte, die über das Formular übergeben werden über die Methode update_attributes . Da diese Methode true oder false zurückliefert, können wir darauf abfragen und entsprechend reagieren. Wenn der Datensatz erfolgreich upgedatet werden konnte, soll zur Index-Seite weitergeleitet werden. Wenn nicht, soll das Formular zum Ändern geladen werden:
def update @bookmark = Bookmark.find(params[:id]) if @bookmark.update_attributes(params[:bookmark]) redirect_to :action => "index" else render :action => "edit" end end
Testen können Sie das über den Aufruf der URL:
http://localhost:3000/bookmarks/edit/1
Abbildung http://localhost:3000/bookmarks/edit/1
Links anlegen
Was uns noch fehlt, sind Links von der Index-Seite aus, um neue Bookmarks anzulegen, vorhandene zu editieren oder auch zu löschen (wenn wir auch das Löschen noch nicht programmiert haben), damit wir die jeweiligen URLs nicht immer händisch im Browser eingeben müssen. Dazu müssen die entsprechenden Links in der index.html.erb hinzugefügt werden:
Listing app/views/bookmarks/index.html.erb
<% @title = "liste" %> <h2>Liste der Favoriten</h2> <ul> <% @bookmarks.each do |bookmark|%> <li> <%= link_to h(bookmark.title), h(bookmark.url) %> (<%= link_to "Details", :action => "show", :id => bookmark.id %> | <%= link_to "ändern", :action => "edit", :id => bookmark.id %> | <%= link_to "löschen", :action => "destroy", :id => bookmark.id %> ) </li> <% end %> </ul> <p> <%= link_to "Neuen Favorit erstellen", :action => "new" %> </p>
Abbildung Die Linksammlung
:confirm
Um ein versehentliches Löschen eines Bookmarks zu verhindern, ist es sinnvoll, eine JavaScript-Abfrage vorzuschalten. Dazu müssen wir im link_to -Helper in der index.html.erb noch eine weitere Option hinzufügen:
<%= link_to "löschen", {:action => "destroy", :id => bookmark.id}, :confirm => "Wollen Sie diesen Datensatz wirklich löschen?" %>
Löschen von Datensätzen
destroy
Jetzt müssen wir nur noch die Action destroy im Controller implementieren. Zuerst muss über den übergebenen Parameter id ein konkreter Bookmark geladen werden, der dann gelöscht wird. Unsere Action destroy soll keinen eigenen View erhalten, weil das wenig sinnvoll ist. Nach dem Löschen soll eine Weiterleitung zur Index-Seite erfolgen:
def destroy @bookmark = Bookmark.find(params[:id]) @bookmark.destroy redirect_to :action => "index" end
Testen können Sie das über den bereits angelegten Link zum Löschen auf der Index-Seite.
Am Beispiel der Bookmarkverwaltung haben Sie bis jetzt die Grundoperationen kennengelernt, um Objekte anzulegen, zu lesen, zu ändern und zu löschen. Wir haben im Controller die entsprechenden Actions angelegt und die erforderlichen Views erstellt.
Resource- und Scaffold-Generatoren
Da das eine Anforderung ist, die immer wieder vorkommt, gibt es in Rails Generatoren, die automatisch die Controller mit den erforderlichen Actions anlegen und die dazugehörigen Views erstellen. Wir haben es in diesem Beispiel manuell gemacht, um zu zeigen, wie das im Einzelnen funktioniert und was letztendlich hinter den Generatoren steckt, wenn wir diese in den nächsten Beispielen einsetzen.
Über den Generator
ruby script/generate resource Modelname Feldname:Feldtyp Feldname:Feldtyp ...
werden das Model, die Migration-Datei und der Controller angelegt.
Über den Generator
ruby script/generate scaffold Modelname Feldname:Feldtyp Feldname:Feldtyp ...
werden das Model, die Migration-Datei, der Controller mit den erforderlichen sieben Actions für CRUD und die dazugehörigen Views angelegt.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.