Σχόλια

Το ιστολόγιο έχει αναπτυχθεί, έχουμε γράψει μερικές πολύ καλές αναρτήσεις στο ιστολόγιο και τις δημοσιεύσαμε μέσω του Adminer. Οι άνθρωποι διαβάζουν το ιστολόγιο και είναι πολύ παθιασμένοι με τις ιδέες μας. Λαμβάνουμε πολλά μηνύματα ηλεκτρονικού ταχυδρομείου με επαίνους κάθε μέρα. Όμως, για ποιο λόγο όλοι αυτοί οι έπαινοι όταν τους έχουμε μόνο στο email, ώστε να μην μπορεί να τους διαβάσει κανείς άλλος; Δεν θα ήταν καλύτερα αν οι άνθρωποι μπορούσαν να σχολιάζουν απευθείας στο ιστολόγιο, ώστε όλοι οι άλλοι να μπορούν να διαβάσουν πόσο φοβεροί είμαστε;

Ας κάνουμε όλα τα άρθρα σχολιάσιμα.

Δημιουργία νέου πίνακα

Ανοίξτε ξανά το Adminer και δημιουργήστε έναν νέο πίνακα με όνομα comments με τις παρακάτω στήλες:

  • id int, έλεγχος αυτόματης αύξησης (AI)
  • post_id, ξένο κλειδί που παραπέμπει στον πίνακα posts
  • name varchar, μήκος 255
  • email varchar, μήκος 255
  • content text
  • created_at timestamp

Θα πρέπει να μοιάζει ως εξής:

Μην ξεχάσετε να χρησιμοποιήσετε την αποθήκευση πίνακα InnoDB και πατήστε Αποθήκευση.

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;

Φόρμα για σχολιασμό

Αρχικά, πρέπει να δημιουργήσουμε μια φόρμα, η οποία θα επιτρέπει στους χρήστες να σχολιάζουν στη σελίδα μας. Το Nette Framework έχει φοβερή υποστήριξη για φόρμες. Μπορούν να διαμορφωθούν σε έναν παρουσιαστή και να αποδοθούν σε ένα πρότυπο.

Το Nette Framework διαθέτει την έννοια των στοιχείων. Ένα συστατικό είναι μια επαναχρησιμοποιήσιμη κλάση ή ένα κομμάτι κώδικα, το οποίο μπορεί να συνδεθεί με ένα άλλο συστατικό. Ακόμα και ένας παρουσιαστής είναι ένα συστατικό. Κάθε συστατικό δημιουργείται με τη χρήση του εργοστασίου συστατικών. Ας ορίσουμε λοιπόν το εργοστάσιο της φόρμας σχολίων στο PostPresenter.

protected function createComponentCommentForm(): Form
{
	$form = new Form; // σημαίνει Nette\Application\UII\Form

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

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

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

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

	return $form;
}

Ας το εξηγήσουμε λίγο. Η πρώτη γραμμή δημιουργεί μια νέα περίπτωση του συστατικού Form. Οι ακόλουθες μέθοδοι επισυνάπτουν εισόδους HTML στον ορισμό της φόρμας. ->addText θα αποδίδεται ως <input type=text name=name>, με <label>Your name:</label>. Όπως ίσως έχετε ήδη μαντέψει αυτή τη στιγμή, το ->addTextArea επισυνάπτει μια <textarea> και το ->addSubmit προσθέτει ένα <input type=submit>. Υπάρχουν κι άλλες τέτοιες μέθοδοι, αλλά αυτό είναι το μόνο που πρέπει να ξέρετε αυτή τη στιγμή. Μπορείτε να μάθετε περισσότερα στην τεκμηρίωση.

Αφού οριστεί το συστατικό της φόρμας σε έναν παρουσιαστή, μπορούμε να το αποδώσουμε (εμφανίσουμε) σε ένα πρότυπο. Για να το κάνετε αυτό, τοποθετήστε την ετικέτα {control} στο τέλος του προτύπου λεπτομερειών δημοσίευσης, στο Post/show.latte. Επειδή το όνομα του συστατικού είναι commentForm (προέρχεται από το όνομα της μεθόδου createComponentCommentForm), η ετικέτα θα μοιάζει ως εξής

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

{control commentForm}

Τώρα, αν ελέγξετε τις λεπτομέρειες κάποιας δημοσίευσης, θα υπάρχει μια νέα φόρμα για τη δημοσίευση σχολίων.

Αποθήκευση στη βάση δεδομένων

Προσπαθήσατε να υποβάλετε κάποια δεδομένα; Ίσως έχετε παρατηρήσει ότι η φόρμα δεν εκτελεί καμία ενέργεια. Είναι απλά εκεί, φαίνεται ωραία και δεν κάνει τίποτα. Πρέπει να της επισυνάψουμε μια μέθοδο επανάκλησης, η οποία θα αποθηκεύσει τα υποβληθέντα δεδομένα.

Τοποθετήστε την ακόλουθη γραμμή πριν από τη γραμμή return στο component factory για το commentForm:

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

Σημαίνει “μετά την επιτυχή υποβολή της φόρμας, καλέστε τη μέθοδο commentFormSucceeded του τρέχοντος παρουσιαστή”. Αυτή η μέθοδος δεν υπάρχει ακόμα, οπότε ας τη δημιουργήσουμε.

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');
}

Θα πρέπει να την τοποθετήσετε αμέσως μετά το εργοστάσιο του συστατικού commentForm.

Η μέθοδος new έχει ένα όρισμα που είναι η περίπτωση της φόρμας που υποβάλλεται, η οποία δημιουργήθηκε από το εργοστάσιο συστατικών. Λαμβάνουμε τις υποβληθείσες τιμές στο $data. Και στη συνέχεια εισάγουμε τα δεδομένα στον πίνακα της βάσης δεδομένων comments.

Υπάρχουν δύο ακόμη κλήσεις μεθόδων που πρέπει να εξηγήσουμε. Η ανακατεύθυνση ανακατευθύνει κυριολεκτικά στην τρέχουσα σελίδα. Θα πρέπει να το κάνετε αυτό κάθε φορά που η φόρμα έχει υποβληθεί, είναι έγκυρη και η λειτουργία επανάκλησης έκανε αυτό που έπρεπε να κάνει. Επίσης, όταν ανακατευθύνετε τη σελίδα μετά την υποβολή της φόρμας, δεν θα βλέπετε το γνωστό μήνυμα Would you like to submit the post data again? που μερικές φορές μπορείτε να δείτε στο πρόγραμμα περιήγησης. (Γενικά, μετά την υποβολή μιας φόρμας με τη μέθοδο POST, θα πρέπει πάντα να ανακατευθύνετε τον χρήστη σε μια ενέργεια GET ).

Το flashMessage είναι για την ενημέρωση του χρήστη σχετικά με το αποτέλεσμα κάποιας ενέργειας. Επειδή κάνουμε ανακατεύθυνση, το μήνυμα δεν μπορεί να περάσει απευθείας στο πρότυπο και να αποδοθεί. Έτσι υπάρχει αυτή η μέθοδος, που θα το αποθηκεύσει και θα το κάνει διαθέσιμο κατά την επόμενη φόρτωση της σελίδας. Τα μηνύματα flash αποδίδονται στο προεπιλεγμένο αρχείο app/Presenters/templates/@layout.latte και μοιάζει κάπως έτσι:

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

Όπως ήδη γνωρίζουμε, περνούν αυτόματα στο πρότυπο, οπότε δεν χρειάζεται να το σκεφτείτε πολύ, απλά λειτουργεί. Για περισσότερες λεπτομέρειες δείτε την τεκμηρίωση.

Απόδοση των σχολίων

Αυτό είναι ένα από τα πράγματα που θα σας αρέσει πολύ. Η βάση δεδομένων Nette έχει αυτό το δροσερό χαρακτηριστικό που ονομάζεται Explorer. Θυμάστε ότι έχουμε δημιουργήσει τους πίνακες ως InnoDB; Ο Adminer έχει δημιουργήσει τα λεγόμενα ξένα κλειδιά που θα μας γλιτώσουν από έναν τόνο δουλειάς.

Ο Εξερευνητής της Nette Database χρησιμοποιεί τα ξένα κλειδιά για να επιλύσει τις σχέσεις μεταξύ των πινάκων και γνωρίζοντας τις σχέσεις, μπορεί να δημιουργήσει αυτόματα ερωτήματα για εσάς.

Όπως ίσως θυμάστε, έχουμε περάσει τη μεταβλητή $post στο πρότυπο στο PostPresenter::renderShow() και τώρα θέλουμε να επαναλάβουμε όλα τα σχόλια που έχουν τη στήλη post_id ίση με το $post->id μας . Μπορείτε να το κάνετε καλώντας το $post->related('comments'). Είναι τόσο απλό. Κοιτάξτε τον κώδικα που προκύπτει.

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

Και το πρότυπο:

...
<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>
...

Παρατηρήστε το ειδικό χαρακτηριστικό n:tag-if. Γνωρίζετε ήδη πώς λειτουργεί το n: attributes. Λοιπόν, αν προτάξετε το χαρακτηριστικό με το tag-, θα τυλίξει μόνο τις ετικέτες, όχι το περιεχόμενό τους. Αυτό σας επιτρέπει να μετατρέψετε το όνομα του σχολιαστή σε σύνδεσμο, αν έχει δώσει το email του. Τα αποτελέσματα αυτών των δύο γραμμών είναι πανομοιότυπα:

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

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