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:

  1. On the homepage, let’s add a “Write new post” link.
  2. It will show a form with title and textarea for content.
  3. 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?

  1. Add the link to homepage template (app/presenters/templates/Homepage/default.latte)
  2. Create a new page (template) which will show the form
  3. Define the form for adding a new post
  4. Define handler which will be executed when the form gets submitted, which will save the post.

You should already know how to add a new link to the homepage. So try it yourself.

If you’re not sure, add this code somewhere to app/presenters/templates/Homepage/default.latte:

<a n:href="Post:create">Write new post</a>

Page for Creating a New Post

The link we've just created points to PostPresenter and its action create. We can add a new method renderCreate, but actually it is not necessary. We don’t need to fetch any data from database and put it into the template, so the method would be empty. In such case, it doesn’t need to exist at all.

If you want, you can have the empty method there; perhaps it’ll have some code later. It’s up to you.

Let’s just create the template (app/presenters/templates/Post/create.latte):

{block content}
<h1>New post</h1>

{control postForm}

Everything should be obvious by now. The last line tries to render the form which we are about to create.

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 PostPresenter:

protected function createComponentPostForm()
{
    $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.

public function postFormSucceeded($form, $values)
{
    $post = $this->database->table('posts')->insert($values);

    $this->flashMessage("Post was published", 'success');
    $this->redirect('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.

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 and update the form handler, which will be able either to add a new post (as it does now), or to edit existing ones.

Add the method to the PostPresenter:

public function actionEdit($postId)
{
    $post = $this->database->table('posts')->get($postId);
    if (!$post) {
        $this->error('Post not found');
    }
    $this['postForm']->setDefaults($post->toArray());
}

Notice that the method is called actionEdit (and not renderEdit, as you may have expected). Render methods are used for passing the data into templates. Actions, on the other hand, may be doing a lot more, they should check if the page can be displayed by the current visitor or user and they should be doing most of the heavy work, that doesn't belong to form handlers. Passing data into the template would of course also work in action methods, but it's recommended not to do it, because of separating responsibilities. Your code is simply more synoptic when you split the code into render and action methods.

Now create the template (app/presenters/templates/Post/edit.latte):

{block content}
<h1>Edit post</h1>

{control postForm}

Let’s also extend the form handler:

public function postFormSucceeded($form, $values)
{
    $postId = $this->getParameter('postId');

    if ($postId) {
        $post = $this->database->table('posts')->get($postId);
        $post->update($values);
    } else {
        $post = $this->database->table('posts')->insert($values);
    }

    $this->flashMessage('Post was published', 'success');
    $this->redirect('show', $post->id);
}

When postId 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 postId is not provided, it means that a new post shall be added.

But where does the postId come from? It is the parameter passed to actionEdit method. You can now add a link to the app/presenters/templates/Post/show.latte template:

<a n:href="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.