Step 2 – Create a template for admin area

(created: January 22, 2015; last update: October 01, 2015)

Now that we have an admin area, it would be nice to make it look good. So let’s first create a basic template and then inject the Bootstrap CSS in it.

As you’ve probably read in “Step 11 – Creating and using page templates in CodeIgniter”, it is quite easy to create a template.

So let’s apply that to our blog admin area:

First of all let’s see what we can change in MY_Controller class. The $data and the render() method will be universal throughout our blog, and if Admin_Controller class and Public_Controller class extend MY_Controller, why not define those in there?

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Controller extends CI_Controller
{
  protected $data = array();
  function __construct()
  {
    parent::__construct();
    $this->data['page_title'] = 'CI App';
    $this->data['before_head'] = '';
    $this->data['before_body'] ='';
  }

  protected function render($the_view = NULL, $template = 'master')
  {
    if($template == 'json' || $this->input->is_ajax_request())
    {
      header('Content-Type: application/json');
      echo json_encode($this->data);
    }
    else
    {
      $this->data['the_view_content'] = (is_null($the_view)) ? '' : $this->load->view($the_view,$this->data, TRUE);;
      $this->load->view('templates/'.$template.'_view', $this->data);
    }
  }
}

class Admin_Controller extends MY_Controller
{

    function __construct()
    {
      parent::__construct();
    }
}

class Public_Controller extends MY_Controller
{

  function __construct()
  {
    parent::__construct();
  }
}

As you can see, we’ve added a default page title inside page_title key. Also, one might ask: What are those before_head and before_body? Often you find yourself that you need to insert something inside the html page either before closing the <head> tag or before closing the <body> tag (things like javascripts or css styles). This way we can fill these keys with html code as values, and will be inserted into the pages. Now, for our admin area we want to use a different template so we should overwrite the MY_Controller properties. So let’s change the MY_Controller.php to accomodate the changes needed for Admin_Controller:

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Controller extends CI_Controller
{
  protected $data = array();
  function __construct()
  {
    parent::__construct();
    $this->data['page_title'] = 'CI App';
    $this->data['before_head'] = '';
    $this->data['before_body'] ='';
  }

  protected function render($the_view = NULL, $template = 'master')
  {
    if($template == 'json' || $this->input->is_ajax_request())
    {
      header('Content-Type: application/json');
      echo json_encode($this->data);
    }
    else
    {
      $this->data['the_view_content'] = (is_null($the_view)) ? '' : $this->load->view($the_view,$this->data, TRUE);;
      $this->load->view('templates/'.$template.'_view', $this->data);
    }
  }
}

class Admin_Controller extends MY_Controller
{

  function __construct()
  {
    parent::__construct();
    $this->data['page_title'] = 'CI App - Dashboard';
  }

  protected function render($the_view = NULL, $template = 'admin_master')
  {
    parent::render($the_view, $template);
  }
}

class Public_Controller extends MY_Controller
{

  function __construct()
  {
    parent::__construct();
  }
}

In application/views/templates we will have to create an admin_master_view.php:

<?php defined('BASEPATH') OR exit('No direct script access allowed');
$this->load->view('templates/_parts/admin_master_header_view'); ?>

<div class="container">
  <?php echo $the_view_content; ?>
</div>
<?php $this->load->view('templates/_parts/admin_master_footer_view');?>

And after that we will create the template parts, the admin_master_header_view.php, and the admin_master_footer_view.php inside application/views/templates/_parts directory:

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title><?php echo $page_title;?></title>
</head>
<body>

<nav>My menu item</nav>
<footer>
  <p class="footer">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo (ENVIRONMENT === 'development') ? 'CodeIgniter Version <strong>' . CI_VERSION . '</strong>' : '' ?></p>
</footer>

</body>

</html>

Now, returning to our dashboard_view.php, we should remove the header and footer part and leave those to the master template. So the new dashboard_view.php will look like this now:

<div class="container">
  <h1>Welcome to the Dashboard!</h1>
</div>

But what if for some pages we would have to put some html code just before the </head> or </body> tags? For this we will create two more $data elements inside the MY_Controller class, and pass them to the master template:

Now, going to the admin_master_header_view.php, we echo the $data[‘before_head’]:

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title><?php echo $page_title;?></title>
<?php echo $before_head;?>
</head>
<body>

<nav>My menu item</nav>

And, going to admin_master_footer_view.php, we echo the $data[‘before_footer’]:

<footer>
  <p class="footer">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo (ENVIRONMENT === 'development') ? 'CodeIgniter Version <strong>' . CI_VERSION . '</strong>' : '' ?></p>
</footer>
<?php echo $before_body;?>
</body>

</html>

Now go to the Dashboard controller, in application/controllers/admin directory, and instead of calling $this->load->view(‘admin/dashboard_view’); we call the method we defined in MY_Controller and extended in Admin Controller: $this->render(‘admin/dashboard_view’).

<?php defined('BASEPATH') OR exit('No direct script access allowed');

class Dashboard extends Admin_Controller
{

  function __construct()
  {
    parent::__construct();
  }

  public function index()
  {
    $this->render('admin/dashboard_view');
  }
}

Perfect! It’s time for…

Inserting the the Bootstrap CSS into our template

First of all we should create an assets directory in which we will keep our css, javascript and site’s specific images (like logo and so on). Inside it we will also create an admin directory so that we will know that in there we can find all our admin specific assets:

-application/
-system/
-assets/
- -admin/

After we’ve downloaded Bootstrap CSS, we extract the archive inside the assets/admin directory:

-application/
-system/
-assets/
- -admin/
- - -css/
- - -fonts/
- - -js/

Let’s make sure that no one else will look at our admin assets by creating an empty index.html inside the assets/admin directory. You can also create a .htaccess file inside the assets directory (http://www.thesitewizard.com/apache/prevent-directory-listing-htaccess.shtml)

Options -Indexes

After this we should go to our admin_master_header_view.php and load the Bootstrap’s assets:

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo $page_title;?></title>
<link href="assets/admin/css/bootstrap.min.css" rel="stylesheet">
<?php echo $before_head;?>
</head>
<body>

<nav>My menu</nav>

Now, in admin_master_footer_view.php we load the javascripts needed for Bootstrap:

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<footer>
  <p class="footer">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo (ENVIRONMENT === 'development') ? 'CodeIgniter Version <strong>' . CI_VERSION . '</strong>' : '' ?></p>
</footer>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<?php echo $before_body;?>
</body>
</html>

Now let’s return to our admin_master_header_view.php and create our horizontal menu:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo $page_title;?></title>
<link href="<?php echo site_url('assets/admin/css/bootstrap.min.css');?>" rel="stylesheet">
<?php echo $before_head;?>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
  <div class="container">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="<?php echo site_url('admin');?>">Project name</a>
    </div>
    <div id="navbar" class="collapse navbar-collapse">
      <ul class="nav navbar-nav">
        <li><a href="#">A link</a></li>
        <li><a href="#">Another link</a></li>
      </ul>
    </div><!--/.nav-collapse -->
  </div>
</nav>

Now, what about the “Project name”. Why not use our configuration file that we’ve made in a previous tutorial so that it will use the “cms_title” to replace the hard-coded title? So, let’s change Project title  into <?php echo $this->config->item(‘cms_title’);?>.

Also, as you can see, inside the menu we’ve used site_url(). We will use a lot of URL helper functions, so let’s just add this to our autoload.php:

...
$autoload['helper'] = array('url');
...

Also, some changes inside the admin_master_view.php are necessary:

<?php defined('BASEPATH') OR exit('No direct script access allowed');

$this->load->view('templates/_parts/admin_master_header_view'); ?>
<div class="container">
    <div class="main-content" style="padding-top:40px;">
        <?php echo $the_view_content; ?>
    </div>
</div>
<?php $this->load->view('templates/_parts/admin_master_footer_view');?>

…and some in admin_master_footer_view…

<?php defined('BASEPATH') OR exit('No direct script access allowed');?>
<footer>
    <div class="container">
        <p class="footer">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo  (ENVIRONMENT === 'development') ?  'CodeIgniter Version <strong>' . CI_VERSION . '</strong>' : '' ?></p>
    </div>
</footer>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script src="<?php echo site_url('assets/admin/js/bootstrap.min.js');?>"></script>
<?php echo $before_body;?>
</body>
</html>

Cool! Next we will secure our admin area.

Here is our application if you’ve followed all the previous steps and this one.

20 comments

  1. But I got a problem

    Fatal error: Class ‘Admin_Controller’ not found in /home/gyuksd/public_html/application_test/controllers/admin/Dashboard.php on line 4
    A PHP Error was encountered

    Severity: Warning

    Message: Cannot modify header information – headers already sent by (output started at /home/gyuksd/public_html/application_test/controllers/admin/Dashboard.php:4)

    Filename: core/Common.php

    Line Number: 564

    Backtrace:

    A PHP Error was encountered

    Severity: Error

    Message: Class ‘Admin_Controller’ not found

    Filename: admin/Dashboard.php

    Line Number: 4

    Backtrace:

    1. Did you follow the previous tutorial? If you simply copy/pasted what I’ve written in the tutorials you can login by using “administrator” as username and “password” as password.

  2. something missing in this tutorial (MY_Controller.php) :
    $this->data[‘before_head’] = ”;
    $this->data[‘before_body’] = ”;
    but in your ciapp.zip it’s completed. thanks.

  3. sir,
    I need your help, i found a problem, i was trying to figure out why and i am pretty sure that i have done all your instruction. The error message is shown below:

    A PHP Error was encountered

    Severity: Error

    Message: Call to undefined function site_url()

    Filename: _parts/admin_master_header_view.php

    Line Number: 8

    Backtrace:

      1. after i fill the helper with url,

        $autoload[‘helper’] = array(‘url’);

        the error continues

        Severity: Notice

        Message: Undefined variable: before_head

        Filename: _parts/admin_master_header_view.php

        Line Number: 9

        1. Oh… sorry about that. I’ve changed the tutorial a bit now… You define them inside the MY_Controller’s constructor;

          data[‘page_title’] = ‘CI App’;
          $this->data[‘before_head’] = ”;
          $this->data[‘before_body’] =”;
          }

          Thank you for reporting me this.

          1. Thank you. Anytime you have a problem from my tutorials just tell me. I can tame them… 😉

  4. It’s always really useful to read and read again such great tutorial.
    Just a question about the following code:

    if($template == ‘json’ || $this->input->is_ajax_request())
    {
    header(‘Content-Type: application/json’);
    echo json_encode($this->data);
    }

    I don’t understand how to use it…even if, of course, I understand it’s AJAX related. Right now, for, AJAX code, I add a json_encode row in my controllers and some Javascript code in the view to get and parse the returned code. Didn’t use so far the code above but now I’m just asking if I could use it improving my scripts.
    Thanks a lot for any feedback

    1. I was assuming that sometimes you would have some methods that would return json data. So, instead of doing json_encode($array_of_data), you could store the data inside $this->data and then call the same $this->render(NULL,’json’);

Leave a Reply

Your email address will not be published. Required fields are marked *

No spam? * Time limit is exhausted. Please reload CAPTCHA.