7 – View single Blog post

Before we can view a post, even though the view.phtml file is present, we have to include it in our routing.

Open up the module/Blog/config/module.config.php and update the route “blog” with the following child route inside the existing configuration. Underneath the closing options => [], key, paste the following code:

'child_routes' => [
    //routeName: blog/view
    //route: /blog/:id[/slug]
    'view' => [
        'type'          => 'Segment',
        'may_terminate' => true,
        'options'       => [
            'route'       => '/:id[/:slug]',
            'constraints' => [
                'id'   => '[a-f0-9]+',
                'slug' => '[a-zA-Z0-9_-]+',
        ],
        'defaults'    => [
            'module'     => 'Blog',
            'controller' => 'Blog\\Controller\\Post',
            'action'     => 'view',
            ],
        ],
    ],
],

You’ll notice that the route is a bit different from the default /blog route. For a view route we use just variables. In this case we use both :id and :slug. The slug variable is optional, this will allow us to create short permalinks in the future. Where a title (and therefore a slug) may be updated later on, the ID of the post will remain the same.

We’ve also included a constraints => [] key. In this we define each variable and define what it is allowed to contain. We use a Regex pattern to define the constraints(learn regextest your regex).

Now that we have the route setup we’ll go ahead and create the view. Open up /module/Blog/view/blog/post/view.phtml and copy in the following code.

/** @var Blog\Entity\Post $post */
?>
<section>
    <article class="blogPost">
        <header>
            <h2>
                <?= $this->escapeHtml($post->getTitle()) ?>
            </h2>
            <h3>
                <?= $this->translate('Created') ?>: <?= $post->getCreated()->format('d-m-Y') ?>
            </h3>
        </header>

        <div class="blogBody">
            <?= $this->escapeHtml($post->getBody()) ?>
        </div>
    </article>
</section>

It’s very similar to the index.phtml view, so won’t dwell on it.

To now make the view work, open up /module/Blog/src/Blog/Controller/PostController.php and create the viewAction() with the following code:

public function viewAction()
{
    $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('Blog\\Entity\\Post')->find($id);
    if (!$post instanceof Post) {
        throw new \Exception('Something went wrong retrieving this post, please try again or contact us.');
    }

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

Note how we’re getting the ID of the post we wish to show. Zend does some under the hood magic for us and makes parameters used in the URL available to use in a few ways.

Instead of $this->params()->fromRoute(‘id’) we could also do $this->params(‘id’), Zend’s magic would find the ‘id’ parameter and return it to us. However, Zend also translates any parameters passed in the URL after a ?, for example for a search action. This search action might also contain an id parameter, causing a conflict. For this reason it’s better to use the explicite fromRoute() function. (As a side note, use fromQuery() for parameters after a ? in a URL).

Let’s have a quick look at a post to see if what we’ve done has worked. You should see something similare to what’s shown below.

Nothing yet links to the view page though. Open up your index.phtml view (in the Blog module). Replace the contents of the <h2> element, that shows the title of a blog, with the code below:

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

As you can see we’ve put an <a> element (anchor tag) into the <h2> element to surround the title. Now we have a structure going from Home to Blog to a blog Post.

Small refactoring

In your PostController.php file you now have the indexAction() and the viewAction(). Both use the Doctrine EntityManager to get a Repository for the Post entity. However, you might notice that for both of these functions we have typed out the path to the required Entity. Now, with just the 2 functions it’s not much of an issue when we have to refactore. But as we still have 3 functions to create for this tutorial and who knows how many others you’ll add yourself afterwards, refactoring would quickly become a pain.

We’re going to beat the pain to the punch and refactor this now so we won’t have to spend forever fixing this up if it ever were to change. Create a file called Entity.php in the /module/Application/src/Application/Entity folder, next to AbstractEntity.php.

The use for this file is the same as for our ConnectionString.php file. It’s simply to hold constants so that we can re-use something that’s defined in a single location.

Copy the following code into Entity.php:

namespace Application\Entity;

class Entity
{
    const POST  = 'Blog\\Entity\\Post';
}

As you can see, nothing fancy. Re-open the PostController class and replace the strings Blog\\Entity\\Post with Entity::POST. Make sure to include the Entity class at the top of the file (hint: in PhpStorm, place the cursor on the word Entity and press Alt+Enter).