Komentarji

Blog je bil uveden, napisali smo nekaj zelo dobrih blogov in jih objavili prek Adminerja. Ljudje berejo blog in so zelo navdušeni nad našimi idejami. Vsak dan prejmemo veliko elektronskih sporočil s pohvalami. Toda čemu so vse te pohvale, če jih dobimo samo v elektronski pošti, tako da jih nihče drug ne more prebrati? Ali ne bi bilo bolje, če bi ljudje lahko komentirali neposredno na blogu, tako da bi vsi drugi lahko prebrali, kako super smo?

Naj bo mogoče komentirati vse članke.

Ustvarjanje nove tabele

Ponovno zaženite program Adminer in ustvarite novo tabelo z imenom comments s temi stolpci:

  • id int, preverite samodejni prirastek (AI)
  • post_id, tuj ključ, ki se sklicuje na tabelo posts
  • name varchar, dolžina 255
  • email varchar, dolžina 255
  • content besedilo
  • created_at časovni žig

Izgledati mora takole:

Ne pozabite uporabiti shranjevanja tabel InnoDB in pritisnite Shrani.

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;

Obrazec za komentiranje

Najprej moramo ustvariti obrazec, ki bo uporabnikom omogočal komentiranje na naši strani. Okvir Nette ima odlično podporo za obrazce. Lahko jih konfigurirate v predstavniku in prikažete v predlogi.

Nette Framework ima koncept komponent. Komponenta** je razred ali del kode, ki ga je mogoče ponovno uporabiti in ga je mogoče priključiti na drugo komponento. Tudi predstavnik je komponenta. Vsaka komponenta je ustvarjena s pomočjo tovarne komponent. Zato definirajmo tovarno obrazca za komentarje v PostPresenter.

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

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

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

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

	$form->addSubmit('send', 'Publish comment');

	return $form;
}

Malo jo razložimo. Prva vrstica ustvari nov primerek komponente Form. Naslednje metode pripenjajo vhode HTML v definicijo obrazca. ->addText se bo prikazal kot <input type=text name=name>z <label>Your name:</label>. Kot ste morda že uganili zdaj, ->addTextArea priklopi <textarea> in ->addSubmit doda vnos <input type=submit>. Podobnih metod je še več, vendar je to vse, kar morate zdaj vedeti. Več lahko izveste v dokumentaciji.

Ko je komponenta obrazca opredeljena v predstavniku, jo lahko upodobimo (prikažemo) v predlogi. To storite tako, da postavite oznako {control} na konec predloge podrobnosti prispevka, v Post/show.latte. Ker je ime komponente commentForm (izhaja iz imena metode createComponentCommentForm), bo oznaka videti takole

...
<h2>Post new comment</h2>

{control commentForm}

Če si zdaj ogledate podrobnosti neke objave, bo na voljo nov obrazec za objavo komentarjev.

Shranjevanje v zbirko podatkov

Ste poskusili poslati nekaj podatkov? Morda ste opazili, da obrazec ne izvede nobene akcije. Samo tam je, je videti kul in ne naredi ničesar. Nanj moramo priključiti metodo povratnega klica, ki bo shranila poslane podatke.

Pred vrstico return v tovarni komponente za commentForm postavite naslednjo vrstico :

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

Pomeni “po uspešni oddaji obrazca pokliči metodo commentFormSucceeded trenutnega predstavnika”. Ta metoda še ne obstaja, zato jo ustvarimo.

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

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

	$this->flashMessage('Thank you for your comment', 'success');
	$this->redirect('this');
}

Postaviti jo morate takoj za tovarno komponent commentForm.

Nova metoda ima en argument, ki je primerek obrazca, ki se pošilja in ga je ustvarila tovarna komponent. Oddane vrednosti prejmemo v $data. Nato pa podatke vstavimo v tabelo podatkovne zbirke comments.

Razložiti je treba še dva klica metod. Preusmeritev dobesedno preusmeri na trenutno stran. To morate storiti vsakič, ko je obrazec oddan, veljaven in je operacija povratnega klica naredila, kar bi morala. Prav tako pri preusmeritvi strani po oddaji obrazca ne boste videli znanega sporočila Would you like to submit the post data again?, ki se včasih pojavi v brskalniku. (Na splošno morate po oddaji obrazca z metodo POST uporabnika vedno preusmeriti na dejanje GET.)

Sporočilo flashMessage je namenjeno obveščanju uporabnika o rezultatu neke operacije. Ker gre za preusmeritev, sporočila ni mogoče neposredno posredovati predlogi in ga izrisati. Zato je tu ta metoda, ki ga bo shranila in dala na voljo ob naslednjem nalaganju strani. Sporočila flash so prikazana v privzeti datoteki app/UI/@layout.latte in so videti takole:

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

Kot že vemo, se samodejno posredujejo predlogi, zato vam o tem ni treba preveč razmišljati, preprosto deluje. Za več podrobnosti si oglejte dokumentacijo.

Prikazovanje komentarjev

To je ena od stvari, ki vam bo preprosto všeč. Podatkovna zbirka Nette ima to kul funkcijo z imenom Raziskovalec. Se spomnite, da smo tabele ustvarili kot InnoDB? Adminer je ustvaril tako imenovane tuje ključe, ki nam bodo prihranili ogromno dela.

Nette Database Explorer uporablja tuje ključe za reševanje razmerij med tabelami, in ker pozna razmerja, lahko samodejno ustvari poizvedbe za vas.

Kot se morda spomnite, smo spremenljivko $post posredovali predlogi v PostPresenter::renderShow(), zdaj pa želimo iterirati po vseh komentarjih, ki imajo stolpec post_id enak našemu $post->id. To lahko storite tako, da pokličete $post->related('comments'). Tako preprosto je. Oglejte si nastalo kodo.

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

In predlogo:

...
<h2>Comments</h2>

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

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

Opazite poseben atribut n:tag-if. Že veste, kako deluje n: attributes. Če atributu predpomnite tag-, bo ovijal le oznake, ne pa tudi njihove vsebine. Tako lahko ime komentatorja spremenite v povezavo, če je navedel svoj elektronski naslov. Rezultati teh dveh vrstic so enaki:

<strong n:tag-if="$important"> Hello there! </strong>

{if $important}<strong>{/if} Hello there! {if $important}</strong>{/if}