Let’s make sure we can also view a single, complete, 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'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 regex
, test 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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
/** @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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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:
1
2
3
4
5
6
7
<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
:
1
2
3
4
5
6
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).