Creating and Editing Posts
That's great! We have a super cool new blog, people are actively discussing in the comments, and we finally have some time for more programming. Although Adminer is a great tool, it's not very comfortable for writing new blog posts. Perhaps it's the right time to create a simple form for adding new posts directly from the application. Let's do it.
Let's start by designing the user interface:
- On the homepage, add a “Write new post” link.
- This link will display a form with a title field and a textarea for the post content.
- When we click the Save button, the post will be saved to the database.
Later, we will also add authentication and allow only logged-in users to add new posts. But let's do that later. What code do we need to write now to make everything work?
- Create a new presenter with a form for adding posts.
- Define a callback that will be triggered after the successful submission of the form and will save the new post to the database.
- Create a new template for the form.
- Add a link to the form on the main page template.
New Presenter
Let's name the new presenter EditPresenter
and save it in app/Presentation/Edit/EditPresenter.php
.
It also needs to connect to the database, so here again, we write a constructor that requires a database connection:
<?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,
) {
}
}
Form for Saving Posts
Forms and components were already covered when we added support for comments. If it's still unclear, go review how forms and components work, we'll wait here ;)
Now add this method to the EditPresenter
:
protected function createComponentPostForm(): Form
{
$form = new Form;
$form->addText('title', 'Title:')
->setRequired();
$form->addTextArea('content', 'Content:')
->setRequired();
$form->addSubmit('send', 'Save and publish');
$form->onSuccess[] = $this->postFormSucceeded(...);
return $form;
}
Saving New Post from Form
Continue by adding the handler method:
private function postFormSucceeded(array $data): void
{
$post = $this->database
->table('posts')
->insert($data);
$this->flashMessage('Post was published successfully.', 'success');
$this->redirect('Post:show', $post->id);
}
Just a quick explanation: it fetches the values from the form, inserts them into the database, creates a message for the user that the post was saved successfully, and redirects to the page where that post is published so that you can see how it looks like.
Page for Creating a New Post
Now let's create the template Edit/create.latte
:
{block content}
<h1>New post</h1>
{control postForm}
Everything should be clear by now. The last line renders the form we just created.
We could also create a corresponding renderCreate()
method, but it's not necessary. We don't need to retrieve any
data from the database or pass it to the template, so the method would be empty. In such cases, the method doesn't need to exist
at all.
Link for Creating Posts
You probably already know how to add a link to EditPresenter
and its create
action. Try it out.
Just add the following to the app/Presentation/Home/default.latte
file:
<a n:href="Edit:create">Write new post</a>
Editing Posts
Now let's add the capability to edit existing posts. It will be very simple. We already have the postForm
form,
and we can use it for editing as well.
We'll add a new edit
page to the EditPresenter
:
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());
}
And create the corresponding template Edit/edit.latte
:
{block content}
<h1>Edit post</h1>
{control postForm}
And update the method postFormSucceeded
, which will be able either to add a new post (as it does now), or to edit
existing ones:
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('Post was published successfully.', 'success');
$this->redirect('Post:show', $post->id);
}
When id
parameter is provided, it means that a post is being edited. In such case, we’ll check that the post
really exists and if so, we’ll update it in the database. If the id
is not provided, it means that a new post shall
be added.
But where does the id
come from? It is the parameter passed to renderEdit
method.
You can now add a link to the app/Presentation/Post/show.latte
template:
<a n:href="Edit:edit $post->id">Edit this post</a>
Summary
The blog is working, people are commenting rapidly and we no longer rely on Adminer for adding new posts. It is fully independent and even normal people can post there. But wait, that’s probably not ok, that anyone, I mean really anyone on the Internet, can post on our blog. Some form of authentication is required so that only logged-in users would be able to post. We'll add that in the next chapter.