Putting MVC Together
The business objects and the Data Access Object (DAO) model are created. Now we show you how you can load the business objects in the controller (C) via the DAO model (M) and pass them to the view (V).
The Overview
So that we can always access the DAO in the controller, we load the BlogDao onto the controller. Insert the following lines directly after the annotation method of qs\controller\BlogController:
private $blogDao;
private function _init(BlogDao $blogDao) {
$this->blogDao = $blogDao;
}
Via a dependency injection you fetch the BlogDao and store it on the controller.
In the index() method we want to show all blog articles.
public function index() {
// fetch the articles
$blogArticles = $this->blogDao->getOnlineBlogArticles();
// pass the articles to the view
$this->forward('..\view\overview.html', array('blogArticles' => $blogArticles));
}
To do so we fetch the $blogArticles via $this->blogDao->getOnlineBlogArticles() and pass them to the view as parameters.
Now adapt qs\view\overview.html.php as follows:
<?php
use n2n\impl\web\ui\view\html\HtmlView;
use n2n\web\ui\view\View;
use qs\bo\BlogArticle;
$view = HtmlView::view($this);
$html = HtmlView::html($view);
// read the blog articles from the parameters
$blogArticles = $view->getParam('blogArticles');
$view->useTemplate('boilerplate.html', array('title' => 'Overview'));
?>
<h1>Our Blog Articles</h1>
<?php foreach ($blogArticles as $blogArticle): $view->assert($blogArticle instanceof BlogArticle) ?>
<article>
<h2><?php $html->out($blogArticle->getTitle()) ?></h2>
<p><?php $html->out($blogArticle->getLead()) ?></p>
<?php $html->linkToController($blogArticle->getUrlPart(), 'read') ?>
</article>
<?php endforeach ?>
In the view we first have to read out the blog articles that we passed to the view in the controller via $view->getParam(). They are now available as an array and we can iterate over them with foreach.
We need $view->assert() to be able to benefit from auto-completion in the IDE and to ensure that we have received the correct objects.
Question
Do you still remember why we do not use the
echofunction in the views and always do the output via the HtmlBuilder ($html)?
With $html->linkToController() we always get a link to the path of the active controller. Via the first parameter we can extend the path. Since we extend it with the URL of the article, we get the path to the detail method of our controller. The second argument is the label for the link. With this instruction we thus link from the overview to the detail view of our blog.
The Detail View
Now adapt the method detail() in the controller:
public function detail(string $urlPart) {
// fetch the article
$blogArticle = $this->blogDao->getBlogArticleByUrlPart($urlPart);
// check whether the article was found
if ($blogArticle === null) {
throw new PageNotFoundException('Invalid urlPart: ' . $urlPart);
}
// pass the article on
$this->forward('~\view\detail.html', array('blogArticle' => $blogArticle));
}
Because we stored the $blogDao on the controller, we can access it via $this in this method too, and read out directly the $blogArticle that matches $urlPart via ->getBlogArticleByUrlPart().
Now it is important that we check whether we received a $blogArticle. If not, you can throw a PageNotFoundException. With this n2n generates a 404 error page. Pass a logical $message as parameter, so that you always know where the 404 error message was caused.
In the forward() method you see that we again make use of passing a parameter to the view — namely the $blogArticle.
The detail view needs a bit more adjustment:
<?php
use n2n\impl\web\ui\view\html\HtmlView;
use n2n\web\ui\view\View;
use qs\bo\BlogArticle;
use n2n\l10n\DateTimeFormat;
use n2n\io\managed\img\impl\ThSt;
$view = HtmlView::view($this);
$html = HtmlView::html($view);
// read the article from the parameters and check it
$blogArticle = $view->getParam('blogArticle');
$view->assert($blogArticle instanceof BlogArticle);
// read the comments of the article
$blogComments = $blogArticle->getComments();
// article title is passed to the template
$view->useTemplate('boilerplate.html', array('title' => $blogArticle->getTitle()));
?>
<h1><?php $html->out($blogArticle->getTitle()) ?></h1>
<p>
<strong><?php $html->out($blogArticle->getLead()) ?></strong>
</p>
<strong>Article categories</strong>
<ul>
<?php foreach ($blogArticle->getCategories() as $blogCategory): ?>
<li><?php $html->out($blogCategory->getName()) ?></li>
<?php endforeach ?>
</ul>
<?php $view->out($blogArticle->getContentHtml()) ?>
<?php $html->linkToController(null, 'To the overview') ?>
First we read out the parameter $blogArticle via $view->getParam(). We can read out the comments of the article directly via $blogArticle->getComments(). Note also that we pass the title of our article to the template.
Further down we then also read out the categories of the article ($blogArticle->getCategories()).
Something new can be seen at $view->out($blogArticle->getContentHtml()). Here we cannot use $html->out(), because it would escape the HTML tags. So as not to open up any security holes, it is extremely important here that it is ensured that no malicious tags can be in the output HTML!
Here too we used $html->linkToController() again to link to another action method. With the null we link to the index method of the active controller.
Controller and Views
In this file we have prepared BlogController, detail.html and overview.html for you to download.