Extender el CI_Controller por defecto de CodeIgniter

Una de las ventajas de usar un framework tan sencillo y modular como CodeIgniter es que podemos ampliarlo o mejorarlo a nuestro gusto. Después de crear varios controladores, modelos y vistas vemos que hay cosas que se repiten demasiado: carga de modelos, carga de vistas, paso de variables a las vistas y lo más importante, si tenemos una cabecera y pie de página común a toda nuestra aplicación ¿tenemos que cargarlos en cada acción de un controlador? Con el CodeIgniter de base por desgracia sí, pero nosotros mismos tarde o temprano pensaríamos en crear un controlador base que por defecto cargara la cabecera, la vista correspondiente, el pie de página y lo enviara todo junto al usuario. No hace falta hacerlo, ya que, en Github existe una librería llamada **codeigniter-base-controller** que usando una serie de convenciones podemos crear controladores mucho más cortos y sencillos de mantener.

La librería codeigniter-base-controller del desarrollador Jamie Rumbelow, alojada en su cuenta de GitHub, es una ampliación de la clase **CI_Controller** para usar en nuestras aplicaciones CodeIgniter. Los controladores extenderán de la clase **MY_Controller**, cuyo objetivo es realizar una carga automática predictiva de vistas, uso de layouts y "partials" para facilitar el diseño. Este controlador se basa principalmente en el mismo principio que CodeIgniter, uso de convención en vez configuración para favorecer la simplicidad y consistencia en contra del uso de ficheros complejos de configuración.

Ejemplo de uso:

/*
* Ejemplo del controlador libros que extiende
   de la clase base MY_Controller
*/
class Libros extends MY_Controller
{
    protected $models = array( 'libros', 'comentarios' );

    public function index()
    {
        $this->data['libros'] = $this->libros->getLibrosPortada();
    }

    public function ver($id)
    {
        if ($this->input->is_ajax_request())
        {
            $this->layout = FALSE;
        }
        $this->data['libro'] = $this->libros->get($id);
        $this->data['comentarios'] = $this->comentarios->get($id);
    }
}

En este ejemplo vemos la definición de una clase controladora libros que extiende de la clase MY_Controller. Dentro de la misma se cargan los modelos 'Libros_model' (se llamará libros) y 'Comentarios_model' (se llamará comentarios) de forma automática gracias a que definimos la propiedad $this->models()

Tenemos dos acciones:

  • index(): es la acción que se invoca por defecto si no especificamos ninguna en la url (http://localhost/proyecto/index.php/libros/).
    Usamos el método getLibrosPortada() de la propiedad libros (Libros_model) de esta forma: $this->libros->getLibrosPortada(); (mucho más clara y sencilla que usar $this->Libros_model->getLibrosPortada()) Lo más importante es que lo que nos devuelve getLibrosPortada() (listado de libros para la portada) serán almacenados $this->data['libros']. MY_Controller se encargará automáticamente de buscar en la carpeta libros (mismo nombre que el controlador) la vista index.php (mismo nombre que la acción) y pasará la variable $libros a la misma (si vemos el código interno de la clase lo que hace es ejecutar este código $this->load->view('libros/index', $this->data)

  • ver($id): acción para visualizar los datos del libro que pasan como parámetro en la url (por ejemplo: http://localhost/proyecto/index.php/libros/ver/2).
    Posteriormente obtenemos los datos del libro $this->libros->get($id) y sus comentarios $this->comentarios->get($id) los cuales almacenamos en los índices 'libro' y 'comentarios' del array $this->data. Como en el caso anterior,MY_Controller se encargará automáticamente de buscar en la carpeta libros (mismo nombre que el controlador) la vista ver.php (mismo nombre que la acción) y pasará la variable $libro y $comentarios a la misma.

Más adelante veremos todo el código de la clase **MY_Controller** para entender como funciona internamente. Veremos que es una clase muy sencilla pero a la vez muy potente.

Instalación

Tan solo hay que copiar el fichero **MY_Controller.php** en la carpeta **application/core** de nuestra aplicación. CodeIgniter se encarga de inicializar dicha clase automáticamente por nosotros. Lo siguiente que tenemos que hacer es modificar los controladores creados para que extiendan de la clase **MY_Controller** y ya tendremos disponible toda la funcionalidad proporcionada por dicha clase base.

Vistas y Plantillas (Layouts)

La vistas se cargarán automáticamente en función del controlador actual y el nombre de la acción a ejecutar, es decir, si ejecutamos la acción **ver** del controlador **libros**, MY_Controller buscará en la carpeta **libros** (mismo nombre que el controlador cargado) la vista **ver.php** (mismo nombre que la acción a ejecutar) ¿Sencillo no?
Cualquier variable establecida en el array **$this->data** (propiedad protegida de MY_Controller) será pasada a través de la vista y/o plantilla. Como hemos comentado anteriormente, de forma predeterminada, la clase buscará la vista en **application/views/controller/action.php**.

Con el fin de evitar que una vista sea renderizada automáticamente podemos establecer a FALSE la variable $this->view.

$this->view = FALSE;

O bien cargar una vista diferente de la que se carga automáticamente de esta forma:

$this->view = 'some_path/some_view.php';

La vistas serán cargadas dentro de un layout para evitar cargar en cada acción de un controlador la cabecera y pie de página de nuestra aplicación. La clase buscará un fichero de layout en esta ruta **application/views/layouts/application.php**, si no se puede encontrar ese fichero, volverá de nuevo al archivo **application/views/layouts/application.php**, el cual es el layout por defecto de toda la aplicación.

Con el fin de especificar dónde se renderizará la vista cargada dentro del layout, todo el contenido de dicha vista será almacenada en la variable **$yield**, por lo que tan sólo tendremos que hacer un echo de la misma en el layout:

<h1>Cabecera</h1>

<div id="page">
    <?php echo $yield ?>
</div>

<p>Pie</p>

Si queremos desactivar el layout por completo y sólo mostrar la vista, por ejemplo para las peticiones AJAX, podemos establecer a FALSE la propiedad $this->layout.

$this->layout = FALSE;

Igual que con **$this->view**, podemos cambiar el layout por defecto para ajustarlo a una plantilla específica dando valor a **$this->layout**. Veamos un ejemplo:

   $this->layout = 'layouts/mobile.php';

Cualquier variable que establezcamos en **$this->data** se pasará a la vista y al layout, así como en cualquier partial.

Carga de modelos

Podemos especificar una lista de modelos que serán cargados automáticamente con la propiedad **$this->models**. Por ejemplo:

protected $models = array( 'usuarios', 'libros', 'pedidos' );

El nombre de un modelo está basado en la variable **$this->model_string**. Esto nos permite nombrar a los modelos como más nos guste. De forma predeterminada, un modelo sigue esta regla:

protected $model_string = '%_model';

El símbolo de porcentaje (%) serán reemplazado por el nombre del modelo del array $this->models. Esto hará que el objeto CI (el cual es un Singleton) cargue el modelo según el nombre declarado.

Veamos un ejemplo:

   protected $models = array( 'usuarios' );

   public function index()
   {
       //otra forma de cargar modelos
       //$this->load->model('usuarios_model', 'usuarios');
       $this->usuarios->read(1);
   }

Para cargar modelos dentro de un controlador se usa la librería Loader de CI junto con el método model(). Dicho método requiere el nombre de la clase modelo (Nombre_model) y un segundo parámetro optativo para cambiar el nombre y hacerlo más corto o más descriptivo.
Por ejemplo:
$this->load->model('Libros_model', 'libros');

$this->libros->getLibrosPortada();
Más información en http://ellislab.com/codeigniter/user-guide/general/models.html#loading

Carga automática de Helpers

Podemos cargar de forma automática una serie de helpers mediante el array **$this->helpers**. Por ejemplo:

   protected $helpers = array( 'url', 'html', 'cookie', 'file' );

Páginas de error 404 personalizables

Antes de que CodeIgniter lance un error 404 estándar, MY_Controller buscará un método _404 en el controlador. Esto nos permite personalizar una página 404 para cada controlador (por si fuera necesario).

Código fuente de la clase MY_Controller

<?php

class MY_Controller extends CI_Controller
{
   //VARIABLES
   /**
    * The current request's view. Automatically guessed
    * from the name of the controller and action
    */
   protected $view = '';

   /**
    * An array of variables to be passed through to the
    * view, layout and any asides
    */
   protected $data = array();

   /**
    * The name of the layout to wrap around the view.
    */
   protected $layout;

   /**
    * An arbitrary list of asides/partials to be loaded into
    * the layout. The key is the declared name, the value the file
    */
   protected $asides = array();

   /**
    * A list of models to be autoloaded
    */
   protected $models = array();

   /**
    * A formatting string for the model autoloading feature.
    * The percent symbol (%) will be replaced with the model name.
    */
   protected $model_string = '%_model';

   /**
    * A list of helpers to be autoloaded
    */
   protected $helpers = array();

   // GENERIC METHODS
   /**
    * Initialise the controller, tie into the CodeIgniter superobject
    * and try to autoload the models and helpers
    */
   public function __construct()
   {
     parent::__construct();

     $this->_load_models();
     $this->_load_helpers();
   }
   //VIEW RENDERING
   /**
    * Override CodeIgniter's despatch mechanism and route the request
    * through to the appropriate action. Support custom 404 methods and
    * autoload the view into the layout.
    */
   public function _remap($method)
   {
      if (method_exists($this, $method))
      {
         call_user_func_array(
            array($this, $method),
            array_slice($this->uri->rsegments, 2));
      }
     else
     {
         if (method_exists($this, '_404'))
         {
            call_user_func_array(
              array($this, '_404'),
              array($method));
         }
         else
         {
            show_404(strtolower(get_class($this)).'/'.$method);
         }
     }
     $this->_load_view();
   }

   /**
    * Automatically load the view, allowing the developer to override if
    * he or she wishes, otherwise being conventional.
    */
   protected function _load_view()
   {
      // If $this->view == FALSE,
      //   we don't want to load anything
      if ($this->view !== FALSE)
      {
         // If $this->view isn't empty, load it.
         // If it isn't, try and guess based on the controller
         //  and action name
         $view = (!empty($this->view)) ?
                  $this->view
                  :
                  $this->router->directory . $this->router->class .
                         '/' . $this->router->method;

         // Load the view into $yield
         $data['yield'] = $this->load->view($view, $this->data, TRUE);

         // Do we have any asides? Load them.
         if (!empty($this->asides))
         {
            foreach ($this->asides as $name => $file)
            {
               $data['yield_'.$name] =
                  $this->load->view($file, $this->data, TRUE);
            }
         }

         // Load in our existing data with the asides and view
         $data = array_merge($this->data, $data);
         $layout = FALSE;

         // If we didn't specify the layout, try to guess it
         if (!isset($this->layout))
         {
             $file = APPPATH . 'views/layouts/' . $this->router->class . '.php';
             if (file_exists($file)) {
               $layout = 'layouts/' . $this->router->class;
             }
             else {
               $layout = 'layouts/application';
             }
         }

         // If we did, use it
         else if ($this->layout !== FALSE)
         {
            $layout = $this->layout;
         }

         // If $layout is FALSE, we're not interested in loading a layout,
         //    so output the view directly
         if ($layout == FALSE)
         {
            $this->output->set_output($data['yield']);
         }

         // Otherwise? Load away :)
         else
         {
            $this->load->view($layout, $data);
         }
      }
   }

   // MODEL LOADING
   /**
    * Load models based on the $this->models array
    */
   private function _load_models()
   {
     foreach ($this->models as $model)
     {
        $this->load->model($this->_model_name($model), $model);
     }
   }

   /**
    * Returns the loadable model name based on
    * the model formatting string
    */
   protected function _model_name($model)
   {
     return str_replace('%', $model, $this->model_string);
   }

   // HELPER LOADING
   /**
    * Load helpers based on the $this->helpers array
    */
   private function _load_helpers()
   {
     foreach ($this->helpers as $helper)
     {
         $this->load->helper($helper);
     }
   }
}

results matching ""

    No results matching ""