Yorumlar

Blog yayına alındı, çok ilginç birkaç blog gönderisi yazdık ve onları Adminer ile yayımladık. İnsanlar blogumuzu okuyor ve ona büyük coşku duyuyor. Her gün övgü dolu pek çok e-posta alıyoruz. Ama tüm bu övgü yalnızca e-postamızda kalıyor ve başka kimse okuyamıyorsa ne işe yarar? Okuyucular doğrudan yazılara yorum yapabilseydi ve herkes ne kadar harika olduğumuzu görebilseydi daha iyi olmaz mıydı?

Hadi yorumları gerçekleştirelim.

Yeni Bir Tablo Oluşturma

Adminer'ı yeniden çalıştıralım ve şu sütunlarla bir comments tablosu oluşturalım:

  • id int, autoincrement (AI) işaretli
  • post_id, posts tablosuna başvuran yabancı anahtar
  • name varchar, uzunluk 255
  • email varchar, uzunluk 255
  • content text
  • created_at timestamp

Tablo şuna benzer görünmelidir:

Yine InnoDB depolama motorunu kullanmayı unutmayın.

CREATE TABLE `comments` (
	`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`post_id` int NOT NULL,
	`name` varchar(255) NOT NULL,
	`email` varchar(255) 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;

Yorum Formu

Önce, kullanıcıların gönderilere yorum yapmasını sağlayan bir form oluşturmamız gerekiyor. Nette Framework'ün formlar için mükemmel desteği vardır. Onları presenter'da yapılandırıp şablonda render edebiliriz.

Nette Framework, bileşen kavramını kullanır. Bileşen, başka bir bileşene iliştirilebilen, yeniden kullanılabilir bir sınıf ya da kod parçasıdır. Presenter bile bir bileşendir. Her bileşen bir factory metoduyla oluşturulur. PostPresenter içinde yorum formu için bir factory oluşturalım.

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

	$form->addText('name', 'Adınız:')
		->setRequired();

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

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

	$form->addSubmit('send', 'Yorumu yayımla');

	return $form;
}

Bunu kısaca açıklayalım. İlk satır, Form bileşeninin yeni bir örneğini oluşturur. Sonraki metotlar, form tanımına HTML girdileri iliştirir. ->addText, bir <label>Adınız:</label> etiketiyle birlikte <input type="text" name="name"> olarak render edilir. Tahmin edebileceğiniz gibi ->addTextArea bir <textarea>, ->addSubmit ise bir <input type="submit"> ekler. Benzer pek çok metot var, ama şimdilik bunlar yeterli. Belgelerden daha fazlasını öğrenebilirsiniz.

Form bileşeni presenter'da tanımlandıktan sonra, onu şablonda render edebiliriz (gösterebiliriz). Bunun için {control} etiketini, tek bir gönderiyi gösteren şablonun, yani Post/show.latte dosyasının sonuna koyun. Bileşenin adı commentForm olduğundan (createComponentCommentForm metot adından türetilir), etiket şöyle görünecek:

...
<h2>Yeni yorum yaz</h2>

{control commentForm}

Şimdi bir gönderinin ayrıntı sayfasını görüntülerseniz, sonda yeni yorum formunu göreceksiniz.

Veritabanına Kaydetme

Formu doldurup göndermeyi denediniz mi? Formun henüz aslında hiçbir şey yapmadığını fark etmiş olabilirsiniz. Gönderilen veriyi kaydedecek bir callback metodu iliştirmemiz gerekiyor.

commentForm bileşeninin factory metodunda return deyiminden önce şu satırı ekleyin:

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

Bu satır, “form başarıyla gönderildikten sonra geçerli presenter'ın commentFormSucceeded metodunu çağır” demektir. Bu metot henüz yok, hadi oluşturalım:

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('Yorumunuz için teşekkürler', 'success');
	$this->redirect('this');
}

Onu commentForm bileşen factory'sinin hemen ardına koymalısınız.

Yeni metodun bir argümanı var: bileşen factory'sinin oluşturduğu, gönderilen formun örneği. Gönderilen değerleri $data içinde alırız. Sonra veriyi comments veritabanı tablosuna ekleriz.

Açıklanacak iki metot çağrısı daha var. redirect('this') metodu, harfi harfine geçerli sayfaya geri yönlendirir. Bunu, bir form her başarıyla gönderildiğinde ve callback işlemi tamamlandığında yapmalısınız. Form gönderiminden sonra yönlendirmek, tarayıcıdaki bilinen “Gönderi verilerini yeniden göndermek ister misiniz?” mesajını da önler. (Genel olarak, bir formu POST metoduyla gönderdikten sonra her zaman bir GET eylemine yönlendirmelisiniz.)

flashMessage metodu, kullanıcıyı bir işlemin sonucu hakkında bilgilendirmek için kullanılır. Yönlendirme yaptığımızdan, mesaj öylece şablona aktarılıp doğrudan render edilemez. Bu metot mesajı saklar ve bir sonraki sayfa yüklemesinde kullanılabilir kılar. Flash mesajları, app/Presentation/@layout.latte ana yerleşim şablonunda render edilir ve şöyle görünür:

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

Zaten bildiğimiz gibi flash mesajları şablona otomatik olarak aktarılır, dolayısıyla bunu pek düşünmemize gerek yok; öylece çalışır. Ayrıntılar için belgelere bakın.

Yorumların Render Edilmesi

Bu, öylece seveceğiniz özelliklerden biri. Nette Database'in Explorer adlı harika bir özelliği var. Veritabanı tablolarını InnoDB motoruyla bilinçli olarak nasıl oluşturduğumuzu anımsıyor musunuz? Adminer, bize bolca iş kazandıran yabancı anahtarlar oluşturdu.

Nette Database Explorer, tablolar arasındaki ilişkileri çözmek için bu yabancı anahtarları kullanır. Bu ilişkileri bilerek, veritabanı sorgularını sizin için otomatik oluşturabilir.

Anımsayacağınız gibi, PostPresenter::renderShow() içinde şablona $post değişkenini aktarmıştık. Şimdi post_id sütunu bizim $post->id değerimizle eşleşen tüm yorumları dolaşmak istiyoruz. Bunu $post->related('comments') çağırarak yapabiliriz. Evet, o kadar basit. Nihai koda bakalım:

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

Ve şablon:

...
<h2>Yorumlar</h2>

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

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

Özel n:tag-if niteliğine dikkat edin. n:attribute'ların nasıl çalıştığını zaten biliyorsunuz. Niteliğin önüne tag- koyarsanız, işlev yalnızca HTML etiketinin kendisine uygulanır, içeriğine değil. Bu, yorum yapanın adını yalnızca e-postasını verdiyse bağlantıya çevirmemizi sağlar. Şu iki satır aynı sonucu verir:

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

{if $important}<strong>{/if} Merhaba! {if $important}</strong>{/if}