Kommentek

Feltöltöttük a blogot a webszerverre, és közzétettünk néhány nagyon érdekes bejegyzést az Adminer segítségével. Az emberek olvassák a blogunkat, és nagyon lelkesek érte. Minden nap sok dicsérő e-mailt kapunk. De mit ér ez a sok dicséret, ha csak e-mailben van meg, és senki sem olvashatja el? Jobb lenne, ha az olvasó közvetlenül kommentelhetné a cikket, így mindenki elolvashatná, milyen csodálatosak vagyunk.

Tehát programozzunk kommenteket.

Új tábla létrehozása

Indítsuk el az Adminert, és hozzunk létre egy comments táblát a következő oszlopokkal:

  • id int, jelöljük be az autoincrement-et (AI)
  • post_id, idegen kulcs, amely a posts táblára hivatkozik
  • name varchar, length 255
  • email varchar, length 255
  • content text
  • created_at timestamp

A táblának tehát valahogy így kell kinéznie:

Ne felejtse el újra az InnoDB tárolót használni.

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;

Kommentálási űrlap

Először létre kell hoznunk egy űrlapot, amely lehetővé teszi a felhasználók számára a bejegyzések kommentálását. A Nette Framework csodálatos támogatást nyújt az űrlapokhoz. Konfigurálhatjuk őket a presenterben, és megjeleníthetjük a sablonban.

A Nette Framework a komponensek koncepcióját használja. A komponens egy újra felhasználható osztály vagy kódrészlet, amely egy másik komponenshez csatolható. Még a presenter is egy komponens. Minden komponenst egy factory hoz létre. Tehát létrehozunk egy factory-t a komment űrlaphoz a PostPresenter-ben.

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

	$form->addText('name', 'Név:')
		->setRequired();

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

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

	$form->addSubmit('send', 'Komment közzététele');

	return $form;
}

Magyarázzuk el ezt is egy kicsit. Az első sor létrehoz egy új Form komponens példányt. A következő metódusok HTML inputokat csatolnak ennek az űrlapnak a definíciójához. A ->addText <input type="text" name="name">-ként jelenik meg <label>Név:</label>-lel. Ahogy valószínűleg helyesen sejti, a ->addTextArea <textarea>-ként, a ->addSubmit pedig <input type="submit">-ként jelenik meg. Sokkal több hasonló metódus létezik, de ezek egyelőre elegendőek ehhez az űrlaphoz. Többet a dokumentációban olvashat.

Ha az űrlap már definiálva van a presenterben, megjeleníthetjük (kirajzolhatjuk) a sablonban. Ezt úgy tehetjük meg, hogy a {control} taget elhelyezzük a sablon végén, amely egy konkrét bejegyzést jelenít meg, a Post/show.latte-ban. Mivel a komponens neve commentForm (a név a createComponentCommentForm metódus nevéből származik), a tag így fog kinézni:

...
<h2>Új komment hozzáadása</h2>

{control commentForm}

Ha most megtekinti a bejegyzés részleteit tartalmazó oldalt, annak végén látni fogja az új komment űrlapot.

Mentés az adatbázisba

Próbálta már kitölteni és elküldeni az űrlapot? Valószínűleg észrevette, hogy az űrlap valójában semmit sem csinál. Csatolnunk kell egy callback metódust, amely elmenti az elküldött adatokat.

A return előtti sorba a commentForm komponens factory-jában helyezzük el a következő sort:

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

Az előző írásmód azt jelenti, hogy “az űrlap sikeres elküldése után hívd meg a commentFormSucceeded metódust a jelenlegi presenterből”. Ez a metódus azonban még nem létezik. Hozzuk létre tehát:

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('Köszönöm a kommentet', 'success');
	$this->redirect('this');
}

Ezt a metódust közvetlenül a commentForm űrlap factory-ja után helyezzük el.

Ennek az új metódusnak egy argumentuma van, ami az elküldött űrlap példánya – amelyet a factory hozott létre. Az elküldött értékeket a $data-ban kapjuk meg. Ezután az adatokat elmentjük a comments adatbázis táblába.

Van még két másik metódus, amelyek magyarázatot érdemelnek. A redirect metódus szó szerint visszairányít az aktuális oldalra. Ezt célszerű minden űrlap elküldése után megtenni, ha érvényes adatokat tartalmazott, és a callback a műveletet a tervek szerint hajtotta végre. Továbbá, ha az űrlap elküldése után átirányítjuk az oldalt, nem fogjuk látni a jól ismert Szeretné újra elküldeni az űrlap adatait? üzenetet, amelyet néha a böngészőben láthatunk. (Általában az űrlap POST metódussal történő elküldése után mindig átirányításnak kell következnie egy GET akcióra.)

A flashMessage metódus arra szolgál, hogy tájékoztassa a felhasználót valamilyen művelet eredményéről. Mivel átirányítunk, az üzenetet nem lehet egyszerűen átadni a sablonnak és megjeleníteni. Ezért van ez a metódus, amely elmenti ezt az üzenetet, és elérhetővé teszi a következő oldalbetöltéskor. A Flash üzenetek a fő sablonban, az app/Presentation/@layout.latte-ban jelennek meg, és így néznek ki:

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

Ahogy már tudjuk, a flash üzenetek automatikusan átadódnak a sablonnak, így nem kell sokat gondolkodnunk rajta, egyszerűen működik. További információkért látogassa meg a dokumentációt.

Kommentek megjelenítése

Ez az egyik olyan dolog, amit egyszerűen imádni fog. A Nette Database nagyszerű funkcióval rendelkezik, amelyet Explorer-nek hívnak. Emlékszik még, hogy az adatbázis tábláit szándékosan InnoDB tárolóval hoztuk létre? Az Adminer így létrehozott valamit, amit idegen kulcsok-nak neveznek, amelyek sok munkát takarítanak meg nekünk.

A Nette Database Explorer idegen kulcsokat használ a táblák közötti kapcsolatok feloldására, és ezen kapcsolatok ismeretében automatikusan képes adatbázis lekérdezéseket létrehozni.

Ahogy biztosan emlékszik, a sablonnak átadtuk a $post változót a PostPresenter::renderShow() metódussal, és most iterálni szeretnénk az összes kommenten, amelyek post_id oszlopának értéke megegyezik a $post->id-val. Ezt a $post->related('comments') hívásával érhetjük el. Igen, ilyen egyszerűen. Nézzük meg az eredményül kapott kódot:

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

És a sablont:

...
<h2>Kommentek</h2>

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

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

Figyelje meg a speciális n:tag-if attribútumot. Már tudja, hogyan működnek az n:atributy. Ha az attribútumhoz tag- előtagot csatol, a funkcionalitás csak a HTML tagre vonatkozik, nem a tartalmára. Ez lehetővé teszi számunkra, hogy a kommentelő nevét csak akkor tegyük linkké, ha megadta az e-mail címét. Ez a két sor azonos:

<strong n:tag-if="$important"> Jó napot! </strong>

{if $important}<strong>{/if} Jó napot! {if $important}</strong>{/if}