記事の作成と編集

素晴らしい。とても格好いい新しいブログができて、人々はコメントで活発に話し合っていて、ようやくもう少しプログラムを書く時間ができました。Adminer は優れた道具ですが、新しいブログの記事を書くにはあまり心地よくありません。アプリケーションから直接、新しい記事を足す単純なフォームを作るころ合いかもしれません。やってみましょう。

まず利用者から見えるところを設計するところから始めます。

  1. トップページに「新しい記事を書く」のリンクを足します。
  2. このリンクは、タイトルの項目と記事の中身のテキストエリアを持つフォームを表示します。
  3. 保存のボタンを押すと、その記事はデータベースに保存されます。

あとで認証も足して、ログインした利用者だけが新しい記事を足せるようにします。とはいえそれはあとにしましょう。今すべてを動かすには、どんなコードを書く必要があるでしょうか。

  1. 記事を足すフォームを持つ新しいプレゼンターを作ります。
  2. フォームが正しく送信されたあとに呼ばれ、新しい記事をデータベースに保存するコールバックを定義します。
  3. そのフォームの新しいテンプレートを作ります。
  4. トップページのテンプレートにそのフォームへのリンクを足します。

新しいプレゼンター

新しいプレゼンターを EditPresenter と名付け、app/Presentation/Edit/EditPresenter.php に置きましょう。これもデータベースにつなぐ必要があるので、ここでもデータベース接続を求めるコンストラクタを書きます。

<?php
namespace App\Presentation\Edit;

use Nette;
use Nette\Application\UI\Form;

final class EditPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}
}

記事を保存するフォーム

フォームとコンポーネントは、コメントの機能を足したときにすでに扱いました。まだはっきりしないなら、フォームとコンポーネントの働きを見直してきてください。ここで待っています ;)

では EditPresenter に次のメソッドを足します。

protected function createComponentPostForm(): Form
{
	$form = new Form;
	$form->addText('title', 'タイトル:')
		->setRequired();
	$form->addTextArea('content', '本文:')
		->setRequired();

	$form->addSubmit('send', '保存して公開');
	$form->onSuccess[] = $this->postFormSucceeded(...);

	return $form;
}

フォームから新しい記事を保存する

続いてハンドラのメソッドを足します。

private function postFormSucceeded(array $data): void
{
	$post = $this->database
		->table('posts')
		->insert($data);

	$this->flashMessage('記事を公開しました。', 'success');
	$this->redirect('Post:show', $post->id);
}

手短に説明します。フォームから値を取り出し、データベースに入れ、記事がうまく保存されたことを利用者に伝えるメッセージを作り、その記事が公開されたページへリダイレクトして、どう見えるかを確かめられるようにします。

新しい記事を作るページ

では Edit/create.latte のテンプレートを作りましょう。

{block content}
<h1>新しい記事</h1>

{control postForm}

もうすべて分かるはずです。最後の行が、たった今作ったフォームを描きます。

対応する renderCreate() メソッドも作れますが、必要ありません。データベースからデータを取り出してテンプレートへ渡す必要がないので、そのメソッドは空になります。こうした場合、メソッドはまったくなくてかまいません。

記事を作るためのリンク

EditPresenter とその create アクションへのリンクの足し方は、もうご存じでしょう。やってみてください。

app/Presentation/Home/default.latte ファイルに次を足すだけです。

<a n:href="Edit:create">新しい記事を書く</a>

記事を編集する

では既存の記事を編集できるようにしましょう。とても簡単です。すでに postForm のフォームがあり、それを編集にも使えます。

EditPresenter に新しい edit のページを足します。

public function renderEdit(int $id): void
{
	$post = $this->database
		->table('posts')
		->get($id);

	if (!$post) {
		$this->error('Post not found');
	}

	$this->getComponent('postForm')
		->setDefaults($post->toArray());
}

そして対応するテンプレート Edit/edit.latte を作ります。

{block content}
<h1>記事を編集</h1>

{control postForm}

さらに postFormSucceeded メソッドを、新しい記事を足すことも(今と同じように)、既存の記事を編集することもできるように書き換えます。

private function postFormSucceeded(array $data): void
{
	$id = $this->getParameter('id');

	if ($id) {
		$post = $this->database
			->table('posts')
			->get($id);
		$post->update($data);

	} else {
		$post = $this->database
			->table('posts')
			->insert($data);
	}

	$this->flashMessage('記事を公開しました。', 'success');
	$this->redirect('Post:show', $post->id);
}

id のパラメータが渡されていれば、記事を編集しているということです。その場合はその記事が本当に存在するかを確かめ、存在すればデータベースで更新します。id が渡されていなければ、新しい記事を足すということです。

ところで id はどこから来るのでしょうか。それは renderEdit メソッドに渡されたパラメータです。

これで app/Presentation/Post/show.latte のテンプレートにリンクを足せます。

<a n:href="Edit:edit $post->id">この記事を編集</a>

まとめ

ブログは動いていて、人々は次々とコメントし、新しい記事を足すのにもう Adminer に頼りません。すっかり独り立ちして、ふつうの人でもそこに投稿できます。でも待ってください。誰でも、つまりインターネット上の本当に誰でも私たちのブログに投稿できるのは、たぶんよくありません。ログインした利用者だけが投稿できるように、何らかの認証が要ります。それは次の章で足します。