Kommentare

Wir haben den Blog auf den Webserver hochgeladen und einige sehr interessante Beiträge mit Adminer veröffentlicht. Die Leute lesen unseren Blog und sind sehr begeistert davon. Wir erhalten jeden Tag viele E-Mails mit Lob. Aber was nützt all dieses Lob, wenn wir es nur per E-Mail haben und niemand es lesen kann? Es wäre besser, wenn der Leser den Artikel direkt kommentieren könnte, damit jeder lesen kann, wie großartig wir sind.

Programmieren wir also Kommentare.

Erstellen einer neuen Tabelle

Wir starten Adminer und erstellen eine Tabelle comments mit diesen Spalten:

  • id int, Autoincrement (AI) ankreuzen
  • post_id, Fremdschlüssel, der auf die Tabelle posts verweist
  • name varchar, Länge 255
  • email varchar, Länge 255
  • content text
  • created_at timestamp

Die Tabelle sollte also ungefähr so aussehen:

Vergessen Sie nicht, wieder den InnoDB-Speicher zu verwenden.

CREATE TABLE `comments` (
	`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`post_id` int(11) NOT NULL,
	`name` varchar(250) NOT NULL,
	`email` varchar(250) NOT NULL,
	`content` text NOT NULL,
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
	FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
) ENGINE=InnoDB CHARSET=utf8;

Kommentarformular

Zuerst müssen wir ein Formular erstellen, das es Benutzern ermöglicht, Beiträge zu kommentieren. Das Nette Framework bietet eine erstaunliche Unterstützung für Formulare. Wir können sie im Presenter konfigurieren und im Template rendern.

Das Nette Framework verwendet das Konzept der Komponenten. Eine Komponente ist eine wiederverwendbare Klasse oder ein Codeabschnitt, der an eine andere Komponente angehängt werden kann. Sogar ein Presenter ist eine Komponente. Jede Komponente wird über eine Factory erstellt. Erstellen wir also eine Factory für das Kommentarformular im Presenter PostPresenter.

protected function createComponentCommentForm(): Form
{
	$form = new Form; // bedeutet Nette\Application\UI\Form

	$form->addText('name', 'Name:')
		->setRequired();

	$form->addEmail('email', 'E-Mail:');

	$form->addTextArea('content', 'Kommentar:')
		->setRequired();

	$form->addSubmit('send', 'Kommentar veröffentlichen');

	return $form;
}

Lassen Sie uns das wieder ein wenig erklären. Die erste Zeile erstellt eine neue Instanz der Komponente Form. Die folgenden Methoden fügen HTML-Inputs zur Definition dieses Formulars hinzu. ->addText wird als <input type="text" name="name"> mit <label>Name:</label> gerendert. Wie Sie wahrscheinlich schon richtig erraten haben, wird ->addTextArea als <textarea> und ->addSubmit als <input type="submit"> gerendert. Es gibt noch viel mehr ähnliche Methoden, aber diese reichen vorerst für dieses Formular aus. Mehr dazu finden Sie in der Dokumentation.

Wenn das Formular bereits im Presenter definiert ist, können wir es im Template rendern (anzeigen). Dazu platzieren wir das Tag {control} am Ende des Templates, das einen bestimmten Beitrag rendert, in Post/show.latte. Da die Komponente commentForm heißt (der Name leitet sich vom Namen der Methode createComponentCommentForm ab), sieht das Tag wie folgt aus:

...
<h2>Neuen Kommentar einfügen</h2>

{control commentForm}

Wenn Sie nun die Seite mit den Beitragsdetails anzeigen, sehen Sie am Ende ein neues Formular für Kommentare.

Speichern in der Datenbank

Haben Sie schon versucht, das Formular auszufüllen und abzusenden? Sie haben wahrscheinlich bemerkt, dass das Formular eigentlich nichts tut. Wir müssen eine Callback-Methode anhängen, die die gesendeten Daten speichert.

Fügen Sie die folgende Zeile in die Factory für die Komponente commentForm vor return ein:

$form->onSuccess[] = $this->commentFormSucceeded(...);

Die vorherige Anweisung bedeutet “rufe nach erfolgreichem Absenden des Formulars die Methode commentFormSucceeded des aktuellen Presenters auf”. Diese Methode existiert jedoch noch nicht. Erstellen wir sie also:

private function commentFormSucceeded(\stdClass $data): void
{
	$id = $this->getParameter('id');

	$this->database->table('comments')->insert([
		'post_id' => $id,
		'name' => $data->name,
		'email' => $data->email,
		'content' => $data->content,
	]);

	$this->flashMessage('Vielen Dank für Ihren Kommentar', 'success');
	$this->redirect('this');
}

Platzieren Sie diese Methode direkt nach der Factory des commentForm.

Diese neue Methode hat ein Argument, nämlich die übermittelten Daten als Objekt $data. Anschließend speichern wir die Daten in der Datenbanktabelle comments.

Es gibt noch zwei weitere Methoden, die einer Erklärung bedürfen. Die redirect-Methode leitet buchstäblich zurück zur aktuellen Seite. Dies ist nach jedem Absenden eines Formulars sinnvoll, wenn es gültige Daten enthielt und der Callback die Operation wie vorgesehen durchgeführt hat. Wenn wir die Seite nach dem Absenden des Formulars weiterleiten, sehen wir auch nicht die bekannte Meldung Möchten Sie die Formulardaten erneut senden?, die wir manchmal im Browser sehen können. (Generell gilt, dass nach dem Absenden eines Formulars mit der POST-Methode immer eine Weiterleitung zu einer GET-Aktion erfolgen sollte.)

Die Methode flashMessage dient dazu, den Benutzer über das Ergebnis einer Operation zu informieren. Da wir weiterleiten, kann die Nachricht nicht einfach an das Template übergeben und gerendert werden. Deshalb gibt es diese Methode, die diese Nachricht speichert und beim nächsten Laden der Seite verfügbar macht. Flash-Nachrichten werden im Haupt-Template app/Presentation/@layout.latte gerendert und sehen so aus:

<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
	{$flash->message}
</div>

Wie wir bereits wissen, werden Flash-Nachrichten automatisch an das Template übergeben, sodass wir uns darüber keine großen Gedanken machen müssen, es funktioniert einfach. Weitere Informationen finden Sie in der Dokumentation.

Rendern von Kommentaren

Dies ist eines der Dinge, die Sie einfach lieben werden. Nette Database hat eine großartige Funktion namens Explorer. Erinnern Sie sich noch daran, dass wir die Tabellen in der Datenbank absichtlich mit dem InnoDB-Speicher erstellt haben? Adminer hat dadurch etwas geschaffen, das sich Fremdschlüssel nennt, was uns viel Arbeit erspart.

Der Nette Database Explorer verwendet Fremdschlüssel, um die Beziehung zwischen Tabellen aufzulösen, und aus dem Wissen über diese Beziehungen kann er automatisch Datenbankabfragen erstellen.

Wie Sie sich sicher erinnern, haben wir die Variable $post mit der Methode PostPresenter::renderShow() an das Template übergeben, und jetzt möchten wir über alle Kommentare iterieren, deren Spaltenwert post_id mit $post->id übereinstimmt. Dies können wir durch den Aufruf von $post->related('comments') erreichen. Ja, so einfach ist das. Schauen wir uns den resultierenden Code an:

public function renderShow(int $id): void
{
	...
	$this->template->post = $post;
	$this->template->comments = $post->related('comments')->order('created_at');
}

Und das Template:

...
<h2>Kommentare</h2>

<div class="comments">
	{foreach $comments as $comment}
		<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
			{$comment->name}
		</a></b> schrieb:</p>

		<div>{$comment->content}</div>
	{/foreach}
</div>
...

Beachten Sie das spezielle Attribut n:tag-if. Sie wissen bereits, wie n:Attribute funktionieren. Wenn Sie dem Attribut das Präfix tag- hinzufügen, wird die Funktionalität nur auf das HTML-Tag angewendet, nicht auf dessen Inhalt. Dies ermöglicht es uns, den Namen des Kommentators nur dann zu einem Link zu machen, wenn er seine E-Mail-Adresse angegeben hat. Diese beiden Zeilen sind identisch:

<strong n:tag-if="$important"> Guten Tag! </strong>

{if $important}<strong>{/if} Guten Tag! {if $important}</strong>{/if}