So far in this series we have looked purely at the framework, or building blocks of our application. It has not been pretty, and we have been focussing mainly on our underlying code. In this article we will look at how we can add some structure and design elements to our application. So far, we have ugly white screens that simply display un-styled menus and forms. In order to make our framework, and our application easily customisable, I want to be able to swap and change between templates and styles easily within my application. For example, I may want some content to show full-screen with discreet menu’s or I may want other pages to provide styling that is different from other pages within my application. To do this, we will modify the way we render our Views, and the App class’s render() method, and we will also add some code to our View class in order to be able to change the template for each View. Let’s begin by modifying our App’s render() method and the View class.

<?PHP //core/app.class.php -- updates only --
	private function routeRequest(){
		if(isset($this->URL[0])){
			if(class_exists($this->URL[0])){
				$this->Controller = $this->URL[0];
				parent::$CurrentController = $this->URL[0];
			}
		}
		$ControllerName = 'new'.$this->Controller;
		$this->Controller = self::$ControllerName();
		
		if(isset($this->URL[1])){
			if(method_exists($this->Controller, $this->URL[1])){
				$this->Method = $this->URL[1];
				parent::$CurrentMethod = $this->URL[1];
			}
		}
		
		$this->Params = $this->URL ? array_values($this->URL) : [];
		call_user_func([$this->Controller, $this->Method], $this->Params);
	}
	
	public function render(){
		$this->Controller->View->render();
	}
?>
	
<?PHP // core/view.class.php -- updates only --
	
	protected $Template = 'Standard';
	
	public function getTemplate(){
		return new $this->Template;
	}
?>
	
<?PHP // index.php -- updates only --
	include_once '../vendor/autoload.php';
	include_once 'core/password.php';
	//Set Custom Session Handler
	App::setSessionHandler();
	$App = new App($_SERVER['REQUEST_URI']);
	?>
	<html>
		<head>
		...

In the app.class.php file, we have simply moved the call_user_func() method call into our routeRequest() method. In the last article, we moved this into the App class’s render() method, however that meant we could only access information relating to the View to be rendered as it was actually rendered. Now that we wish to include stylesheets and images etc, we actually need to be able to know the View, and its required template before we render the view to the screen. To do this, we allow the App class to call on our Controller and the appropriate Method, however, we will not call the View->render() method from our Controller, we will now use the App->render() method to render the view. This allows us to run the Controller and Method required, and access information about the view to load our templates and resources, and then render the view when we are ready to display the page content. To do this, We need to remove the $this->View->render() method call from each of our Controller’s Methods, luckily at this point we do not have many to update. You will notice our App’s render() method now calls on the Controller’s View to render the content.

In the View.class.php file, we have added a protected $Template variable, and assigned a default of ‘Standard’. We also define a method getTemplate() which returns a new instance of the class whose name matches the $Template value. In this example, we will create a class named Standard which will provide information about the template.

Next, we need to modify the index.php file, and the only change we have made so far is to move the $App = new App(); call to very near to the top of the file. This will enable us to load resources using the <head> section of our html page, remembering that the View’s content will not render until we now call $App->render();

We can now begin work on our template. In this case we will begin by creating the Standard template.

<?PHP	// templates/standard.class.php

class Standard {
	protected $StyleSheet = 'public/default.css';
	
	public function __construct(){
		
	}
	
	public function getStylesheet(){
		return $this->StyleSheet;
	}
} 
	
	
?>

// public/default.css
body {
	background-color: blue;
}

At this stage, the template class is very simple. We have defined a $StyleSheet variable, and stored the location and name of the default CSS file to include, to make things simple I have named this default.css . We have also defined a getStylesheet() method, that returns the stylesheet to be used for this template. That is all we will need in our template demonstrate that our template system is working.

I have also included a very basic default.css file, which sets the background colour of the page to blue. Again, this is just to demonstrate our template system. We now need to tell our index.php file how to find and load the items we need for our template.

// index.php -- <head> section only --
	<head>
		<title>A SaaS Experience</title>
		<link rel="stylesheet" href="/public/zebra_form.css">
		<script src="/public/jquery.js"></script>
		<script src="/public/zebra_form.js"></script>
		<?PHP
			$Template = $App->getTemplate();	
		?>
		<link rel="stylesheet" href="/<?PHP echo $Template->getStylesheet(); ?>">
	</head>
			
<?PHP	// core/app.class.php -- additions only --
	public function getTemplate(){
		return $this->Controller->View->getTemplate();
	}
?>

In the head section of our index.php page, we create a $Template variable, and call on the App class’s getTemplate() method. This will return an instance of the template class for us. We can then include the stylesheet for the template, inserting the value we get back from the template’s getStylesheet() method. This will allow us to load different CSS files for each template we wish to use.

We also need to add the getTemplate() method to our App class in order for our new code to work. This method quite simply calls on the current Controller’s View to find the correct template.

If we now load up our Dashboard, or any of our pages, we should see a blue background.

To demonstrate how easy it is to now change templates for a particular view, I have created a template called Alternate, and a new CSS file called alternate.css in exactly the same way that we have done with our Standard template, but I have made the background green. In the DashboardView class, we now just need to set our $Template variable to ‘Alternate’.

<?PHP	// templates/alternate.class.php

class Alternate {
	protected $StyleSheet = 'public/alternate.css';
	
	public function __construct(){
		
	}
	
	public function getStylesheet(){
		return $this->StyleSheet;
	}
} 
	
	
?>

	// public/alternate.css
	body {
		background-color: green;
	}

<?PHP	// views/dashboardview.class.php -- additions only --
	protected $Template = 'Alternate';
?>

The Dashboard should now load with a green background, as shown below.

All of our other pages will load with a blue background by default, unless we set the $Template to ‘Alternate’ in any view we wish to be green. While this is a simple change, and our application is now even more unreadable and ugly, we have now demonstrated the ease of which we can now customise the look and feel of our application. By creating new templates, we are able to fully customise each page. So far we have looked at adding simple CSS to style the page, but now we will begin adding some structure to our application with our Standard template.

<?PHP // templates/standard.class.php -- additions only --
		public function getPreContent($Internal){
		//Start HTML String
		$html = '<link href="https://fonts.googleapis.com/css?family=Exo|Marvel" rel="stylesheet">';
		
		//Header
		$html .= '<div id="wrapper">
		<div id="header">
			<div id="headercontent">
			<div id="headerleft">
			<h1>Buzi Project Manager</h1>';
		
		if($Internal){
			$Path = App::newPath();
			$html .= '<div id="path">';
			$i = 0;
			foreach($Path->Path AS $Item){
				if($i != 0){
					$html .= ' / ';
				}
				$html .= '<a href="/'.$Item->Controller.'/'.$Item->Method.'" alt="'.$Item->MenuTitle.'">'.$Item->MenuTitle.'</a>';
				$i++;
			}
			$html .= '</div>';
		}
		
		$html .= '</div>
		<div id="headerright">
		<p>'.App::$User->Name.' '.App::$User->Surname.' | Logout
		<p>Buzi IT Pty Ltd
		</div>
		</div></div>';
				
		if($Internal){
			$html .= $this->getPreSecureContent();
		}
		
		//start content space
		$html .= '<div id="content">';
		
		return $html;
	}
	
	public function getPreSecureContent(){
		//Start HTML String
		$html = '';
		
		//Top Level Menu
		$html .= '<div id="topmenucontainer"><div id="topmenu">';
		$Menu = App::newMenu(1);
		$html .= $Menu->Menu;		
		$html .= '</div></div>';
		
		$html .= '<div id="secondmenucontainer"><div id="secondmenu">';
		$Menu = App::newMenu(2);
		$html .= $Menu->Menu;		
		$html .= '</div></div>';
		
		$html .= '<div id="thirdmenu">';
		$Menu = App::newMenu(3);
		$html .= $Menu->Menu;		
		$html .= '</div>';
		
		return $html;
		
	}
	
	public function getPostSecureContent(){
		//Start HTML string
		$html = '';
		
		$html .= '<p>Some secure footer content';
		return $html;
	}
	
	public function getPostContent($Internal){
		//Start HTML string
		$html = '';
		
		//Close Content Area
		$html .= '</div>';
		
		//Footer
		$html .= '
		<div id ="footer">';
		if($Internal){
			$html .= $this->getPostSecureContent();
		}
		$html .= '<p>Application by Buzi IT Pty Ltd</div></div>';
		
		return $html;
	}
?>

To add some structure to our user interface, I have defined 4 methods which we can use to add HTML to our template. These methods are reasonably simple, and there are methods to getPreContent, which will generate all the HTML to be inserted into our page before the content of the page is rendered, getPreSecureContent which we will only call within our getPreContent if the page is an internal page, and then getPostContent and getPostSecureContent which will be inserted after our page content.

The methods may look a little involved, however they simply start an HTML string in a $html variable, and then add the required HTML items to the template. You will see that in the method declaration of the getPreContent and getPostContent, we ask for a variable named $Internal. When we call these methods from our index.php file we will provide the $Internal value. The getPreSecureContent and getPostSecureContent will only be called if the value of $Internal is true. This will save us showing menu’s on external pages and prevent us from accidentally showing data to a user who is not signed in.

In the getPreContent method, we add a link to some custom fonts from Google Fonts, we then add a DIV to the page so we can style our content easier. Next, we define a header DIV, and structure some header content using appropriately named DIV’s. This is not a lesson in HTML or CSS, but is designed to show how we can customise our template to work within our framework, if you need to learn HTML or CSS, you will need to do so before understanding some of the contents of this article.

As part of our header, I want to be able to show the user their current location in the Application. Some developers refer to this as the breadcrumb or breadcrumb trail. To add this, we first check the value of $Internal, if it is true, we can add our breadcrumb trail (we have referred to this as our path in previous articles). We have already developed a Path model as part of our framework, so obtaining the path is very easy. We simply need to ask for a newPath from our App. We then start by adding a DIV named “path” to our HTML. Next we set a counter to 0 to track how many items have been processed, and then loop through our Path to add each item to the HTML. If the counter’s value is not equal to 0 we will add in a “/” before the path item as a separator. By checking the value of the counter we can ensure we do not place a separator before our first item, which may look odd. For each item, we then add a link to the page the item refers to, and set the item’s MenuTitle as the visible text. e then increment our counter, and close off our path DIV. The page will now show a breadcrumb trail for us, and Users will be able to click on any item in the breadcrumb trail to go directly to that page.

In the header, I have also added a “headerright” DIV, which contains the currently logged in User’s name, as well as a logout link (currently not a link as we have not created a logout Controller or Method at this point) and the User’s Company (at this stage hard coded to show how we can build our template). We can now close off all of the open header DIVs.

Next, we want to add our menus to the template, but only if we are on an internal page. We can do this by checking the value of $Internal, and then calling the getPreSecureContent method and adding the returned HTML to our $html string. The getPreSecureContent method starts an empty HTML string, then adds DIVs for each menu level, requests the relevant menu from the App, and adds the returned menu to the HTML string. We then close each of the relevant menu DIVs and return the content to our getPreContent method.

The getPostSecureContent and getPostContent methods work in exactly the same way, simply building an HTML structure for our template. To enable this to work within our App, we need to modify our index.php file to call for the getPreContent and getPostContent methods.

<?PHP	// index.php -- entire updated file --
include_once '../vendor/autoload.php';
include_once 'core/password.php';
//Set Custom Session Handler
App::setSessionHandler();
$App = new App($_SERVER['REQUEST_URI']);
?>
<html>
	<head>
		<title>A SaaS Experience</title>
		<link rel="stylesheet" href="/public/reset.css">
		<link rel="stylesheet" href="/public/zebra_form.css">
		<script src="/public/jquery.js"></script>
		<script src="/public/zebra_form.js"></script>
		<?PHP
			$Template = $App->getTemplate();	
		?>
		<link rel="stylesheet" href="/<?PHP echo $Template->getStylesheet(); ?>">
	</head>
	
	<body>

<?PHP
	
//Add items before content render, eg menu/header
echo $Template->getPreContent($App->requestIsInternal());

// Render page content
$App->render();


//Add items after content render, eg footer
echo $Template->getPostContent($App->requestIsInternal());

?>

	</body>
</html>

I have included the entire index.php file for clarity, however all we have changed is within the <body> and </body> HTML tags. We have added a call to our $Template->getPreContent, providing the response for a call to our App’s requestIsInternal method. This will allow our template to determine whether it is allowed to add secure content. We then call our App’s render method to display the page content, and then call our $Template->getPostContent method and again provide the value of $Internal. These 3 calls will add the template’s HTML structure before and after our page’s current content.

We now have the ability to fully customise each page within our application using custom templates and CSS, we only need to ensure that our template contains a getPreContent and getPostContent method, that returns HTML to our application. You can customise the HTML structure any way you wish. I have created a very simple interface, shown below, using the template shown above, and I have included the CSS for this interface below the example image.

// public/default.css

body {
	background-color: #f3f3f3;
	margin: 0px;
	padding: 0px;
	font-family: 'Marvel', sans-serif;
	height: 100%;
}

#header {
	background-color: #424242;
	color: #ffffff;
	padding: 25px;
}

#headercontent {
	width: 1000px;
	margin: auto;
	display: flex;
	flex-direction: row;
	justify-content: space-between;
}

#headercontent h1 {
	font-size: 35px;
	font-family: 'Exo', sans-serif;
}

#headercontent #path {
	margin-top: 15px;
}

#headerright p {
	margin: 5px;
}

#headerright {
	margin-top: auto;
	margin-bottom: auto;
}

#header a {
	color: #fff;
}

#header a.visited {
	color: #fff;
}

#header a:hover {
	color: orange;
}

#topmenu {
	width: 1000px;
	margin-left: auto;
	margin-right: auto;
	display: flex;
	flex-direction: row;
	justify-content: flex-start;
}

#topmenucontainer {
	background-color: #616161;
}

#topmenu a {
	color: white;
	padding: 15px;
	font-size: 20px;
}

#topmenu a {
	text-decoration: none;
}

#topmenu .current {
	background-color: grey;
	color: white;
	border-top-left-radius: 4px;
	border-top-right-radius: 4px;
}

#topmenu a:hover{
	color: orange;
}

#secondmenucontainer {
	width: 100%;
	background-color: grey;
}

#secondmenu {
	width: 1000px;
	margin-left: auto;
	margin-right: auto;
	display: flex;
	flex-direction: row;
	justify-content: flex-start;
}

#secondmenu a {
	color: white;
	padding: 10px;
	font-size: 20px;
}

#secondmenu .current {
	background-color: #f3f3f3;
	color: #616161;
	border-top-right-radius: 5px;
	border-top-left-radius: 5px;
}

#secondmenu a:hover{
	color: orange;
}

#thirdmenu {
	width: 1000px;
	margin-left: auto;
	margin-right: auto;
	display: flex;
	flex-direction: row;
	justify-content: flex-start;
}
#thirdmenu a {
	color: #616161;
	padding: 10px;
	font-size: 15px;
}

#thirdmenu a:hover{
	color: orange;
}

#content {
	width: 1000px;
	margin-left: auto;
	margin-right: auto;
	padding-bottom: 100px;
	border: 1px solid grey;
	background-color: white;
	padding: 20px;
	border-radius: 4px;
	margin-top: 10px;
	margin-bottom: 10px;
}

#wrapper {
	min-height: 100%;
	position: relative;
}

#footer{
	height: 50px;
	width: 100%;
	background-color: #616161;
	position: absolute;
	bottom: 0px;
	text-align: center;
	padding: 20px;
	color: #f3f3f3;
}

Whilst the example interface is quite basic, it shows how easy it is to customise the look and feel of our application. You can add images, make the menus vertical, the sky is the limit. You could even develop a number of interface themes, and allow your users to select the theme they wish to use. For my final version of this application, I will be creating a Login theme, a Logout theme, and possibly another internal theme to be used when displaying graphs and data without the need for a full header and menu system. Our application now has some structure, and we are nearly ready to begin working on our application, as opposed to the framework that supports it. We can now begin working on features of our application! The first, will be to add Clients to our datastore, so that we can then add projects, and assign clients to them. You will notice in the next article, there may be changes to the template, and menu items may be in different locations and order, as I will take some time to tidy up all of the work we have done so far, as this does mark the transition from “Framework” to “Application” development.