11 E-Mails verwalten mit ActionMailer
Jede Web-Applikation muss E-Mails versenden können. Sei es eine E-Mail aus einem Kontaktformular heraus oder die Bestätigungs-E-Mail zu einem Buchungsvorgang.
Features
ActionMailer ist ein Bestandteil des Rails-Framework und bietet einige Features:
- Versenden von E-Mails
als Text- oder als HTML-E-Mail. Beides gleichzeitig geht auch, dann kann der Mail-Client entscheiden, welches Format er anzeigt.
- Versenden von Dateianhängen
ist mit ActionMailer sogar relativ einfach zu realisieren.
11.1 Beispielprojekt: Kontaktformular 

Beispiel
Die Funktionsweise von ActionMailer zum Versenden von E-Mails wollen wir anhand eines Kontaktformulars zeigen. Der Kunde soll seine Daten und seine Nachricht in das Formular eingeben können, beim Absenden des Formulars soll die Nachricht an den Webseitenbetreiber geschickt werden und der Kunde soll eine Bestätigungs-E-Mail erhalten. Es sollen also zwei E-Mails mit unterschiedlichen Inhalten versendet werden.
Unsere Applikation soll contact heißen. Obwohl wir für ein Kontaktformular eigentlich keine Datenbank benötigen, sondern nur die E-Mail-Funktion, nutzen wir trotzdem eine Datenbank, weil es in Rails viel einfacher ist, wenn ein datenbankbasiertes Model dahinter steht.
Als Datenbanksystem werden wir in diesem Fall SQLite3 verwenden, da es den Vorteil bietet, das wir keine Datenbank anlegen müssen, weil SQLite3 dateibasiert ist und die Dateien automatisch angelegt werden.
Sie können natürlich auch MySQL verwenden, müssen dann aber die entsprechende Datenbank anlegen.
Mehr zum Thema Datenbanken erfahren Sie in Kapitel 10.
Projekt generieren
Wir generieren unser Beispielprojekt mit dem Befehl:
rails contact cd contact
Wir benötigen einen Controller, Views und ein Model, um unser Kontaktformular nutzen zu können. Da man eine Nachricht über ein Kontaktformular auch als eine Ressource betrachten kann (man kann neue Nachrichten hinzufügen, anzeigen, bearbeiten, und löschen), werden wir den scaffold -Generator zum Erzeugen einer Resource nutzen, der einiges an Code automatisch generiert und uns damit die Arbeit erleichtert.
Benötigte Felder
Unser Model soll contact_message heißen, und der Einfachheit halber wollen wir nur die Felder name, email für die E-Mail-Adresse und message für die Nachricht nutzen. Die Felder name und email sind vom Typ string und das Feld message ist vom Typ text, da es möglich sein soll mehr als 256 Zeichen in
Scaffold
einer Nachricht zu speichern. Wir wechseln also in das Projektverzeichnis contact und führen folgenden Befehl aus:ruby script/generate scaffold contact_message name:string \ email:string message:text
Der Generator hat automatisch die Model-Datei, die Controller-Datei, die Views und die Migration-Datei erstellt.
Migration
Sollten wir doch noch weitere Felder benötigen, könnten wir jetzt die Migration-Datei öffnen und weitere Felder hinzufügen, ansonsten führen wir die Migration mit dem Befehl rake db:migrate aus.
Test im Browser
Wir können unsere Applikation im Browser testen. Dazu starten wir in der Konsole den lokalen Rails-Server mit dem Befehl
ruby script/server
und rufen über http://localhost:3000/contact_messages unsere Applikation im Browser auf:
Abbildung E-Mail-Applikation
Neue Nachricht anlegen
Über den Link »New contact message« gelangen wir zum Formular:
Abbildung Neue Nachricht verfassen
Nachricht anzeigen
Wenn wir das Formular absenden, wird uns die neue Nachricht angezeigt:
Abbildung So sieht die Nachricht aus
Alle Nachrichten
Über den Zurück-Link gelangen wir zur Übersicht aller Nachrichten:
Abbildung Liste aller Nachrichten
Was wir erst einmal nicht benötigen, ist, dass eine Liste aller angelegten Nachrichten ausgegeben wird, und wir benötigen auch keine Funktion zum Editieren und Löschen von Nachrichten. Das könnte man alles nutzen, wenn man die Nachrichten verwalten möchte, aber für uns sind das erst einmal überflüssige Funktionen bzw. Actions im Controller.
Benötigte Actions
Wir benötigen nur die Actions
- new
stellt das Formular zum Anlegen einer Nachricht zur Verfügung.
- create
speichert die neue Nachricht in der Datenbank. Hier werden wir auch das Versenden der E-Mails hinzufügen.
- show
zeigt dem Kunden die Zusammenfassung seiner gesendeten Nachricht an.
Nicht benötigte Actions
Die anderen Actions können wir aus dem Controller löschen. Dazu öffnen wir das Projekt in einem Editor und löschen die Actions index, edit, update und destroy aus der Datei contact_messages_controller.rb im Verzeichnis app/controllers . Wir können auch die entsprechenden View-Dateien index.html.erb und edit.html.erb aus dem Verzeichnis app/views/contact_messages löschen. In den verbleibenden beiden View-Dateien new.html.erb und show.html.erb können wir die Links entfernen.
mailer
Nun können wir eine neue Kontaktnachricht über das Formular anlegen, aber es wird noch keine E-Mail verschickt. Um E-Mails versenden zu können, setzen wir den mailer -Generator ein, der uns neben Testdateien ein Model generiert, das für das Versenden der E-Mails zuständig ist, und Template-Dateien, in denen wir die Texte für die E-Mails hinterlegen können.
Das von dem Generator mailer erzeugte Model wird kein datenbankbasiertes Model sein, das von ActiveRecord erbt, so wie das Model ContactMessage für das Kontaktformular. Das für den Mailversand zuständige Model wird von ActionMailer erben.
Generator einsetzen
Der mailer -Generator erwartet einen Namen und eine Liste von Views für die einzelnen E-Mails die versendet werden sollen, als Parameter:
ruby script/generate mailer MailerName view1 view2
Unser mailer soll contact_mailer heißen und die Views für die beiden zu versendenden E-Mails confirmation und message:
ruby script/generate mailer contact_mailer confirmation message
Es werden folgende Dateien erzeugt:
exists app/models/ create app/views/contact_mailer exists test/unit/ create test/fixtures/contact_mailer create app/models/contact_mailer.rb create test/unit/contact_mailer_test.rb create app/views/contact_mailer/confirmation.erb create test/fixtures/contact_mailer/confirmation create app/views/contact_mailer/message.erb create test/fixtures/contact_mailer/message
Erzeugte Dateien
Neben diversen Testdateien werden die Model-Datei contact_mailer.rb im Verzeichnis app/models und die beiden Templates confirmation.erb und message.erb im Verzeichnis app/views/contact_mailer erzeugt. Das Template confirmation.erb werden wir nutzen, um eine Bestätigungs-E-mail an den Kunden zu senden, und das Template message.erb, um eine Zusammenfassung der Daten an den Webseitenbetreiber zu schicken.
Bestätigungs- E-Mail
Zunächst möchten wir die Bestätigungs-E-mail für den Kunden anpassen. Wir wählen einen ganz einfachen Standardtext und geben diesen in die confirmation.erb ein:
Listing app/views/contact_mailer/confirmation.erb
Vielen Dank für Ihre Nachricht. Wir werden uns so schnell wie möglich bei Ihnen melden.
Das Model ContactMailer erbt, wie bereits erwähnt, im Gegensatz zum Model ContactMessage von ActionMailer::Base und enthält die beiden Methoden confirmation und message:
Listing app/models/contact_mailer.rb
class ContactMailer < ActionMailer::Base def confirmation(sent_at = Time.now) @subject = 'ContactMailer#confirmation' @body = {} @recipients = '' @from = '' @sent_on = sent_at @headers = {} end def message(sent_at = Time.now) @subject = 'ContactMailer#message' @body = {} @recipients = '' @from = '' @sent_on = sent_at @headers = {} end end
Ein Mailer-Model enthält immer so viele Methoden, wie es E-Mail-Templates gibt, die auch so heißen, wie die Templates.
Konfiguration
Den beiden Methoden können Sie über den optionalen Parameter sent_at den Zeitpunkt übergeben, an dem die E-Mail versendet werden soll. Da dieser Parameter optional ist, können Sie ihn auch weglassen. Standardmäßig wird der Wert von sent_at auf Time.now gesetzt, was bedeutet, dass die E-Mail zum Zeitpunkt des Aufrufs der Methode versendet wird.
Darüber hinaus können Sie in den Methoden innerhalb des Mailer-Models folgende Variablen setzen:
- @subject
Betreff der E-Mail
- @body
Hier können Sie den Text eingeben, wenn Sie eine Textmail versenden und den Text nicht in das ActionView-Template eingeben. Oder Sie können über @body Variablen in Form eines Hashs an das Action View-Template übergeben.
- @recipients
Eine oder mehrere Empfänger-Adressen der E-Mail. Bei nur einem Empfänger wird die eine E-Mail-Adresse als String übergeben, bei mehreren Empfängern wird ein Array mit den einzelnen E-Mail-Adressen übergeben.
- @from
Absender-Adresse der E-Mail
- @sent_on
Zeitpunkt zu dem die E-Mail versendet werden soll. Wird auf den Wert des optionalen Parameters sent_at gesetzt.
- @headers
Optionale Variable. Hier können Header-Informationen der E-Mail als Hash übergeben werden.
In unserem Beispiel haben wir die Werte in der Methode confirmation wie folgt gesetzt:
def confirmation(sent_at = Time.now) @subject = 'Ihre Kontaktnachricht erhalten' @body = {} @recipients = 'tanja@test.lu' @from = 'info@railsbuch.de' @sent_on = sent_at @headers = {} end
Standardtext
Da wir einen Standardtext für die Bestätigungs-E-mail an die Kunden im Template confirmation.erb definiert haben und auch immer nur diesen Standardtext versenden möchten, benötigen wir keine Variablen innerhalb unseres Templates. Deshalb lassen wir die Variable @body leer. Der Einfachheit halber haben wir im ersten Schritt die E-Mail-Adresse für den Empfänger der E-Mail statisch eingesetzt. Im nächsten Schritt werden wir zeigen, wie Sie die E-Mail-Adresse aus dem Formularfeld an die Methode übergeben können. Wir wollen keine besonderen Header-Informationen übergeben, deshalb lassen wir die Variable @headers auch leer.
Zeitpunkt zum Versenden der E-Mail
Wir möchten unsere Bestätigungs-E-mail dann versenden, wenn ein Kunde das Kontaktformular ausgefüllt und abgeschickt hat und die Nachricht in der Datenbank gespeichert werden konnte. Das entspricht im ContactMessagesController in der Action create der Stelle, an der abgefragt wird, ob das Speichern erfolgreich war. Innerhalb dieser Abfrage müssen wir das Model ContactMailer aufrufen, um die Bestätigungs-E-mail zu versenden. Allerdings rufen wir dazu nicht einfach die Methode confirmation aus dem Model auf, sondern per Konvention wird dem Methodennamen ein deliver_ vorangestellt, so dass wir die Methode deliver_confirmation aufrufen müssen:
Listing app/controllers/contact_messages_controller.rb
def create
@contact_message = ContactMessage.new(
params[:contact_message])
respond_to do |format|
if @contact_message.save
ContactMailer.deliver_confirmation
flash[:notice] = 'ContactMessage was successfully
created.'
format.html { redirect_to contact_message_url(
@contact_message) }
format.xml { head :created, :location =>
contact_message_url(@contact_message) }
else
format.html { render :action => "new" }
format.xml { render :xml =>
@contact_message.errors.to_xml }
end
end
end
Da wir keine XML-Anfrage bearbeiten möchten, können wir die Methode create wie folgt vereinfachen:
Listing app/controllers/contact_messages_controller.rb
def create @contact_message = ContactMessage.new( params[:contact_message]) if @contact_message.save ContactMailer.deliver_confirmation flash[:notice] = 'Vielen Dank für Ihre Nachricht.' redirect_to(@contact_message) else render :action => "new" end end
Wenn wir jetzt den lokalen Rails-Server erneut starten und unser Kontaktformular ( http://localhost:3000/contact_messages/new ) ausfüllen und absenden, können wir nicht sehen, ob die E-Mail versendet wurde, da in der Entwicklungsumgebung normalerweise keine E-Mails versendet werden.
Kontrolle mit Hilfe der Konsole
Aber in dem Konsole-Fenster, in dem wir den lokalen Rails-Server mit ruby script/server gestartet haben, wird alles protokolliert. Zum einen finden wir hier den INSERT -Befehl, der die Nachricht in der Datenbank gespeichert hat, und wir finden hier einen Eintrag Sent mail, innerhalb dem protokolliert wurde, an wen welche E-Mail mit welchem Betreff und Text versendet wurde:
INSERT INTO contact_messages ("message", "name", "updated_at", "created_at", "email") VALUES('Das ist ein Test', 'Tanja', '2008-01-08 21:33:20', '2008-01-08 21:33:20', 'tanja@test.lu') Sent mail: Date: Tue, 8 Jan 2008 21:33:20 +0100 From: info@railsbuch.de To: tanja@test.lu Subject: Ihre Kontaktnachricht erhalten Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Vielen Dank für Ihre Nachricht. Wir werden uns so schnell wie möglich bei Ihnen melden.
Sollten die Umlaute innerhalb des Konsole-Fensters nicht richtig dargestellt werden, muss Sie das nicht beunruhigen. Das passiert nur in der Konsole. Innerhalb der E-Mail die versendet wird, werden alle Zeichen korrekt dargestellt.
Da wir in der Variablen @recipients innerhalb der Methode confirmation eine statische E-Mail-Adresse angegeben haben, wird unsere Bestätigungs-E-mail immer an die gleiche Adresse gesendet. Sie soll aber an die E-Mail-Adresse, die im Kontaktformular eingegeben wurde gesendet werden.
Parameter übergeben
Wir können die E-Mail-Adresse aus dem Formular über einen Parameter in die Methode confirmation übergeben. Im ContactMessagesController steht uns das Objekt @contact_message zur Verfügung. Dieses Objekt verfügt über das Feld »email« (welche Felder zur Verfügung stehen, können Sie in der Datei db/schema.rb nachschauen), auf das wir zugreifen können.
Wir müssen nur den Parameter, über den wir die E-Mail-Adresse übergeben, in der Methode confirmation hinzufügen und ihn innerhalb der Methode an die Variable @recipients übergeben.
Listing app/controllers/contact_messages_controller.rb
def create
@contact_message = ContactMessage.new(
params[:contact_message])
if @contact_message.save
ContactMailer.deliver_confirmation(@contact_message.email)
flash[:notice] = 'Vielen Dank für Ihre Nachricht.'
redirect_to(@contact_message)
else
render :action => "new"
end
end
Parameter empfangen
Die übergebene E-Mail-Adresse empfangen wir in der Methode confirmation im Model ContactMailer über den Parameter email, der innerhalb der Methode an die Variable @recipients übergeben wird:
Listing app/models/contact_mailer.rb
def confirmation(email, sent_at = Time.now) @subject = 'Ihre Kontaktnachricht erhalten' @body = {} @recipients = email @from = 'info@railsbuch.de' @sent_on = sent_at @headers = {} end
Wenn Sie jetzt einen erneuten Test durchführen, sehen Sie in der Ausgabe im Konsole-Fenster, dass die E-Mail-Adresse aus dem Formular übernommen wurde:
Abbildung Test des Formulars
INSERT INTO contact_messages ("message", "name", "updated_at",
"created_at", "email") VALUES('Meine erste Nachricht',
'Tanja', '2008-01-08 21:44:33', '2008-01-08 21:44:33',
'tanja@railsbuch.de')
Sent mail:
Date: Tue, 8 Jan 2008 21:44:33 +0100
From: info@railsbuch.de
To: tanja@railsbuch.de
Subject: Ihre Kontaktnachricht erhalten
Mime-Version: 1.0
Content-Type: text/plain; charset=utf-8
Vielen Dank für Ihre Nachricht.
Wir werden uns so schnell wie möglich bei Ihnen melden.
Es soll auch eine E-Mail mit der Zusammenfassung aller Daten aus dem Kontaktformular an den Webseitenbetreiber gesendet werden. Für das Versenden dieser E-Mail haben wir im Model ContactMailer die Methode message und das entsprechende Template message.erb angelegt.
@body
Da wir in dieser E-Mail alle Daten ausgeben möchten, Name, E-Mail und die Nachricht selbst, ist es erforderlich, dass wir diese Werte aus dem Formular in der E-Mail andrucken. Das heißt, wir müssen die Werte aus dem ContactMessagesController an das Template, in dem der Text für die E-Mail generiert wird, übergeben. Innerhalb der Methode message steht uns dazu die Varibale @body zur Verfügung. Das heißt, die Werte müssen aus dem Controller in die Methode übergeben werden und innerhalb der Methode an die Variable @body.
Objekt übergeben
Da wir im Gegensatz zu der Bestätigungs-E-mail an den Kunden, diesmal alle Werte an die Methode deliver_message übergeben müssen, übergeben wir nicht mehr einzelne Werte, sondern das ganze Objekt @contact_message aus dem ContactMessagesController:
Listing app/controllers/contact_messages_controller.rb
def create
@contact_message = ContactMessage.new(
params[:contact_message])
if @contact_message.save
ContactMailer.deliver_confirmation(@contact_message.email)
ContactMailer.deliver_message(@contact_message)
flash[:notice] = 'Vielen Dank für Ihre Nachricht.'
redirect_to(@contact_message)
else
render :action => "new"
end
end
Die Methode message empfängt das übergebene Objekt in dem Parameter contact_message und übergibt diesen Parameter in einem Hash an die Variable @body:
Listing app/models/contact_mailer.rb
def message(contact_message, sent_at = Time.now) @subject = 'Neue Kontaktanfrage' @body = {:contact_message => contact_message} @recipients = 'info@railsbuch.de' @from = contact_message.email @sent_on = sent_at @headers = {} end
E-Mail-Text
Nachdem auch die anderen Variablen in der Methode message gesetzt sind, können wir den E-Mail-Text im Template message.erb gestalten. Da wir das Objekt contact_message in der Variablen @body übergeben haben, steht es uns im Template als Instanzvariable @contact_message zur Verfügung:
Listing app/views/contact_mailer/message.erb
Name: <%= @contact_message.name %> E-Mail: <%= @contact_message.email %> Nachricht: <%= @contact_message.message %>
Wenn wir jetzt einen erneuten Test durchführen, sehen wir im Konsole-Fenster zwei Sent mail -Einträge. Einen für unsere Bestätigungs-E-mail an den Kunden und einen für die Zusammenfassung an den Webseitenbetreiber:
INSERT INTO contact_messages ("message", "name", "updated_at", "created_at", "email") VALUES('Meine erste Nachricht', 'Tanja', '2008-01-08 21:54:06', '2008-01-08 21:54:06', 'tanja@railsbuch.de') Sent mail: Date: Tue, 8 Jan 2008 21:54:06 +0100 From: info@railsbuch.de To: tanja@railsbuch.de Subject: Ihre Kontaktnachricht erhalten Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Vielen Dank für Ihre Nachricht. Wir werden uns so schnell wie möglich bei Ihnen melden. Sent mail: Date: Tue, 8 Jan 2008 21:54:06 +0100 From: tanja@railsbuch.de To: info@railsbuch.de Subject: Neue Kontaktanfrage Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Name: Tanja E-Mail: tanja@railsbuch.de Nachricht: Meine erste Nachricht
In Rails kann man also relativ einfach E-Mails verschicken. Durch Einsatz des mailer -Generators, dem der Name des Models und die einzelnen Views übergeben werden müssen, wird uns sehr viel Arbeit abgenommen. Der Generator erzeugt neben Testdateien das Mailer-Model mit den erforderlichen Methoden und die View-Dateien, die wir nur noch konfigurieren und an unsere Bedürfnisse anpassen müssen. Das Versenden erfolgt dann einfach durch Aufruf des Mailer-Models mit der Methode deliver_Methodenname, der wir eventuell in dem entsprechenden Controller individuelle Parameter übergeben können.
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.