Creating and Editing Posts
What a great time. We have a super cool new blog, people are arguing in comments and we have finally some time for more programming. Though we like Adminer, it is not that comfortable to write blog posts in it. Perhaps it's the right time to add a simple form for adding new posts directly from our app. Let’s do it.
Let’s start by designing the UI:
- On the homepage, let’s add a “Write new post” link.
- It will show a form with title and textarea for content.
- When you click a Save button, it’ll save the blog post.
Later we’ll also add authentication and allow only logged-in users to add new posts. But let’s do that later. What code will we need to write to make it work?
- Create a new presenter with a form for adding posts.
- Define a callback that will be triggered after successful submission of the form and that will save the new post to the database.
- Create a new template for the form.
- Add a link to the form to the main page template.
New Presenter
Name the new presenter EditPresenter
and save it in app/UI/Edit/
. It also needs to connect to the
database, so here again we write a constructor that will require a database connection:
<?php
namespace App\UI\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 have been already covered when we were adding support for comments. If you're confused about the topic, go checkout how forms and components work again, 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 a handler method.
private function postFormSucceeded(array $data): void
{
$post = $this->database
->table('posts')
->insert($data);
$this->flashMessage('Post was published', '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
Let’s just create the template Edit/create.latte
:
{block content}
<h1>New post</h1>
{control postForm}
Everything should be clear by now. The last line shows the form we are about to create.
We could also create a corresponding renderCreate
method, but it is not necessary. We don't need to get any data
from the database and pass it to the template, so that method would be empty. In such cases, the method may not 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 to the app/UI/Home/default.latte
file:
<a n:href="Edit:create">Write new post</a>
Editing Posts
Let’s also add the capability to edit existing posts. It shall be pretty simple – we already have postForm
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 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', '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/UI/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.