This Premium video and companion article will teach you how to create a simple yet powerful library to handle layouts in the popular CodeIgniter Framework. The library you'll create will allow you to maximize your efficiency, save time and code, modularize your views and even your Javascript and CSS files.
Step 1 Download Required Files
For this tutorial, all you're going to need is the CodeIgniter 1.7.2 framework. You can download it from their website at codeigniter.com/downloads/
Step 2 How We'll Do This
The basic functionality of this library will be very simple. We'll take the contents of a view, render them with the appropriate data, then take the rendered content and assign it to a variable. Now, we'll render the layout itself, and replace a part of the layout with the contents of this variable. Simple, but powerful enough.
The idea is to mimic the calls to $this->load->view().
When we call this method, we pass the name (and location) of our view, and then an array of data that will be accessible from the view. Here's an example:
function method($url_param) { $this->load->view('controller_views/method_view', array('url_param' => $url_param)); }
The above code will take the file system/application/views/controller_views/method_view.php
, pass it the url_param variable, and then send it to the browser. Here's where we come in. We will not send the content to the browser yet. Instead, we'll send it to the layout, then to the browser. But how do we do that?
The view()
method we just called can be passed a third (boolean) parameter, that, if true, will return the rendered view instead of sending it to the browser. We can save that content, perform a second call to the same method, but this time call a layout file that will print this content (surrounded with all headers, sidebars and footers).
Full Screencast
Step 3 Create a New Library
We'll create this library step by step, starting from the most basic. First off, we'll create a new library in our system/application/libraries folder and call it Layouts.
If you've never created a CodeIgniter library, they're simply classes, which get loaded by a call to
$this->load->library()
.
So, let's jump straight into the code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * Layouts Class. PHP5 only. * */ class Layouts { public function __construct() { } }
Let's go through each section of the code:
- The very first line is a coding convention of CodeIgniter, it essentially makes sure users can't directly access the file from their browsers, because CodeIgniter sets the
BASEPATH
constant in itsindex.php
file. - The class will be PHP 5 only. This will allow us to add method chaining to the class, which will be useful later when we work with JS and CSS includes.
- The class constructor has nothing for the time being. This is just the skeleton of our library.
So, what happens if we include this from a controller
? Well, nothing. The class does absolutely nothing for now (not even the constructor), so nothing will happen.
Step 4 Create a Layout
We'll create a very simple layout to explain how all works.
<!DOCTYPE HTML> <html> <head> <title>Our very first layout!</title> </head> <body> <?php echo $content_for_layout; ?> </body> </html>
As you can see, this is extremely basic; it's just a title and a body. Now the important part is in the PHP code there. We echo a $content_for_layout
variable. The idea is to assign the rendered content to this variable. This way, it'll get printed there, surrounded with the rest of the body, head, etc.
Step 5 Write Some Code!
Let's write some code to handle this layout:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * Layouts Class. PHP5 only. * */ class Layouts { // Will hold a CodeIgniter instance private $CI; public function __construct() { $this->CI =& get_instance(); } public function view($view_name, $params = array(), $layout = 'default') { // Load the view's content, with the params passed $view_content = $this->CI->load->view($view_name, $params, TRUE); // Now load the layout, and pass the view we just rendered $this->CI->load->view('laytous/' . $layout, array( 'content_for_layout' => $view_content )); } }
Let's explain what the new code looks like:
- We added a new private attribute to our library:
$CI
. From within our libraries, we can't access the CodeIgniter instance directly. The only way is to get a reference to it and access it from there. So, in our constructor (which gets called when the library is loaded), we get our CI instance, and assign it to our local private$CI
attribute, so we can call it later. We need it to call theload->view()
method. - Now, we added a view method. The syntax is practically identical to the
load->view()
method. We get a view name, an array of parameters (the variables that will be visible from the view), and a layout name, which by default will be (duh) 'default'. The latter allows us to have multiple layouts in our application (maybe one for the login box without menus and stuff). - Now, as we spoke earlier, we call the
load->view()
method, we pass the view's name, the params, and a third parameter with the value ofTRUE
. This ensures that we will not send the output to the browser. Instead it will be returned to us, and assigned to the variable$view_content
. - Finally, we load the layout file (which we will store in the
system/application/views/layouts
folder), and pass the content of the just loaded view as a parameter. When the layout gets loaded, the$content_for_layout
variable will be replaced with the content just loaded, and will be sent to the browser (note the missing final parameter, we don't passTRUE
this time).
Step 6 Change the Title of the Page
The basic library is technically done. But there are a couple of things we can add to it to make it even better.
As it is, the title of the layout is always the same. This is not practical. We need to be able to change it easily from our controllers, without having to create an infinite ammount of layouts with different titles (this would defeat the purpose of this tutorial). So, how do we do this? We'll suppose the site has a permanent title, say "Layouts Library". After that, we would put the section of the site we're visiting. For example, for the login page, the title would read "Layouts Library | Login".
First, let's rewrite the layout a bit.
<!DOCTYPE HTML> <html> <head> <title>Layouts Library<?php echo $title_for_layout ?></title> </head> <body> <?php echo $content_for_layout; ?> </body> </html>
We just added another PHP echo
. This time we print the $title_for_layout
variable, which we'll tweak in our library. Here's the rewritten library:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * Layouts Class. PHP5 only. * */ class Layouts { // Will hold a CodeIgniter instance private $CI; // Will hold a title for the page, NULL by default private $title_for_layout = NULL; // The title separator, ' | ' by default private $title_separator = ' | '; public function __construct() { $this->CI =& get_instance(); } public function set_title($title) { $this->title_for_layout = $title; } public function view($view_name, $params = array(), $layout = 'default') { // Handle the site's title. If NULL, don't add anything. If not, add a // separator and append the title. if ($this->title_for_layout !== NULL) { $separated_title_for_layout = $this->title_separator . $this->title_for_layout; } // Load the view's content, with the params passed $view_content = $this->CI->load->view($view_name, $params, TRUE); // Now load the layout, and pass the view we just rendered $this->CI->load->view('laytous/' . $layout, array( 'content_for_layout' => $view_content, 'title_for_layout' => $separated_title_for_layout )); } }
What did we do here?
- We added two new attributes to our library:
$title_for_layout
and$title_separator.
The first will hold our title, and the second, will define the string that will separate the title of the layout from the title set by theset_title()
method. - Since the
$title_for_layout
was set to private, we add a method to set it from our controllers. Thus,set_title()
will set the value of$title_for_layout
to whatever we tell it to, eg. 'Login'. - In the
view()
method, we added a chunk of code to handle the new attribute. If the user never sets a title for the page, we want to be able to "degrade gracefully", ie. not append the separator for nothing. Thus, we first check the value of the$title_for_layout
attribute. If notNULL
, then we append the configured separator and the title set by theset_title()
method. - When rendering the layout, we make sure we pass the new attribute (even if NULL), so we can echo it in the title.
Step 7 Adding modular CSS and JS
Now, for last, we want to be able to add CSS and Javascript files modularly. What does this mean? Say you want to use a jQuery plugin, but you just want to use it on a single part of the website (maybe a form validation plugin). You could just include it on the view itself, but that doesn't look very good on the final code. It's always preferable to have all Javascript (and CSS) includes in the header
. We're going to create a method (well, two actually) that will allow us to do just this.
Go ahead and add these two methods to your library:
public function add_include($path, $prepend_base_url = TRUE) { if ($prepend_base_url) { $this->CI->load->helper('url'); // Load this just to be sure $this->file_includes[] = base_url() . $path; } else { $this->file_includes[] = $path; } return $this; // This allows chain-methods } public function print_includes() { // Initialize a string that will hold all includes $final_includes = ''; foreach ($this->includes as $include) { // Check if it's a JS or a CSS file if (preg_match('/js$/', $include)) { // It's a JS file $final_includes .= '<script type="text/javascript" src="' . $include . '"></script>'; } elseif (preg_match('/css$/', $include)) { // It's a CSS file $final_includes .= '<link href="' . $include . '" rel="stylesheet" type="text/css" />'; } return $final_includes; } }
Be sure to also add this new attribute to your class, just above the constructor
:
private $includes = array();
and this to your layout, just after the title
<?php echo $this->layouts->print_includes() ?>
A little explanation:
- The
add_include()
method allows us to add multiple JS or CSS files from ourcontroller
. It even allows method chaining, meaning we can do something like$this->layouts->add_include('js/jquery.js')->add_include('js/jquery.plugin.js')->add_include('css/jquery.plugin.css');
which can be very comfortable when loading multiple things. This method chaining feature is the reason we need PHP 5, because PHP 4 doesn't support it. - The
$prepend_base_url
parameter on theadd_include()
method, will by default prepend the base url of the CodeIgniter installation. By calling this method with the$prepend_base_url
set toFALSE
, we can include remote files (for example, the jQuery lib from Google's CDN). - The
print_includes()
method is self-explanatory. It iterates through all the includes added with theadd_include()
method, checks whether the file is a Javascript or a CSS file (no other files supported), and appends the include to a string that will finally be echo'ed in the layout.
Conclusion
We've created a complete and very practical layout manager library for CodeIgniter from scratch! This will allow you to save time, avoid unnecesary calls to include headers, content, and footer all the time, and modularize your Javascript and CSS files.
This is the final code for the layout and the library:
<!DOCTYPE HTML> <html> <head> <title>Layouts Library<?php echo $title_for_layout ?></title> <?php echo $this->layouts->print_includes(); ?> </head> <body> <?php echo $content_for_layout; ?> </body> </html>
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); /** * Layouts Class. PHP5 only. * */ class Layouts { // Will hold a CodeIgniter instance private $CI; // Will hold a title for the page, NULL by default private $title_for_layout = NULL; // The title separator, ' | ' by default private $title_separator = ' | '; public function __construct() { $this->CI =& get_instance(); } public function set_title($title) { $this->title_for_layout = $title; } public function view($view_name, $params = array(), $layout = 'default') { // Handle the site's title. If NULL, don't add anything. If not, add a // separator and append the title. if ($this->title_for_layout !== NULL) { $separated_title_for_layout = $this->title_separator . $this->title_for_layout; } // Load the view's content, with the params passed $view_content = $this->CI->load->view($view_name, $params, TRUE); // Now load the layout, and pass the view we just rendered $this->CI->load->view('laytous/' . $layout, array( 'content_for_layout' => $view_content, 'title_for_layout' => $separated_title_for_layout )); } public function add_include($path, $prepend_base_url = TRUE) { if ($prepend_base_url) { $this->CI->load->helper('url'); // Load this just to be sure $this->file_includes[] = base_url() . $path; } else { $this->file_includes[] = $path; } return $this; // This allows chain-methods } public function print_includes() { // Initialize a string that will hold all includes $final_includes = ''; foreach ($this->includes as $include) { // Check if it's a JS or a CSS file if (preg_match('/js$/', $include)) { // It's a JS file $final_includes .= '<script type="text/javascript" src="' . $include . '"></script>'; } elseif (preg_match('/css$/', $include)) { // It's a CSS file $final_includes .= '<link href="' . $include . '" rel="stylesheet" type="text/css" />'; } return $final_includes; } } }
Be sure to watch the screencast for the full overview and commentary!
Comments