Создание и редактирование записей

Прекрасно! У нас есть суперклассный новый блог, люди активно спорят в комментариях, и у нас наконец появилось время ещё попрограммировать. Хотя Adminer – отличный инструмент, писать в нём новые записи блога не слишком удобно. Пожалуй, самое время создать простую форму для добавления новых записей прямо из приложения. Сделаем это.

Начнём с проектирования пользовательского интерфейса:

  1. На главную страницу добавим ссылку “Написать новую запись”.
  2. Эта ссылка покажет форму с полем заголовка и текстовой областью для содержимого записи.
  3. Когда мы нажмём кнопку Сохранить, запись сохранится в базу данных.

Позже мы добавим ещё и аутентификацию и разрешим добавлять новые записи только вошедшим пользователям. Но это сделаем потом. Какой код нам нужно написать сейчас, чтобы всё заработало?

  1. Создать новый презентер с формой для добавления записей.
  2. Определить callback, который будет вызван после успешной отправки формы и сохранит новую запись в базу данных.
  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('Запись не найдена');
	}

	$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. Он полностью самостоятелен, и публиковать в нём могут даже обычные люди. Но постойте, вряд ли это нормально, что кто угодно, то есть буквально кто угодно в интернете, может публиковать в нашем блоге. Нужна какая-то аутентификация, чтобы публиковать могли только вошедшие пользователи. Её мы добавим в следующей главе.