Wir erstellen ein Kontaktformular

Wir schauen uns an, wie man in Nette ein Kontaktformular erstellt, einschließlich des E-Mail-Versands. Los geht's!

Zuerst müssen wir ein neues Projekt erstellen. Wie das geht, erklärt die Seite Erste Schritte. Und dann können wir mit der Erstellung des Formulars beginnen.

Am einfachsten ist die Erstellung eines Formulars direkt im Presenter. Wir können den vorbereiteten HomePresenter verwenden. In ihn fügen wir die Komponente contactForm hinzu, die das Formular darstellt. Dazu schreiben wir eine Factory-Methode createComponentContactForm() in den Code, die die Komponente erstellt:

use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'Name:')
			->setRequired('Geben Sie einen Namen ein');
		$form->addEmail('email', 'E-Mail:')
			->setRequired('Geben Sie eine E-Mail-Adresse ein');
		$form->addTextarea('message', 'Nachricht:')
			->setRequired('Geben Sie eine Nachricht ein');
		$form->addSubmit('send', 'Senden');
		$form->onSuccess[] = [$this, 'contactFormSucceeded'];
		return $form;
	}

	public function contactFormSucceeded(Form $form, $data): void
	{
		// E-Mail senden
	}
}

Wie Sie sehen, haben wir zwei Methoden erstellt. Die erste Methode createComponentContactForm() erstellt ein neues Formular. Dieses hat Felder für Name, E-Mail und Nachricht, die wir mit den Methoden addText(), addEmail() und addTextArea() hinzufügen. Wir haben auch einen Button zum Absenden des Formulars hinzugefügt. Aber was, wenn der Benutzer ein Feld nicht ausfüllt? In diesem Fall sollten wir ihm mitteilen, dass es sich um ein Pflichtfeld handelt. Das haben wir mit der Methode setRequired() erreicht. Schließlich haben wir auch das Ereignis onSuccess hinzugefügt, das ausgelöst wird, wenn das Formular erfolgreich gesendet wird. In unserem Fall ruft es die Methode contactFormSucceeded auf, die sich um die Verarbeitung des gesendeten Formulars kümmert. Das werden wir gleich in den Code einfügen.

Die Komponente contactForm lassen wir im Template Home/default.latte rendern:

{block content}
<h1>Kontaktformular</h1>
{control contactForm}

Für den eigentlichen E-Mail-Versand erstellen wir eine neue Klasse, die wir ContactFacade nennen und in der Datei app/Model/ContactFacade.php ablegen:

<?php
declare(strict_types=1);

namespace App\Model;

use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$mail = new Message;
		$mail->addTo('admin@example.com') // Ihre E-Mail
			->setFrom($email, $name)
			->setSubject('Nachricht vom Kontaktformular')
			->setBody($message);

		$this->mailer->send($mail);
	}
}

Die Methode sendMessage() erstellt und sendet eine E-Mail. Sie verwendet dazu einen sogenannten Mailer, den sie als Abhängigkeit über den Konstruktor erhält. Lesen Sie mehr über das Senden von E-Mails.

Nun kehren wir zum Presenter zurück und vervollständigen die Methode contactFormSucceeded(). Diese ruft die Methode sendMessage() der Klasse ContactFacade auf und übergibt ihr die Daten aus dem Formular. Und wie erhalten wir das Objekt ContactFacade? Wir lassen es uns über den Konstruktor übergeben:

use App\Model\ContactFacade;
use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	public function __construct(
		private ContactFacade $facade,
	) {
	}

	protected function createComponentContactForm(): Form
	{
		// ...
	}

	public function contactFormSucceeded(stdClass $data): void
	{
		$this->facade->sendMessage($data->email, $data->name, $data->message);
		$this->flashMessage('Die Nachricht wurde gesendet');
		$this->redirect('this');
	}
}

Nachdem die E-Mail gesendet wurde, zeigen wir dem Benutzer noch eine sogenannte Flash-Nachricht an, die bestätigt, dass die Nachricht gesendet wurde, und leiten dann auf dieselbe Seite weiter (this), damit das Formular nicht durch Aktualisieren im Browser erneut gesendet werden kann.

So, und wenn alles funktioniert, sollten Sie in der Lage sein, eine E-Mail von Ihrem Kontaktformular zu senden. Herzlichen Glückwunsch!

HTML-Template für E-Mails

Bisher wird eine einfache Text-E-Mail gesendet, die nur die über das Formular gesendete Nachricht enthält. In der E-Mail können wir jedoch HTML verwenden und ihr Erscheinungsbild attraktiver gestalten. Wir erstellen dafür ein Template in Latte, das wir in app/Model/contactEmail.latte speichern:

<html>
	<title>Nachricht vom Kontaktformular</title>

	<body>
		<p><strong>Name:</strong> {$name}</p>
		<p><strong>E-Mail:</strong> {$email}</p>
		<p><strong>Nachricht:</strong> {$message}</p>
	</body>
</html>

Es bleibt übrig, ContactFacade anzupassen, damit dieses Template verwendet wird. Im Konstruktor fordern wir die Klasse LatteFactory an, die ein Objekt Latte\Engine, also den Latte-Template-Renderer, erstellen kann. Mit der Methode renderToString() rendern wir das Template in einen String, der erste Parameter ist der Pfad zum Template und der zweite sind die Variablen.

namespace App\Model;

use Nette\Bridges\ApplicationLatte\LatteFactory;
use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$latte = $this->latteFactory->create();
		$body = $latte->renderToString(__DIR__ . '/contactEmail.latte', [
			'email' => $email,
			'name' => $name,
			'message' => $message,
		]);

		$mail = new Message;
		$mail->addTo('admin@example.com') // Ihre E-Mail
			->setFrom($email, $name)
			->setHtmlBody($body);

		$this->mailer->send($mail);
	}
}

Die generierte HTML-E-Mail übergeben wir dann der Methode setHtmlBody() anstelle der ursprünglichen setBody(). Ebenso müssen wir den Betreff der E-Mail nicht in setSubject() angeben, da ihn die Bibliothek aus dem <title>-Element des Templates übernimmt.

Konfiguration

Im Code der Klasse ContactFacade ist immer noch unsere Administrator-E-Mail admin@example.com fest codiert. Es wäre besser, sie in die Konfigurationsdatei zu verschieben. Wie geht das?

Zuerst passen wir die Klasse ContactFacade an und ersetzen den String mit der E-Mail durch eine Variable, die über den Konstruktor übergeben wird:

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
		private string $adminEmail,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		// ...
		$mail = new Message;
		$mail->addTo($this->adminEmail)
			->setFrom($email, $name)
			->setHtmlBody($body);
		// ...
	}
}

Und der zweite Schritt ist die Angabe des Wertes dieser Variablen in der Konfiguration. In die Datei app/config/services.neon schreiben wir:

services:
	- App\Model\ContactFacade(adminEmail: admin@example.com)

Und das war's. Wenn es viele Einträge im Abschnitt services gäbe und Sie das Gefühl hätten, dass die E-Mail dazwischen untergeht, können wir sie zu einer Variablen machen. Wir ändern die Notation auf:

services:
	- App\Model\ContactFacade(adminEmail: %adminEmail%)

Und in der Datei app/config/common.neon definieren wir diese Variable:

parameters:
	adminEmail: admin@example.com

Und fertig!