お問い合わせフォームを作ろう

Nette でお問い合わせフォームを作り、送られたデータをメールで送るところまでを見ていきましょう。さっそく始めます。

まず新しいプロジェクトを作る必要があります。方法ははじめにのページで説明しています。それができたら、フォームを作り始められます。

最も簡単なのは、プレゼンターの中に直接フォームを作る方法です。すでにある HomePresenter を使えます。フォームを表す contactForm というコンポーネントを追加しましょう。プレゼンターのコードに、そのコンポーネントを作るファクトリメソッド createComponentContactForm() を足します。

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

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'お名前:')
			->setRequired('お名前を入力してください');
		$form->addEmail('email', 'メールアドレス:')
			->setRequired('メールアドレスを入力してください');
		$form->addTextArea('message', 'メッセージ:')
			->setRequired('メッセージを入力してください');
		$form->addSubmit('send', '送信');
		$form->onSuccess[] = $this->contactFormSucceeded(...);
		return $form;
	}

	private function contactFormSucceeded(Form $form, $data): void
	{
		// メールの送信
	}
}

ご覧のとおり、メソッドを 2 つ作りました。ひとつめの createComponentContactForm() は新しいフォームのインスタンスを作ります。名前、メールアドレス、メッセージの項目を、それぞれ addText()addEmail()addTextArea() メソッドで追加しています。送信ボタンも足しました。では、ユーザーが項目を空のままにしたらどうなるでしょうか。その場合は必須であることを伝えるべきです。それを setRequired() メソッドで実現しました。最後に onSuccessイベントハンドラを結びつけています。これはフォームが正しく送信されたときに発火します。ここでは contactFormSucceeded メソッドを呼び、送られたデータの処理を担当させます。このメソッドはこのあと実装します。

contactForm コンポーネントを Home/default.latte テンプレートで描きましょう。

{block content}
<h1>お問い合わせフォーム</h1>
{control contactForm}

メールの送信そのもののために、ContactFacade という新しいクラスを作り、app/Model/ContactFacade.php に置きます。

namespace App\Model;

use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$mail = new Message;
		$mail->addTo('admin@example.com') // あなたのメールアドレス
			->setFrom($email, $name)
			->setSubject('お問い合わせフォームからのメッセージ')
			->setBody($message);

		$this->mailer->send($mail);
	}
}

sendMessage() メソッドがメールを作って送ります。そのために mailer サービスを使い、コンストラクタで依存関係として受け取っています。詳しくはメールの送信をご覧ください。

ではプレゼンターに戻って contactFormSucceeded() メソッドを仕上げましょう。フォームから送られたデータを渡して、ContactFacade クラスの sendMessage() メソッドを呼びます。では ContactFacade オブジェクトはどうやって手に入れるのでしょうか。依存性注入を使い、コンストラクタで要求します。

use App\Model\ContactFacade;
use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	public function __construct(
		private ContactFacade $facade,
	) {
	}

	protected function createComponentContactForm(): Form
	{
		// ...
	}

	public function contactFormSucceeded(stdClass $data): void
	{
		$this->facade->sendMessage($data->email, $data->name, $data->message);
		$this->flashMessage('メッセージを送信しました');
		$this->redirect('this');
	}
}

メールを送ったあと、送信を知らせるフラッシュメッセージをユーザーに表示します。それからリダイレクトして、ブラウザの再読み込みでフォームが再送信されないようにします。

ここまで正しく設定できていれば、お問い合わせフォームからメールを送れるはずです。おめでとうございます。

HTML メールのテンプレート

今のところ、フォームから送られたメッセージだけを含むプレーンテキストのメールが送られます。しかしメールに HTML を使えば、見た目をより魅力的にできます。そのためのテンプレートを Latte で作り、app/Model/contactEmail.latte として保存します。

<html>
	<title>お問い合わせフォームからのメッセージ</title>

	<body>
		<p><strong>お名前:</strong> {$name}</p>
		<p><strong>メールアドレス:</strong> {$email}</p>
		<p><strong>メッセージ:</strong> {$message}</p>
	</body>
</html>

あとは、このテンプレートを使うよう ContactFacade を書き換えるだけです。コンストラクタで LatteFactory クラスを要求します。これは Latte のテンプレートレンダラーである Latte\Engine オブジェクトを作れます。renderToString() メソッドでテンプレートを文字列に描きます。第 1 パラメータはテンプレートファイルへのパス、第 2 パラメータはそこに渡す変数の配列です。

namespace App\Model;

use Nette\Bridges\ApplicationLatte\LatteFactory;
use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$latte = $this->latteFactory->create();
		$body = $latte->renderToString(__DIR__ . '/contactEmail.latte', [
			'email' => $email,
			'name' => $name,
			'message' => $message,
		]);

		$mail = new Message;
		$mail->addTo('admin@example.com') // あなたのメールアドレス
			->setFrom($email, $name)
			->setHtmlBody($body);

		$this->mailer->send($mail);
	}
}

生成した HTML のメール本文は、もとの setBody() ではなく setHtmlBody() メソッドに渡します。setSubject() でメールの件名を指定する必要もありません。ライブラリがテンプレートの <title> 要素から自動的に取り出してくれるからです。

設定

ContactFacade クラスのコードには、管理者のメールアドレス admin@example.com がまだ直接書かれています。これは設定ファイルに移したほうがよいでしょう。どうすればよいでしょうか。

まず ContactFacade クラスを書き換え、直接書かれたメールアドレスの文字列を、コンストラクタで渡される変数に置き換えます。

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
		private string $adminEmail,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		// ...
		$mail = new Message;
		$mail->addTo($this->adminEmail)
			->setFrom($email, $name)
			->setHtmlBody($body);
		// ...
	}
}

次の段階は、この変数の値を設定で与えることです。app/config/services.neon ファイルに次を足します。

services:
	- App\Model\ContactFacade(adminEmail: admin@example.com)

これで完了です。services セクションに項目が多く、メールアドレスがその中で埋もれてしまうと感じるなら、パラメータにできます。次のように書き換えてください。

services:
	- App\Model\ContactFacade(adminEmail: %adminEmail%)

そしてこのパラメータを app/config/common.neon ファイルで定義します。

parameters:
	adminEmail: admin@example.com

これで出来上がりです。