Learn HTML & CSS Learn HTML & CSS
Only this week 80 % discount on HTML & CSS and JavaScript e-learning courses!

Lesson 4 - Wiring controllers and views

In the previous lesson, Router, we created a router and ended the day with determining controller class names and parameters based on a URL address. In today's tutorial, we'll get the system partially working and display a simple article.

Views

Let's start with out with something easy. We'll prepare 2 views, wire them with controllers, and display an article.

Page layout

As you already know, each controller contains the "view that is to be rendered"'s name. The RouterController will display the view along with the page layout, which will consist of a header, navigation menu, footer among other things. However, it will not contain the main content, which will be added in later, by the internal (nested) controller.

You should all know at least a little bit of HTML and CSS at this point, however, I went ahead and prepared a simple layout template for you. Create a layout.phtml file in the "views" folder, and add the following code into it:

<!DOCTYPE html>
<html lang="en">
        <head>
                <base href="/localhost" />
                <meta charset="UTF-8" />
                <title><?= $title ?></title>
                <meta name="description" content="<?= $description ?>" />
                <link rel="stylesheet" href="style.css" type="text/css"/>
        </head>

        <body>
                <header>
                        <h1>ICT.social MVC - Sample website</h1>
                </header>

                <nav>
                        <ul>
                                <li><a href="article/home">Home</a></li>
                                <li><a href="article">Articles</a></li>
                                <li><a href="contact">Contact</a></li>
                        </ul>
                </nav>
                <br clear="both" />

                <article>

                </article>

                <footer>
                        <p>Sample MVC tutorial from <a href="http://www.ict.social" target="_blank">ict.social</a> - The social network for programmers.</p>
                </footer>
        </body>
</html>

There are several things we'll touch base on, in regard to our source code. First of all, it's 99% HTML and clearly well formatted. It uses HTML 5 tags, which should go without saying nowadays.

The head includes a <base> tag, which sets the root folder on a website. This part is important for images, CSS files icons, and so on. Since we agreed on using pretty URLs with slashes, the browser could easily mistake them for subfolders. By setting the root folder, we explicitly state that we're located in the root. Remember that on the production server, you'd have to replace "localhost" with an absolute address like "http://www.do­main.com/". Theoretically, the "/" character shouldn't mess anything up, but a certain browser with a blue "e" does not support this feature.

Notice the PHP directives in the page head:

<title><?= $title ?></title>
<meta name="description" content="<?= $description ?>" />

We take a variable from a controller (from its $data array) and add it to the template. We don't insert HTML into PHP ever, but we may add a very small amount of PHP into HTML, without breaking the HTML structure. I'm sure you can tell that this:

<?= $variable ?>

is a shortened version of that:

<?php echo $variable; ?>

PHP has a lot of shorter syntax for templates to make it easier to manage them without any other means. I often mix the terms "template" and "view". I always mean the same thing, since our MVC essentially has every view as a phtml template.

The last interesting part is the article body, i.e. the part between the <article> tags. Notice how it's empty at the moment. We'll render the view based on the inner controller's data later on.

Error page

Next, we'll create an error page template. It'll be shown in case a user enters an invalid URL. The HTML code for the error.phtml file will be as follows:

<h1>Error 404</h1>
<p>The requested page was not found, please check the URL.</p>

Style

What sort of a website would it be without a bit of style? I've prepared this part for you as well. Create a style.css file in the root folder, and add the following content:

body {
        font-family: verdana;
        font-size: 14px;
        width: 900px;
        margin: 0 auto;
}

h1 {
        text-align: center;
        color: #444444;
        text-shadow: 3px 3px 3px #aaaaaa;
}

footer {
        font-size: 11px;
        text-align: center;
        padding-top: 20px;
}

article {
        text-shadow: 3px 3px 3px #aaaaaa;
}

nav ul {
        list-style-type: none;
}

nav li {
        float: left;
        margin-right: 15px;
}

nav a {
        background: #6FA4F8;
        color: white;
        padding: 5px 10px;
        border-radius: 10px;
        text-decoration: none;
        border: none;
        cursor: pointer;
}

nav a:hover {
        background: #2976f8;
        color: #EEEEEE;
        text-decoration: none;
}

There is nothing interesting here aside from a few CSS 3 properties.

Rendering views

Our views are done and the controllers are done. Now, let's make the application functional at last.

Move back to the RouterController and remove the test outputs in the process() method that we added last time. If no controller is specified (the first URL parameter is empty or missing), we'll redirect the user to the home article. Despite the fact that it doesn't exist yet, we can use its URL.

public function process($params)
{
        $parsedUrl = $this->parseUrl($params[0]);

        if (empty($parsedUrl[0]))
                $this->redirect('article/home');
        // The controller is the 1st URL parameter
        $controllerClass = $this->dashesToCamel(array_shift($parsedUrl)) . 'Controller';

If the script continues, that means we've got a controller's class name, after which we'll check whether the file really exists. If it does, we;ll create an instance of said class. Otherwise, we'll redirect the user to the error page.

if (file_exists('controllers/' . $controllerClass . '.php'))
        $this->controller = new $controllerClass;
else
        $this->redirect('error');

We now have an instance of the inner (nested) controller exactly where we want it to be. Now we'll call the process() method on the inner controller and let it execute its inner logic. For example, it could be looking for an article in the database. To be more accurate, the inner controller's process() method will call the appropriate model's logic, but all of this will be dealt with later on.

$this->controller->process($parsedUrl);

The last thing we have to do now is set the router's view, which is the website layout's template.

Let's create a few variables for the template. We already know that we used the $title and $description variables in there. We also know that we pass variables to the view as the $this->data[] array's keys. When it comes to template variables, we'll add the inner controller's title and description. The code will be the following:

$this->data['title'] = $this->controller->head['title'];
$this->data['description'] = $this->controller->head['description'];

We still have to set the view, which we'll do simply by setting the template's filename to the $view property:

// Sets the main template
$this->view = 'layout';

The RouterController is done!

ErrorController

Now, let's create a controller for an actual web page. This controller will be for the error page. Create an ErrorController.php file in the "controllers" folder with the following contents:

class ErrorController extends Controller
{
    public function process($params)
    {
        // HTTP header
        header("HTTP/1.0 404 Not Found");
        // HTML header
        $this->head['title'] = 'Error 404';
        // Sets the template
        $this->view = 'error';
    }
}

The controller sends a header to the browser to inform it that it's on an error page. Aside from that, it doesn't do anything other than setting the title and the template.

We're done! That was fast, wasn't it? We'll add next couple of system parts in the same way.

Wiring up

Time to wrap it all up! Go ahead and call the renderView() method on the router in index.php, right after calling its process() method. Here, we can clearly see how the logic and output are separated into two individual tasks.

$router->renderView();

When we enter a URL address, we'll be redirected to the error page, where the router will call the ErrorController. Here, we see that router worked and rendered the template for us:

ICT.social MVC framework in PHP – Rendering the layout

The inner controller's template (ErrorController) has not been rendered, let's make it right. We'll insert this template in the empty spot in the <article> tag in the layout.phtml template. Since the template is currently being processed by the RouterController accessing the inner controller as an instance property is fairly straightforward. We won't insert things like this in views, but we pretty much have to in layouts:

<?php $this->controller->renderView();

The result:

ICT.social MVC framework v PHP – Rendering the view

Finally, we have something functional. Even though it's just an error page :) In the next lesson, Contact form, we'll add a simple contact form into our system.


 

Download

Downloaded 230x (9.91 kB)
Application includes source codes in language PHP

 

 

Article has been written for you by David Capka
Avatar
Do you like this article?
2 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn College The author learned IT at the Unicorn College - a prestigious college providing education on IT and economics.
Previous article
Router
All articles in this section
Simple object-oriented CMS in PHP (MVC)
Thumbnail
Next article
Contact form
Activities (7)

 

 

Comments
Display older comments (5)

Avatar
Agostino I
Member
Avatar
Agostino I:5. July 9:28

It seems that in next articles we'll set them, now they don't have values

Reply 5. July 9:28
while(1){ workHard();} but sometimes....take a rest! :)
Avatar
David Capka
ICT.social team
Avatar
Replies to Agostino I
David Capka:6. July 6:46

These are assigned in the particular inner controller (e.g. ArticleController) and then passed to the template from it. So every controller can change the page title.

Reply  +1 6. July 6:46
You can walk through a storm and feel the wind but you know you are not the wind.
Avatar
Agostino I
Member
Avatar
Agostino I:9. July 14:41

When in the RouterController code(in the process method) we have:

if (file_exists('con­trollers/' . $controllerClass . '.php'))
$this->controller = new $controllerClass;
else
$this->redirect('error');

// Calls the controller
$this->controller->process($par­sedUrl);

// Setting template variables
$this->data['title'] = $this->controller->head['title'];
$this->data['descrip­tion'] = $this->controller->head['descrip­tion'];
// Sets the main template
$this->view = 'layout';
}

$this->redirect('error'); is executed because we don't have another controller: so the "ball is passed" to the ErrorController right? It sets the value of $view to 'error' and shouldn't be executed so the rest part of the code where you set the variable $view to 'layout'

Reply 9. July 14:41
while(1){ workHard();} but sometimes....take a rest! :)
Avatar
Agostino I
Member
Avatar
Agostino I:9. July 14:46

and when in the RouterController code we have:

$this->controller->process($par­sedUrl);

$this->controller shouldn't be blank since we don't have instantiated a controller?

Reply 9. July 14:46
while(1){ workHard();} but sometimes....take a rest! :)
Avatar
David Capka
ICT.social team
Avatar
Replies to Agostino I
David Capka:9. July 16:34

Yes, $this->redirect('error'); creates a new request which is processed like any other URL by the application, resulting in calling ErrorController and rendering the error.phtml view assigned in this particular controller.

$this->controller shouldn't be blank, if we were unable to instantiate the controller, we redirected to error:

if (file_exists('controllers/' . $controllerClass . '.php'))
        $this->controller = new $controllerClass;
else
        $this->redirect('error');
Edited 9. July 18:26
Reply  +1 9. July 16:34
You can walk through a storm and feel the wind but you know you are not the wind.
Avatar
Agostino I
Member
Avatar
Replies to David Capka
Agostino I:10. July 11:56

We redirect to ErrorController, but then is however exectued the following code of the routerController:

// Calls the controller
$this->controller->process($par­sedUrl);

// Setting template variables
$this->data['title'] = $this->controller->head['title'];
$this->data['descrip­tion'] = $this->controller->head['descrip­tion'];
// Sets the main template
$this->view = 'layout';

right?

Reply 10. July 11:56
while(1){ workHard();} but sometimes....take a rest! :)
Avatar
David Capka
ICT.social team
Avatar
Replies to Agostino I
David Capka:11. July 4:54

The line $this->redirect('error'); tells the browser to change the URL and stops the script immediately:

public function redirect($url)
{
    header("Location: /$url");
    header("Connection: close");
        exit;
}

When a new URL is opened (error in our case), the code of the RouterController is executed as you've mentioned.

Reply 11. July 4:54
You can walk through a storm and feel the wind but you know you are not the wind.
Avatar
Agostino I
Member
Avatar
Replies to David Capka
Agostino I:13. July 9:33

But if the script is stopped, so the code that is after

else
                        $this->redirect('error');

that is:

// Calls the controller
                $this->controller->process($parsedUrl);

                // Setting template variables
                $this->data['title'] = $this->controller->head['title'];
                $this->data['description'] = $this->controller->head['description'];
                $this->data['messages'] = $this->getMessages();
                // Sets the main template
                $this->view = 'layout';

should not be executed...or we have the redirect and is however processed the code at the end of RouterController?

I am in trouble to understand if,after the redirection, is however executed the remaining code or if it's stop the execution: I see that is however processed because it is set the view at 'layout' value, but so redirect doesn't stop the script...

Reply 13. July 9:33
while(1){ workHard();} but sometimes....take a rest! :)
Avatar
Agostino I
Member
Avatar
Replies to David Capka
Agostino I:23. July 12:50

I think I got the point:

if there isn't a valid URL, error becomes the URL and $router->process(arra­y($_SERVER['RE­QUEST_URI']));

give to process method of routerController the string "error" and so the ball is passed to the errorController, right?

Reply 23. July 12:50
while(1){ workHard();} but sometimes....take a rest! :)
Avatar
David Capka
ICT.social team
Avatar
Reply  +1 24. July 5:16
You can walk through a storm and feel the wind but you know you are not the wind.
To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

10 messages from 15 displayed. Show all