12 – Edit Blog post

By now the process of creating a new action should be familiar. First up we’re going to create the route, then the view, then the action, followed lastly with whatever is needed for the action and the view.

First up, create a child route within the view route of the Blog module by copying the code below into the view array of the routes config file.

'child_routes' => [
    //routeName: blog/view/edit
    //route: /blog/:id[/slug]/edit
    'edit' => [
        'type'          => 'Literal',
        'may_terminate' => true,
        'options'       => [
            'route'    => '/edit',
            'defaults' => [
                'module'     => 'Blog',
                'controller' => 'Blog\\Controller\\Post',
                'action'     => 'edit',
            ],
        ],
    ],
],

Next up, lets fill in the edit.phtml file. It is identical to the add.phtml, although with the edit action the id field will actually be used.

/** @var Blog\Form\PostForm $form */
?>

<section>
    <?php $form->prepare() ?>
    <?= $this->form()->openTag($form) ?>
    <?= $this->formRow($form->get('csrf')) ?>

    <?= $this->formRow($form->get('id')) ?>
    <?= $this->formRow($form->get('title')) ?>
    <?= $this->formRow($form->get('body')) ?>

    <?= $this->formRow($form->get('submit')) ?>
    <?= $this->form()->closeTag($form) ?>
</section>

Next up, we create the editAction in the PostController. Copy in the code below and we’ll have a look at the differences when compared to the addAction.

public function editAction()
{
    $id = $this->params()->fromRoute('id');
    if (empty($id)) {
        throw new \Exception('Id must be given; if given, it was not found.');
    }

    /** @var Post $post */
    $post = $this->getEntityManager()->getRepository(Entity::POST)->find($id);
    if (!$post instanceof Post) {
        throw new \Exception('Something went wrong retrieving this post, please try again or contact us.');
    }

    /** @var PostForm $form */
    $form = $this->getServiceLocator()->get('post_form');
    $form->get('submit')->setValue('Update post');

    /** @var Request $request */
    $request = $this->getRequest();
    if ($request->isPost()) {
        $form->setData($request->getPost());

        if ($form->isValid()) {
            /** @var Post $post */
            $post = $form->getObject();
            $post->generateSlug();

            //Save post to DB
            $this->getEntityManager()->persist($post);
            $this->getEntityManager()->flush();

            //Redirect user to view new post
            $this->redirect()->toRoute('blog/view', [
                'id'   => $post->getId(),
                'slug' => $post->getSlug(),
            ]);
        }
    } else {
        // Bind object so existing values are set when viewing form, but only before posting updated post
        $form->bind($post);
    }

    return [
        'form' => $form,
    ];
}

As you can see, the action is largely the same. There are a few differences though. At the start of the function we first have to get a post to edit when a user requests the page. So we get the parameter from the route. If none is given an error must be given.

Next up, if the id is set, we try to use it to get a post, which we then verify by using the instanceof method. Again we throw an error if this check doesn’t pass.

We then get the form in the same manner as for the addAction. However, after we get it, we get the submit element and change its value. With a submit type element the value is also the text that gets displayed to the user, therefore we change it to show Update post instead of its original message.

After that we handle it the same we as we do the addAction to save the changes.

On a first request to view the edit page, so that a post may be edited, it won’t pass the if ($request->isPost()) {}. However, if we return the $form element as it is, there will be no data to be displayed in the elements on the client-side. So we add the else {} clause to the if(){} statement and bind the $post we got earlier to the $form.

The reason we put this in the else {} is that we do not want the bind() to execute if a user sends us the form with modified values but it fails the if($form->isValid()) the rebinding of the $post to the $form would overwrite the results of the isValid() function. These get added to the $form object with Zend magic. These validation results also get displayed to the user in case they try to do something that doesn’t pass the filters and validators that we’ve added to the $form element.

Last up, one more change we have to do. As it is right now, the page would technically function, however, a user won’t find a link to it.

Lets now edit the view.phtml. When on the view page you should be able to go the edit page from there. Copy the following code in below the <header> element.

<div class="blogOptions">
    <a href="<?= $this->url('blog/view/edit', [
        'id'   => $this->escapeHtml($post->getId()),
        'slug' => $this->escapeHtml($post->getSlug())
    ]) ?>"
       title="<?= $this->translate('Edit') ?> - <?= $this->escapeHtml($post->getTitle()) ?>">
        <?= $this->translate('Edit') ?>
    </a>
</div>

Now when you visit a blog post you will find a link saying “Edit”, allowing you to use the earlier functionality to edit the post.