domingo, 8 de julio de 2012

An Introduction to Views & Templating in CodeIgniter

Views are a key ingredient in any MVC application, and CodeIgniter applications aren’t any different. Today, we’re going to learn what a view is, and discover how they can be used to create a templating solution for your CodeIgniter projects.

The first part of this tutorial will educate complete beginners to CodeIgniter on what a view is, and how to use them in a typical application. The second half will discuss the motivations for finding a templating solution, and guide the reader through the necessary steps for creating a simple, yet effective templating library. Interested? Let’s get started!


What is a View?

Views are special files used in CodeIgniter to store the markup outputted by the application, usually consisting of HTML and simple PHP tags.

“A view is simply a web page, or a page fragment, like a header, footer, sidebar, etc. In fact, views can flexibly be embedded within other views (within other views, etc., etc.) if you need this type of hierarchy.”

Views are loaded from within controller methods, with the content inside the view subsequently displayed in the browser.


How to Load a view

To load (and display) a view in CodeIgniter, we use the built in Loader library.

  $this->load->view('hello_world', $data, true/false);  

This single line of code will tell CodeIgniter to look for hello_world.php in the application/views folder, and display the contents of the file in the browser.

Note that CodeIgniter allows you to exclude the .php suffix, saving a few keystrokes when typing the view’s filename you wish to load.

The second parameter, $data, is optional and takes an associative array or object. This array/object is used to pass data to the view file, so it can be used or referenced within the view.

The final optional parameter determines whether the view’s contents is displayed in the browser window, or returned as a string. This parameter defaults to false, displaying the content in the browser. We shall see later in the tutorial how this parameter can be used when creating a templating solution.


Creating & Displaying a View

To setup our first view, create a new file called hello_world.php in application/views and write the following simple HTML within:

  <!DOCTYPE html>  <html>          <head>                  <title>Hello World!</title>          </head>          <body>                  <p>                          Hello world!                  </p>          </body>  </html>  

Now to display this view in the browser it must be loaded within a Controller method, using the aforementioned method.

So let’s create a new Controller file called hello_world.php in application/controllers and place the following code within. From within this controller, we shall load the newly created view.

  <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');    class Hello_world extends CI_Controller {            public function index()          {                  $this->load->view('hello_world');          }  }  

Pointing your browser to http://your-ci-install.com/index.php/ will now result in the HTML in application/views/hello_world.php being outputted in the browser. You have successfully loaded a view!

Loading Multiple Views

Splitting a view into several files makes your website easier to maintain and reduces the likely hood of duplicate code.

Displaying a single View is all well and good, but you might want to split the output into several, distinct files, such as header, content & footer views.

Loading several views is achieved by merely calling the $this->load->view() method multiple times. CodeIgniter then concatenates the content of the views together before displaying in the browser.

Create a new file called header.php in application/views and cut & paste the first few lines from our original hello_world.php file in.

  <!DOCTYPE html>  <html>          <head>                  <title>Hello World!</title>          </head>          <body>  

Similarly, create another file called footer.php in application/views and move the last two lines of hello_world.php in.

  </body>  </html>  

This leaves the hello_world.php view file just containing our page content.

  <p>          Hello world!  </p>  

Now to display the page again, we have to load all three views (header.php, hello_world.php, footer.php), in order, within our controller.

Re-open application/controllers/hello_world.php and add the new $this->load->view() calls above and below the existing one.

  <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');    class Hello_world extends CI_Controller {            public function index()          {                  $this->load->view('header');                  $this->load->view('hello_world');                  $this->load->view('footer');          }  }  

Because the header and footer views are now separate from the hello_world view, it means that they can be used in conjunction with any other views in the website. This means the code within the header & footer files don’t need to be copied over into any other views in the project that require this code.

Obviously this is a huge benefit as any changes to the HTML or content in the views, e.g adding a new stylesheet to the header, can be made to only one file, and not every file!


Using Data From the Controller in the View

Now, we’ll look at passing data from the controllers, so they can be used or outputted inside the view.

To achieve this, we shall pass an associative array, $data as the second parameter in the $this->load->view() call.

The values of this array will be accessible within the loaded view as variables, named by their respective keys.

  $data = array(            'title'         =>   'Hello World!',          'content'       =>   'This is the content',          'posts'         =>   array('Post 1', 'Post 2', 'Post 3')    );    $this->load->view('hello_world', $data);  

The above code will give the variable $title the value ‘Hello World!’ inside the hello_world view.

How to Use Variables in Views

Once we have passed our data to the view files, the variables can be used in the usual way.

Typically, the view file will use the passed data to:

  • Display a variable’s value
  • Loop through arrays or object properties
  • Use conditional statements to show, or hide markup

I shall run through quick examples of how to do each.

To display a variable’s content use the simple and familiar, echo statement:

          <h1><?php echo $title; ?></h1>  

Looping through an array, or object, is a common task in view files, and can be achieved with a foreach loop:

  <ul>  <?php foreach($posts as $post) { ?>            <li><?php echo $post; ?></li>    <?php } ?>  </ul>  

Simple conditional statements can be used in view files to slightly alter the output, depending on the data passed.

In general, you want to keep the use of conditional statements in views to a minimum, as overuse can lead to complicated view files, containing “business logic”. Splitting the view into different files, and deciding which is to be shown in the controller, is much more preferable.

  <?php if ( $logged_in ) { ?>            <p><?php echo 'Welcome '.$user_name; ?></p>    <?php } else { ?>            <p>Please login</p>    <?php } ?>  

The above example will either show a “Welcome” message, or a request for the user to login, depending on the value of $logged_in (true/false).


Templating in CodeIgniter

We’ve seen how splitting views into separate, smaller files can help organize and reduce the number of files in your CodeIgniter projects, but now multiple load view calls need to be made each instance a page is displayed.

Let’s assume that you have separate header and footer views, which are used to form a template. Every instance in the project where you wish to load and display a page using this template, three view loads have to be called. Not only can this clutter your controllers, but it results in a lot of repeated code – exactly the thing we wished to rid ourselves of by splitting the files up.

If you want to add extra markup to this template now, for example a sidebar menu. It could go in the header view, but it is more suited to be in its own separate view. Adding this new view to the existing template means going through each instance of the view loads, and adding another in. This can get messy fast.

We need a way to be able to embed view files that display individual page content, inside a template, without repeating code, and one that allows for modifications to be made to the template easily, and efficiently.

The following steps will guide you through creating a simple CodeIgniter library that fulfills these needs, as well as:

  • Enforcing a predictable and maintainable directory structure for your views
  • Allowing for multiple distinct templates to be used
  • Cutting down loading a page view to just one line of code

Once the library is written and in our CodeIgniter tool belt, we shall be able to display a templated page like so:

  $this->template->load('template_name', 'body_view');  

Much nicer!

Our templating solution will use view files which contain the full markup of a template, with a placeholder for another view file (with the page content) to be embedded within.

The placeholder will actually just be a variable named $body. When loading a templated view with our library, the content of the appropriate body view file will be assigned to this $body, embedding the view within the the template.


Step 1: Setting Up the Directory

We want to enforce a sensible, and predictable directory system for our view files to be housed in, so that our views are:

  • Easy to locate
  • Easy to determine which area of the application they belong to
  • Easy to maintain

Our directory system will also allow the library to cleverly determine where to look for view files, cutting down on the amount of code needed to load a templated view.

Create a new folder inside the application/views directory and name it templates. This folder will hold the different template views.


Step 2: Creating the Library

Libraries in CodeIgniter are just PHP classes and are loaded into Controllers much like views are.

  $this->load->library('class_name');  

Custom libraries you use in your CodeIgniter projects are stored in the application/libraries folder. To start writing our templating library, create a new file in this folder called Template.php, and place the following code in:

  <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');            class Template          {                  var $ci;                    function __construct()                  {                          $this->ci =& get_instance();                  }          }  

The above code defines a new class, or library, named Template and the __construct() method within.

This method assigns the CodeIgniter super object to the $ci class variable , allowing all of CodeIgniter’s resources to be used by replacing $this with $this->ci in the usual method calls.

When the library is loaded in the CodeIgniter framework, the __construct() method is automatically called.

Writing the Load Method

Now we shall write the method to actually load a template view. We want to pass up to three parameters to this function:

  • The template name
  • The body view name (optional)
  • The data to be passed to the views (optional)

The result of this method being called, will be the template view being displayed in the browser, with the body view being embedded within, if one is supplied.

Underneath the __construct() method, place the following code:

  function load($tpl_view, $body_view = null, $data = null)  {          if ( ! is_null( $body_view ) )          {                  if ( file_exists( APPPATH.'views/'.$tpl_view.'/'.$body_view ) )                  {                          $body_view_path = $tpl_view.'/'.$body_view;                  }                  else if ( file_exists( APPPATH.'views/'.$tpl_view.'/'.$body_view.'.php' ) )                  {                          $body_view_path = $tpl_view.'/'.$body_view.'.php';                  }                  else if ( file_exists( APPPATH.'views/'.$body_view ) )                  {                          $body_view_path = $body_view;                  }                  else if ( file_exists( APPPATH.'views/'.$body_view.'.php' ) )                  {                          $body_view_path = $body_view.'.php';                  }                  else                  {                          show_error('Unable to load the requested file: ' . $tpl_name.'/'.$view_name.'.php');                  }                    $body = $this->ci->load->view($body_view_path, $data, TRUE);                    if ( is_null($data) )                  {                          $data = array('body' => $body);                  }                  else if ( is_array($data) )                  {                          $data['body'] = $body;                  }                  else if ( is_object($data) )                  {                          $data->body = $body;                  }          }            $this->ci->load->view('templates/'.$tpl_view, $data);  }  

The above code begins by checking if the $body_view parameter was supplied to the method. This variable will hold the name of the view to be used as the body in the template view.

          if ( ! is_null( $body_view ) )  

If the parameter is supplied, a series of file_exists checks are made to try and locate the view file within our directory system.

  if ( file_exists( APPPATH.'views/'.$tpl_view.'/'.$body_view ) )  {          $body_view_path = $tpl_view.'/'.$body_view;  }  else if ( file_exists( APPPATH.'views/'.$tpl_view.'/'.$body_view.'.php' ) )  {          $body_view_path = $tpl_view.'/'.$body_view.'.php';  }  

The code first tries to locate the view file inside of a folder with the same name as the template in the application/views folder.

This is useful if sections of your project are drastically different from others, and use different templates. In these circumstances, it makes sense to group these view files together.

For example, a lot of websites display a different template for distinct sections, such as blogs. In our system, the blog view files can be placed inside the application/views/blog folder, seperating them from the main site views.

If the view file cannot be located in this folder, .php is appended to the end of the filename, and the check is made again. This is simply so .php can be excluded like the native $this->load->view() call.

If the file can still not be located, further checks for it’s location are made.

  else if ( file_exists( APPPATH.'views/'.$body_view ) )  {          $body_view_path = $body_view;  }  else if ( file_exists( APPPATH.'views/'.$body_view.'.php' ) )  {          $body_view_path = $body_view.'.php';  }  else  {          show_error('Unable to load the requested file: ' . $tpl_name.'/'.$view_name.'.php');  }  

This time, the code checks if the view file is located inside the application/views folder, and once again, if it cannot be found, appends .php and checks once more.

If the file is located in one of these places, the path is assigned to $body_view_path, otherwise an error message is thrown using the show_error() function built into CodeIgniter, and the script is terminated.

If the body view file was successfully located, the contents is assigned to the $body variable.

          $body = $this->ci->load->view($body_view_path, $data, TRUE);  

We pass the $data parameter (null if not supplied) to the view load call, and set the third parameter to true to return the view’s output as a string.

We now add this $body variable to the list of data in $data so that it can be embedded in the template view when it is loaded.

  if ( is_null($data) )  {          $data = array('body' => $body);  }  else if ( is_array($data) )  {          $data['body'] = $body;  }  else if ( is_object($data) )  {          $data->body = $body;  }  

If $data was not supplied to the load() call, $data is assigned to an array containing $body under key body. If the parameter was supplied, $body is added to the list by either assigning it to an array key, or object property, both also named body.

The $body variable can now be used in template view files as a placeholder for embedded views.

The last line of our method loads the template view file from the application/views/templates folder, and passes the $data variable in the second parameter.

          $this->ci->load->view('templates/'.$tpl_name, $data);  

And that’s it! The library can now be put to use.


Using the Library

To start using our library, let’s create a template view, named default.php in application/views/templates, and place the following HTML/PHP inside:

  <!DOCTYPE html>  <html>            <head>                  <title><?php echo $title; ?></title>          </head>            <body>                    <h1>Default template</h1>                    <div class="wrapper">                            <?php echo $body; ?>                    </div>            </body>    </html>  

In this template, we reference two variables, $title and $body.

Recall that in our template files, $body serves as a placeholder for an embedded view.

We shall now make another view to be embedded inside this template. Create a new file named content.php in application/views/ and place this simple HTML inside:

  <p>          Hello world!  </p>  

We are now ready to load the templated page view from within a controller.

Inside any controller method, place the following code to display the content view, within the default template.

  $data = array(            'title' => 'Title goes here',    );    $this->load->library('template');  $this->template->load('default', 'content', $data);  

Note: the library has to be loaded in before you can call the load method. To save yourself loading the library every time a template view needs to be displayed,
autoload the class by adding it to the array of libraries in application/config/autoload.php.

If instead of a view file, you want a string to be embedded in the template, simply assign the string to the $data array using the key body, and pass null as the second parameter in the load call.

  $data = array(            'title' => 'Title goes here',          'body'  => 'The string to be embedded here!'    );    $this->template->load('default', null, $data);  

Quick Tip

I’ve found that grouping view files in folders by the controller, and even method, they belong to, really helps keep my views organized & easy to locate.

Organizing your views in this way results in the directory structure closely following the URL schema of controller/method.

For example, say your project has a controller named Members, containing method list.

An appropriate location for the list view file would be in application/views/members, or application/views/members/list, if this method loads multiple views.

This view could then be embedded into a template using our library with the following code:

  $this->template->load('template_name', 'members/list');  

Conclusion

The templating solution discussed in this tutorial is just one of a plethora of different ways to achieve templating in CodeIgniter.

You should hopefully now know what views are, and how to use them effectively – and efficiently – in your CodeIgniter projects.

The templating solution discussed in this tutorial is just one of a plethora of different ways to achieve templating in CodeIgniter. There are a number of different approaches, and I encourage you, reader, to research the other methods and determine which fits best for your projects.

If you have any comments or questions about the tutorial, or any insights on different templating solutions, please leave a comment below! Thanks for reading.



No hay comentarios:

Publicar un comentario