レコードの作成と編集のためのフォーム

Netteでレコードの追加と編集を正しく実装する方法は?両方に同じフォームを使用します。

多くの場合、レコードの追加と編集のためのフォームは同じであり、ボタンのラベルなどが異なるだけです。まずレコードを追加するためにフォームを使用し、次に編集のために使用し、最後に両方の解決策を組み合わせる簡単なPresenterの例を示します。

レコードの追加

レコードを追加するためのPresenterの例です。データベース自体の操作はFacadeクラスに任せます。そのコードはこの例では重要ではありません。

use Nette\Application\UI\Form;

class RecordPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Facade $facade,
	) {
	}

	protected function createComponentRecordForm(): Form
	{
		$form = new Form;

		// ... フォームコントロールを追加 ...

		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // データベースへのレコード追加
		$this->flashMessage('正常に追加されました');
		$this->redirect('...');
	}

	public function renderAdd(): void
	{
		// ...
	}
}

レコードの編集

次に、レコードを編集するためのPresenterがどのようになるかを示します。

use Nette\Application\UI\Form;

class RecordPresenter extends Nette\Application\UI\Presenter
{
	private $record;

	public function __construct(
		private Facade $facade,
	) {
	}

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			!$record // レコードの存在確認
			|| !$this->facade->isEditAllowed(/*...*/) // 権限チェック
		) {
			$this->error(); // エラー 404
		}

		$this->record = $record;
	}

	protected function createComponentRecordForm(): Form
	{
		// アクションが'edit'であることを確認
		if ($this->getAction() !== 'edit') {
			$this->error();
		}

		$form = new Form;

		// ... フォームコントロールを追加 ...

		$form->setDefaults($this->record); // デフォルト値の設定
		$form->onSuccess[] = [$this, 'recordFormSucceeded'];
		return $form;
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$this->facade->update($this->record->id, $data); // レコードの更新
		$this->flashMessage('正常に更新されました');
		$this->redirect('...');
	}
}

presenterのライフサイクル の最初に実行される action メソッドで、レコードの存在とユーザーがそれを編集する権限を確認します。

レコードをプロパティ $record に保存して、デフォルト値を設定するために createComponentRecordForm() メソッドで、そしてIDのために recordFormSucceeded() で利用できるようにします。代替の解決策は、デフォルト値を直接 actionEdit() で設定し、URLの一部であるIDの値を getParameter('id') を使用して取得することです。

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			// 存在確認と権限チェック
		) {
			$this->error();
		}

		// フォームのデフォルト値設定
		$this->getComponent('recordForm')
			->setDefaults($record);
	}

	public function recordFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data);
		// ...
	}
}

しかし、そしてこれが コード全体の最も重要なポイント であるべきですが、フォームを作成する際には、アクションが実際に edit であることを確認する必要があります。そうでなければ、actionEdit() メソッドでの検証はまったく行われません!

追加と編集のための同じフォーム

そして今、両方のPresenterを1つに結合します。createComponentRecordForm() メソッドでどのアクションかを区別し、それに応じてフォームを設定することもできますし、それを直接actionメソッドに任せて条件をなくすこともできます。

class RecordPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Facade $facade,
	) {
	}

	public function actionAdd(): void
	{
		$form = $this->getComponent('recordForm');
		$form->onSuccess[] = [$this, 'addingFormSucceeded'];
	}

	public function actionEdit(int $id): void
	{
		$record = $this->facade->get($id);
		if (
			!$record // レコードの存在確認
			|| !$this->facade->isEditAllowed(/*...*/) // 権限チェック
		) {
			$this->error(); // エラー 404
		}

		$form = $this->getComponent('recordForm');
		$form->setDefaults($record); // デフォルト値の設定
		$form->onSuccess[] = [$this, 'editingFormSucceeded'];
	}

	protected function createComponentRecordForm(): Form
	{
		// アクションが'add'または'edit'であることを確認
		if (!in_array($this->getAction(), ['add', 'edit'])) {
			$this->error();
		}

		$form = new Form;

		// ... フォームコントロールを追加 ...

		return $form;
	}

	public function addingFormSucceeded(Form $form, array $data): void
	{
		$this->facade->add($data); // データベースへのレコード追加
		$this->flashMessage('正常に追加されました');
		$this->redirect('...');
	}

	public function editingFormSucceeded(Form $form, array $data): void
	{
		$id = (int) $this->getParameter('id');
		$this->facade->update($id, $data); // レコードの更新
		$this->flashMessage('正常に更新されました');
		$this->redirect('...');
	}
}