15 diciembre, 2009

Empezando con Kohana, ¿mejor que CodeIgniter?

He traducido este tutorial de NetTuts+ creado por Cristian Gilè sobre el framework PHP Kohana, al igual que hice en su día con el de CodeIgniter.

Kohana es un framework de PHP5 que sigue la arquitectura MVC (Modelo-Vista-Controlador). Hay varias razones por las que deberías elegir Kohana, pero las principales son seguridad, ligereza y simplicidad. En este tutorial, introduciré sus características principales y, con una simple demostración, mostraré cuánto tiempo Kohana puede ahorrarte potencialmente.


Paso 1: ¿Qué es Kohana?



Kohana es un framework de PHP5 que sigue la arquitectura MVC. MVC mantiene separadas la lógica de la presentación. Esto permite  crear un código más limpio y ahorrar tiempo en la búsqueda de errores. Para los que no estén familiarizados con esta arquitectura:
  • Un modelo representa datos sobre los que la aplicación opera. Normalmente es una base de datos.
  • Una vista contiene el código de presentación como HTML, CSS y JavaScript.
  • Un controlador interpreta la entrada del usuario y redirige al modelo y/o vista.
Kohana era originalmente una escisión de CodeIgniter (CI), que es un producto open source de la empresa EllisLab. Hay muchas similitudes entre CI y Kohana, pero todo el código es nuevo o ha sido completamente reescrito. Tal y como puedes leer en la página oficial de Kohana, las características principales son:
  • Altamente seguro
  • Extremadamente ligero
  • Breve curva de aprendizaje
  • Utiliza el patrón MVC
  • 100% compatible con la codificación UTF-8
  • Arquitectura débilmente acoplada
  • Extremadamente fácil de extender

Paso 2: Descargando Kohana



Empecemos. Visita la página oficial de Kohana y haz click sobre la caja verde de la esquina derecha para descargar la última versión. Todas las bibliotecas, asistentes y vistas de Kohana están incluidos en el paquete de descarga por defecto, pero puedes seleccionar módulos extra, herramientas de terceros, y los lenguajes que desees. Para el propósito de este tutorial, el paquete por defecto será suficiente. Haz click en "Download Kohana!" para iniciar la descarga.




Paso 3: Instalando Kohana

Una vez que se ha terminado la descarga:
  1. Descomprimir el zip
  2. Renombrar la carpeta "Kohana_vx.x.x" (x.x.x=número de versión) a "kohana" y súbela a la carpeta raíz de tu servidor web.
  3. Edita el fichero de configuración global application/config/config.php de esta forma:
       $config['site_domain'] = 'localhost/kohana';
  4. Si estás usando un sistema unix (linux, MacOSX), las subcarpetas de la instalación podrían haber perdido sus permisos durante la descompresión del zip. Haz un chmod a 755 de todas ellas ejecutando find . -type d -exec chmod 755 {} \; desde la carpeta raíz de la instalación de Kohana.
  5. Asegúrate de que application/logs y application/cache no sean de sólo lectura. Haz chmod a 666 sobre ellas.
  6. Ahora, apunta el navegador a http://localhost/kohana/ . Automáticamente, el framework llamará a install.php y comprobará los requerimientos con el servidor.
Kohana se ejecutará en prácticamente cualquier entorno que cumpla la configuración mínima:
  • Servidor con soporte de Unicode
  • PHP con una versión igual o posterior a la 5.2.3
  • Un servidor HTTP. Sugiero XAMPP, que es una herramienta sencilla todo-en-uno para instalar MySQL, PHP y Perl.
  • Una base de datos (MsSQL, MySQL, MySQLi, PostgreSQL, PDOSqlite).
También hay extensiones necesarias:
  • PCRE
  • iconv
  • mcrypt
  • SPL
Si la instalación se completa con éxito, serás dirigido a esta página de prueba:


Si alguna de las pruebas falla, deberás corregirla antes de continuar.

Si todas las comprobaciones tienen éxito, dirígete a la carpeta de Kohana y elimina o renombra el archivo install.php. Recarga la página en el navegador y verás una página de bienvenida como esta:




Paso 4: Configurando Kohana

Kohana está preparado para funcionar. No se necesita realizar ninguna configuración. Este framework es genial, ¿no te parece? Vamos a ver un poco de código, sígueme.


Paso 5: El primer proyecto con Kohana

Los tutoriales de programación tradicionales empiezan con el ejemplo "hello world". Pero yo creo que una aplicación simple puede dar una idea clara de cómo funciona el framework. Vamos a construir un gestor de colecciones de CDs -- sólo como divertida demostración. Antes de empezar a escribir código es necesaria una introducción breve a la distribución de carpetas de Kohana.



Nuestra aplicación se ubicará en la carpeta application. En esta carpeta hay varias subcarpetas pero sólo necesitamos lo siguiente para nuestro proyecto:
  • config guarda los archivos de configuración codificados como simples arrays estáticos.
  • controllers guarda nuestra clase de controladores personalizada
  • models guarda nuestra clase de modelos personalizada
  • views guarda nuestro archivos personalizados escritos en HTML (o cualquier lenguaje de markup o scripts necesarios para mostrar los datos y los controles de interfície para el usuario)
El resto de subcarpetas no se necesitan para este tutorial, por lo que te invito a aprender más posteriormente sobre ellos en la página de Kohana.

La carpeta system guarda el núcleo de Kohana y herramientas como bibliotecas (library), asistentes (helper) y archivos de configuración predefinidos. En este proyecto vamos a usar algunas bibliotecas y algunos asistentes - buenas herramientas para acelerar tu trabajo.

La carpeta assets  no pertenece a Kohana por defecto. Yo la he creado para guardar archivos estáticos como CSS, JS o imágenes. Después mostraré como incluirlos en el proyecto.

La carpeta modules es el lugar donde guardar las colecciones reusables de archivos relacionados que unidos aportan una funcionalidad particular a una aplicación. El módulo de autenticación de usuarios, ofrecido por el equipo Kohana, es un ejemplo de módulo.

Ésta es una muy breve introducción al sistema de archivos de Kohana, pero es suficiente para los propósitos de este tutorial. No quiero aburrirte con más teoría.


Paso 6: La base de datos del proyecto

He seleccionado MySQL para mi proyecto, pero recuerda que Kohana soporta igualmente MsSQL, MySQLi, PostgreSQL, o PDOSqlite. Crea una base de datos llamada "cd_collection" o elige el nombre que prefieras, y ejecuta la siguiente SQL mediante phpMyAdmin o cualquier herramienta para administrar la base de datos.

CREATE TABLE `albums` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(50) collate utf8_bin NOT NULL,
  `author` varchar(50) collate utf8_bin NOT NULL,
  `genre_id` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `genre_id` (`genre_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=19 ;

INSERT INTO `albums` (`id`, `name`, `author`, `genre_id`) VALUES
(2, 'Lines, Vines And Trying Times', 'Jonas Brothers', 16),
(3, 'The E.N.D.', 'The Black Eyed Peas', 16),
(4, 'Relapse', 'Eminem', 18),
(5, 'Monuments And Melodies', 'Incubus', 1),
(6, 'Thriller', 'Michael Jackson', 16),
(7, 'Back in Black', 'AC/DC', 4),
(8, 'The Dark Side of the Moon', 'Pink Floyd', 4),
(9, 'Bat out of Hell', 'Meat Loaf', 4),
(10, 'Backstreet Boys', 'Millennium', 16),
(11, 'Rumours', 'Fleetwood Mac', 4),
(12, 'Come on Over', 'Shania Twain', 16),
(13, 'Led Zeppelin IV', 'Led Zeppelin', 4),
(14, 'Jagged Little Pill', 'Alanis Morissette', 4),
(15, 'Sgt. Pepper''s Lonely Hearts Club Band', 'The Beatles', 16),
(16, 'Falling into You', 'Cv©line Dion', 16),
(17, 'Music Box', 'Mariah Carey', 16),
(18, 'Born in the U.S.A.', 'Bruce Springsteen', 4);

CREATE TABLE `genres` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(50) collate utf8_bin NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=22 ;

INSERT INTO `genres` (`id`, `name`) VALUES
(1, 'Alternative Rock'),
(2, 'Blues'),
(3, 'Classical'),
(4, 'Rock'),
(5, 'Country'),
(6, 'Dance'),
(7, 'Folk'),
(8, 'Metal'),
(9, 'Hawaiian'),
(10, 'Imports'),
(11, 'Indie Music'),
(12, 'Jazz'),
(13, 'Latin'),
(14, 'New Age'),
(15, 'Opera'),
(16, 'Pop'),
(17, 'Soul'),
(18, 'Rap'),
(20, 'Soundtracks'),
(21, 'World Music');

ALTER TABLE `albums`
  ADD CONSTRAINT `genre_inter_relational_constraint` FOREIGN KEY (`genre_id`) REFERENCES `genres` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

Como puedes ver, el SQL crea dos tablas, álbumes y géneros, y los puebla con algunos datos. La última sentencia SQL agrega una restricción para la clave ajena "genre_id".

La estructura de la base de datos es muy simple y no necesita mayor explicación:



Ahora, debes decirle a Kohana dónde está tu base de datos y cómo acceder a ella. Edita el archivo de configuración global system/config/database.php de esta forma:

$config['default'] = array
(
    'benchmark'     => TRUE,
    'persistent'    => FALSE,
    'connection'    => array
    (
        'type'     => 'mysql',    
        'user'     => 'root',    
        'pass'     => 'root',    
        'host'     => 'localhost',    
        'port'     => FALSE,        
        'socket'   => FALSE,        
        'database' => 'cd_collection'
    ),
    'character_set' => 'utf8',
    'table_prefix'  => '',
    'object'        => TRUE,
    'cache'         => FALSE,
    'escape'        => TRUE
);

Este código le dice a Kohana que se conecte a una base de datos MySQL llamada "cd_collection" en localhost con el nombre de usuario "root" y la clave "root". Debes cambiarlas de acuerdo con tu configuración de servidor de base de datos.


Paso 7: Creando el controlador

Creémos nuestro primer controlador. Recuerda estas convenciones:
  • el nombre de archivo del controlador debe estar en minúsculas, por ejemplo album.php
  • la clase controlador debe coincidir con su nombre de archivo pero iniciada con mayúscula y debe añadir _Controller, por ejemplo Album_Controller
  • Debe tener la clase Controller como padre
Igualmente, recuerda cómo Kohana estructura sus URLs y cómo puedes llamar a un método de controlador; por ejemplo http://hostname/carpeta_de_kohana/index.php/controller/function

Echa un vistazo a este simple controlador.

<?php defined('SYSPATH') OR die('No direct access allowed.');

class Album_Controller extends Controller
{
    public function __construct()    
    {
        parent::__construct();
    }
    
    public function index()
    {
          echo "My first controller";    
    }
}

PHP5 OOP (programación orientada a objetos) es un prerrequisito. Para aprender más sobre ello, puedes leer aquí.

La función constructor, llamada __construct, inicializa la clase y llama al constructor padre. La función index es la función por defecto, por lo que será usada si llamamos al controlador sin especificar una función (por ejemplo, http://localhost/index.php/kohana/album. Tras el nombre del controlador no hay ninguna función, la función de índice será llamada por defecto).

Dadas estas reglas básicas, enfoquémonos en la aplicación. El controlador de album implementa todas las acciones para la gestión de colección de álbumes. Este controlador nos permite crear un nuevo álbum, mostrar los álbumes guardados en la base de datos, actualizar un álbum o borrar un álbum.

Crea un archivo llamado album.php en application/controllers/ y pega los siguiente:

<?php defined('SYSPATH') OR die('No direct access allowed.');
    
class Album_Controller extends Controller
{
     private $album_model;
     private $genre_model;
    
     private $list_view;
     private $create_view;
     private $update_view;

    public function __construct()    
    {
        parent::__construct();
        $this->album_model   = new Album_Model;
        $this->genre_model      = new Genre_Model;
          $this->list_view       = new View('list');
          $this->update_view      = new View('update');
          $this->create_view      = new View('create');
    }

    public function index()
    {
         $this->show_albums_list();
    }
        
    private function show_albums_list()
    {
        $albums_list = $this->album_model->get_list();
        $this->list_view->set('albums_list',$albums_list);
        $this->list_view->render(TRUE);
    }
    
    public function show_create_editor()
    {
        $this->create_view->set('genres_list',$this->get_genres_list());
         $this->create_view->render(TRUE);
    }

    public function show_update_editor($id)
    {
        $album_data = $this->album_model->read($id);
        $this->update_view->set('album_id',$album_data[0]->id);
        $this->update_view->set('name',$album_data[0]->name);
        $this->update_view->set('author',$album_data[0]->author);
        $this->update_view->set('genre_id',$album_data[0]->genre_id);
        $this->update_view->set('genres_list',$this->get_genres_list());
        $this->update_view->render(TRUE);
    }

    public function create()
    {
        $album_data=array(
        'name'        => $this->input->post('name'),
        'author'      => $this->input->post('author'),
        'genre_id'  => $this->input->post('genre_id')
        );
        $this->album_model->create($album_data);
        url::redirect('album');
    }

    public function update()
    {
        $album_data = array(
            'name'        => $this->input->post('name'),
            'author'      => $this->input->post('author'),
            'genre_id'  => $this->input->post('genre_id')
        );
        $this->album_model->update($this->input->post('album_id'),$album_data);
         url::redirect('album');
    }
 
     public function delete($id)
    {
        $this->album_model->delete($id);
        url::redirect('album');
    }

    private function get_genres_list()
    {
        $db_genres_list  = $this->genre_model->get_list();
        $genres_list  = array();
        
        if(sizeof($db_genres_list) >= 1)
        {
            foreach($db_genres_list as $item)
            {
                $genres_list[$item->id] = $item->name;
            }
        }
        return $genres_list;
    }
}

Déjame explicar lo que hace este código.

Se declaran cinco variables miembro al inicio:

  private $album_model;
  private $genre_model;

  private $list_view;
  private $create_view;
  private $update_view;

Estos miembros son privados porque quiero limitar su visibilidad sólo a esta clase.

En el método construct los objetos de modelo y vista son creados usando los cinco miembros:

  $this->album_model   = new Album_Model;
  $this->genre_model      = new Genre_Model;
  $this->list_view       = new View('list');
  $this->update_view      = new View('update');
  $this->create_view      = new View('create');

Para crear un objeto modelo usa esta sintaxis:
$obj_name = new Name_Model;

Para crear un objeto vista, usa esta sintaxis:
$obj_name = new View('view_filename_without_extension');

Ahora hay dos objetos para acceder al modelo de álbum y género, y tres objetos para acceder a las vistas necesarias para generar la presentación.

El método index llama al método show_albums_list que lista todos los álbumes guardados en la base de datos.
$albums_list = $this->album_model->get_list();   
$this->list_view->set('albums_list',$albums_list);  
$this->list_view->render(TRUE);

En este método puedes ver cómo el modelo y la vista objeto son usada para acceder a métodos relativos. "get_list" es un método del modelo (lo veremos luego) que devuelve todos los álbumes guardados en la base de datos. El resultado se guarda en el array "$album_list". Para pasar el array de resultados del controlador a la vista, el método "set" del objeto vista es llamado. Este método requiere dos parámetros: una nueva variable vacía (album_list) contiene datos de una variable existente ($album_list). Ahora la nueva variable vacía "album_list" contine el array $album_list (veremos después como mostrar su contenido en la vista). El método "render", con el parámetro TRUE, es necesario para generar datos para el navegador.

El método show_create_editor muestra la interfície de usuario para insertar un nuevo álbum.
   1. $this->create_view->set('genres_list',$this->get_genres_list());
$this->create_view->render(TRUE);

La lista de géneros es pasada a la vista.

El método show_update_editor muestra la interfície de usuario para actualizar un álbum existente.

$album_data = $this->album_model->read($id);  
$this->update_view->set('album_id',$album_data[0]->id);  
$this->update_view->set('name',$album_data[0]->name);  
$this->update_view->set('author',$album_data[0]->author);  
$this->update_view->set('genre_id',$album_data[0]->genre_id);  
$this->update_view->set('genres_list',$this->get_genres_list());
$this->update_view->render(TRUE);

"read" es un método del modelo (lo veremos después) que devuelve datos ($album_data) del álbum con un identificador igual a $id. Entonces, cada elemento del álbum de datos devuelto es pasado a la vista.

El método create recibe datos para un nuevo álbum a partir de la vista y guarda los datos recibidos en la base de datos.
$album_data=array(  
'name'      => $this->input->post('name'),  
'author'    => $this->input->post('author'),  
'genre_id'  => $this->input->post('genre_id')  
);  
$this->album_model->create($album_data);  
url::redirect('album');

$album_data es un array que contiene los datos POST de la vista. Para guardar el álbum, el array se pasa al método del modelo create. La última línea es una llamada a un método asistente. Los asistentes (helpers) son funciones que te asisten en el desarrollo. Las clases asistente son automáticamente cargadas por Kohana. Los asistentes se declaran como métodos estáticos de una clase, por lo que no hay necesidad de instanciar la clase. En este caso, el método "redirect" del asistente "url" es llamado y le dice a Kohana que redirija el navegador al controlador de álbum. Esto evita una nueva inserción (por ejemplo, al pulsar F5).

Los métodos update (actualizar) y delete (borrar) funcionan de la misma forma que create.

El último método get_genres_list obtiene la lista de géneros del modelo ($db_genres_list) y construye un nuevo array ($genres_list) para el control de selección (select) en las vistas.
$db_genres_list  = $this->genre_model->get_list();   
$genres_list  = array();  
 
if(sizeof($db_genres_list) >= 1)  
{  
    foreach($db_genres_list as $item)  
    {  
        $genres_list[$item->id] = $item->name;  
    }  
}  
return $genres_list;


Paso 8: Creando el modelo para el proyecto

Creemos los modelos para nuestra aplicación web. Conviene recordar las convenciones indicadas anteriormente a la hora de crear las clases. A continuación está el código para el modelo del álbum. Crea un archivo llamado album.php en application/models/ y pega en él el siguiente código:

<?php defined('SYSPATH') OR die('No direct access allowed.');

class Album_Model extends Model
{
    private $album_table;
    private $genre_table;

     public function __construct()
        {
              parent::__construct();
         $this->album_table = 'albums';
        $this->genre_table = 'genres';
     }
 
     public function read($id)
     {
        $this->db->where('id', $id);
        $query = $this->db->get($this->album_table);
         return $query->result_array();
    }
     
    public function delete($id)
     {
        $this->db->delete($this->album_table, array('id' => $id));
     }
 
     public function update($id,$data)
     {
        $this->db->update($this->album_table, $data, array('id' => $id));
     }
     
    public function create($data)
     {
           $this->db->insert($this->album_table, $data);
     }
    
    public function get_list()
    {
         $this->db->select('albums.id as id,albums.name as name,albums.author as author, genres.name as genre');  
         $this->db->from($this->album_table);  
         $this->db->join($this->genre_table,'genres.id','albums.genre_id');
         $query = $this->db->get();
         return $query->result_array();
    }
}
Todos los métodos de los modelos usan la sintaxis del constructor de consultas (Query builder). Esta herramienta de Kohana acelera el tiempo de desarrollo con bases de datos y simplifica la creación de consultas.

Dos variables miembro son declaradas al inicio de la clase:
private $album_table;  
private $genre_table;

Estos miembros son privados porque quiero limitar la visibilidad sólo a esta clase. Son los contenedores de los nombres de las tablas de la base de datos.

La primera línea en el método del constructor carga la biblioteca de base de datos de Kohana en $this->db. En las dos siguientes líneas, se inicializan las dos variables miembro.
parent::__construct();  
$this->album_table = 'albums';  
$this->genre_table = 'genres';

La consulta en el método read recupera los registros de álbum que tengan un cierto identificador ("$id").
$this->db->where('id', $id);  
    $query = $this->db->get($this->album_table);   
        return $query->result_array();

La consulta en el método delete elimina la fila de la tabla de álbumes que tenga un cierto identificador ("$id").
$this->db->delete($this->album_table, array('id' => $id));

La consulta en el método update actualiza la fila de la tabla de álbumes que tenga un cierto identificador ("$id") con los nuevos valores del array "$data".
$this->db->update($this->album_table, $data, array('id' => $id));

El array "$data" debe contener nombres de registros como claves del array, y sus valores como valores del array. El array debe tener esta forma:
$data = array(  
    'name'          =>   'album_name',  
    'author'        =>   'author_name',  
    'genre_id'      =>   'genre_id'    
    );

La consulta en el método get_list recupera todas las filas de álbumes.
$this->db->select('albums.id as id,albums.name as name,albums.author as author, genres.name as genre');    
$this->db->from($this->album_table);    
$this->db->join($this->genre_table,'genres.id','albums.genre_id');  
$query = $this->db->get();  
return $query->result_array();

Ahora el modelo para los géneros. Crea un archivo llamado genre.php en application/models/ y pega el siguiente código en él:
<?php defined('SYSPATH') OR die('No direct access allowed.');

class Genre_Model extends Model
{
  private $genre_table;

  function __construct()
  {
    parent::__construct();
    $this->genre_table = 'genres';
  }

  function get_list()
  {
    $query = $this->db->get($this->genre_table);
    return  $query->result_array();        
  }
}

Este modelo es muy simple, por lo que no malgasteré tiempo comentándolo. Los modelos y el controlador están preparados para funcionar. Ahora trabajemos sobre las vistas.


Paso 9: Creando la vista del proyecto

Las vistas son archivos que contienen la capa de presentación de la aplicación. Su propósito es mantener esta información separada de la lógica de la aplicación para facilitar la reusabilidad y mantener el código limpio. Para este proyecto se necesitan tres vistas: una para listar la colección de álbumes, otra para crear un nuevo álbum, y otra para editar un álbum existente.

Crea un archivo llamado list.php en application/views/ y pega el siguiente código en él:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<?php
    echo html::stylesheet(array
        (
         'assets/css/style'
        ),
        array
        (
         'screen'
        ), FALSE);
?>
        <title>CD COLLECTION</title>
        </head>
        <body>
        <?php
                echo html::image('assets/images/add.png');
                echo html::anchor('album/show_create_editor', 'Add new album');
        ?>
        <table class="list" cellspacing="0">
        <tr>
            <td colspan="5" class="list_title">CD Collection</td>
        </tr>    
        <tr>
            <td class="headers">Album name</td>
            <td class="headers">Author</td>
            <td colspan='3' class="headers">Genre</td>
        
        </tr>    
        <?php
            foreach($albums_list as $item)
            {
                echo "<tr>";
                echo "<td class='item'>".$item->name."</td>";
                echo "<td class='item'>".$item->author."</td>";
                echo "<td class='item'>".$item->genre."</td>";
                echo "<td class='item'>".html::anchor('album/delete/'.$item->id,html::image('assets/images/delete.png'))."</td>";        
                echo "<td class='item'>".html::anchor('album/show_update_editor/'.$item->id,html::image('assets/images/edit.png'))."</td>";        
                echo "</tr>";
            }
        ?>
        </table>
        </body>
        </html>

Esta vista muestra una página html que contiene una lista de todos los álbumes. Esta lista ha sido creada usando un bucle foreach que imprime la información en una tabla html. Para cada fila de álbum, hay dos imágenes: una "cruz roja" y una "libro de bolsillo". Enlazan respectivamente al método delete y al método update del controlador. Ambos pasan el identificador del álbum al controlador usando una petición GET. Sobre la lista hay un botón para crear un nuevo álbum. En este código también se usa un asistente html ofrecido por Kohana que acelera las operaciones para escribir páginas html.

Creémos ahora un archivo llamado create.php en application/views/.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<?php
    echo html::stylesheet(array
    (
        'assets/css/style'
    ),
    array
    (
        'screen'
    ), FALSE);
?>
<title>CD COLLECTION</title>
</head>
<body>
<?php echo form::open('album/create'); ?>
<table class='editor'>
<tr>
    <td colspan='2' class='editor_title'>Create new album</td>
</tr>
<?php
    echo "<tr>";
    echo "<td>".form::label('name', 'Name: ')."</td>";
    echo "<td>".form::input('name', '')."</td>";
    echo "</tr>";
    
    echo "<tr>";
    echo "<td>".form::label('author', 'Author: ')."</td>";    
    echo "<td>".form::input('author', '')."</td>";    
    echo "<tr/>";
    
    echo "<tr>";
    echo "<td>".form::label('genre', 'Genre: ')."</td>";    
    echo "<td>".form::dropdown('genre_id',$genres_list)."</td>";
    echo "<tr/>";
    
    echo "<tr>";
    echo "<td colspan='2' align='left'>".form::submit('submit', 'Create album')."</td>";
    echo "</tr>";
?>
</table>
<?php echo form::close(); ?>
</body>
</html>

La última es la vista de actualización. Crea un archivo update.php en application/views/.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<?php
    echo html::stylesheet(array
    (
        'assets/css/style'
    ),
    array
    (
        'screen'
    ), FALSE);
?>
<title>CD COLLECTION</title>
</head>
<body>
<?php echo form::open('album/update'); ?>
<table class='editor'>
<tr>
    <td colspan='2' class='editor_title'>Update album</td>
</tr>
<?php
    echo "<tr>";
    echo "<td>".form::label('name', 'Name: ')."</td>";
    echo "<td>".form::input('name', $name)."</td>";
    echo "</tr>";
    
    echo "<tr>";
    echo "<td>".form::label('author', 'Author: ')."</td>";    
    echo "<td>".form::input('author', $author)."</td>";    
    echo "<tr/>";
    
    echo "<tr>";
    echo "<td>".form::label('genre', 'Genre: ')."</td>";    
    echo "<td>".form::dropdown('genre_id',$genres_list, $genre_id)."</td>";
    echo "<tr/>";
    
    echo "<tr>";
    echo "<td colspan='2' align='left'>".form::submit('submit', 'Update album')."</td>";
    echo "</tr>";
    
?>
</table>
<?php
    echo form::hidden('album_id',$album_id);
    echo form::close();
?>
</body>
</html>

El primero es un editor simple que permite al usuario insertar información sobre un nuevo álbum. Los campos como autor y nombre serán insertados usando una entrada html y el género usando un combo. Una vez que el usuario hace click en el botón create, toda la información se pasa como una petición POST al método create/update del controlador de álbum. Cuando el controlador recibe estas variables posteadas llama al modelo que inserta un nuevo álbum en la base de datos. Los formularios de ambas vistas hacen uso del asistente de formularios.

Para dar un poco de estilo a nuestra aplicación, crea la carpeta assets en la carpeta raíz de Kohana al mismo nivel de la carpeta de aplicaciones. Ábrelo y crea dos nuevas carpetas: css e images.

En la carpeta css crea un nuevo archivo llamado style.css y pega lo siguiente en él:
a {
    font-family: Verdana, Geneva, Arial, Helvetica, sans-serif ;
    font-weight: normal;
    font-size: 12px;
    color: #00F;
    vertical-align:text-top;
}

img {
    border: 0;
}

label {
    font-family: Verdana, Geneva, Arial, Helvetica, sans-serif ;
    font-weight: normal;
    font-size: 12px;
}

input {
    border: 1px solid #000;
}

select {
    width:185px;
}

table.editor
{
    text-align: center;
    font-family: Verdana, Geneva, Arial, Helvetica, sans-serif ;
    font-weight: normal;
    font-size: 11px;
    color: #fff;
    width: 280px;
    background-color: #666;
    border: 0px;
    border-collapse: collapse;
    border-spacing: 0px;
}

table.editor td.editor_title
{
    background-color: #666;
    color: #fff;
    padding: 4px;
    text-align: left;
    font-weight: bold;
    font-size: 16px;
}

table.editor td
{
    padding: 4px;
}

table.list
{
    text-align: center;
    font-family: Verdana, Geneva, Arial, Helvetica, sans-serif ;
    font-weight: normal;
    font-size: 11px;
    color: #fff;
    width: 280px;
    background-color: #666;
    border: 0px;
    border-collapse: collapse;
    border-spacing: 0px;
}

table.list td.item
{
    background-color: #CCC;
    color: #000;
    padding: 4px;
    text-align: left;
    border: 1px #fff solid;
}

table.list td.list_title,table.list td.headers
{
    background-color: #666;
    color: #fff;
    padding: 4px;
    text-align: left;
    border-bottom: 2px #fff solid;
    font-weight: bold;
}

table.list td.list_title
{
    font-size: 16px;
}

table.list td.headers
{
    font-size: 12px;
}

Ahora copia las siguientes imágenes en la carpeta images:

Eso es todo. Apunta el navegador a http://localhost/kohana/index.php/album y deberías ver algo parecido a esto:



Si intentas crear un nuevo álbum o editar uno existente, deberías ver algo similar a esto:



Paso 10: Pensamientos finales

Desde luego, algunas mejoras son necesarias para esta aplicación, pero con muy poco código has creado una pequeña aplicación web. Ahora, sabes cómo usar el patrón MVC con Kohana, y cómo usar las bibliotecas y asistentes de la base de datos. Para aprender más, lee la documentación oficial.

Gracias a Kohana, el mantenimiento de código se convierte en una tarea fácil, y añadir nuevas características es pan comido. Espero que hayas disfrutado del tutorial.

16 comentarios:

k@beza dijo...

Muy bueno el articulo y muy buen trabajo de traduccion
Saludos

Anónimo dijo...

Si, es mejor que CI.

Anónimo dijo...

muy útil, gracias

Anónimo dijo...

Estupendo, muchas gracias

Tomas Barrios H. dijo...

Cuales son los beneficios en comparacion a CodeIgniter?

Jose dijo...

Un guia para aprendizes excelente aunque yo como el primero recomendaria que la actualizases para la version actual.

Un saludo, :D

Anónimo dijo...

no funciona el ejemplo con la última versión de kohana....

KIDO dijo...

Me marca error en:

34 $this->db->insert($this->album_table, $data);
35 }
36
37 public function get_list()
38 {
39 $this->db->select('albums.id as id,albums.name as name,albums.author as author, genres.name as genre');
40 $this->db->from($this->album_table);
41 $this->db->join($this->genre_table,'genres.id','albums.genre_id');
42 $query = $this->db->get();
43 return $query->result_array();
44 }


LA LINEA 39

ES BUEN PROYECTO PARA INICIAR EN KOHANA...GRACIAS...

PODRIAS AYUDARME EN EL ERROR???

Anónimo dijo...

Hola.

Estaría muy bien que pusieras la versión de Kohana que has usado para el ejemplo. Porque según dicen en los comentarios, con la última versión no funciona. Y debido a esto, yo por ejemplo, no voy a probar siguiendo estos pasos.

Un saludo. Buen trabajo!

cxrlospxndo dijo...

A mi me funciono con la version de Kohana 2.3.4

Gracias por el post

Anónimo dijo...

instale la 3.3.0 y arroja error...
Kohana_HTTP_Exception [ 404 ]: Unable to find a route to match the URI: album.php

SYSPATH\classes\Kohana\Request.php [ 979 ]

974
975 if ( ! $this->_route instanceof Route)
976 {
977 return HTTP_Exception::factory(404, 'Unable to find a route to match the URI: :uri', array(
978 ':uri' => $this->_uri,
979 ))->request($this)
980 ->get_response();
981 }
982
983 if ( ! $this->_client instanceof Request_Client)
984 {

DOCROOT\index.php [ 118 ] » Kohana_Request->execute()

Environment
Included files (50)
DOCROOT\index.php
APPPATH\bootstrap.php
SYSPATH\classes\Kohana\Core.php
SYSPATH\classes\Kohana.php
SYSPATH\classes\I18n.php
SYSPATH\classes\Kohana\I18n.php
SYSPATH\classes\Kohana\Exception.php
SYSPATH\classes\Kohana\Kohana\Exception.php
SYSPATH\classes\Log.php
SYSPATH\classes\Kohana\Log.php
SYSPATH\classes\Config.php
SYSPATH\classes\Kohana\Config.php
SYSPATH\classes\Log\File.php
SYSPATH\classes\Kohana\Log\File.php
SYSPATH\classes\Log\Writer.php
SYSPATH\classes\Kohana\Log\Writer.php
SYSPATH\classes\Config\File.php
SYSPATH\classes\Kohana\Config\File.php
SYSPATH\classes\Kohana\Config\File\Reader.php
SYSPATH\classes\Kohana\Config\Reader.php
SYSPATH\classes\Kohana\Config\Source.php
SYSPATH\classes\Route.php
SYSPATH\classes\Kohana\Route.php
SYSPATH\classes\Request.php
SYSPATH\classes\Kohana\Request.php
SYSPATH\classes\HTTP\Request.php
SYSPATH\classes\Kohana\HTTP\Request.php
SYSPATH\classes\HTTP\Message.php
SYSPATH\classes\Kohana\HTTP\Message.php
SYSPATH\classes\HTTP\Header.php
SYSPATH\classes\Kohana\HTTP\Header.php
SYSPATH\classes\Request\Client\Internal.php
SYSPATH\classes\Kohana\Request\Client\Internal.php
SYSPATH\classes\Request\Client.php
SYSPATH\classes\Kohana\Request\Client.php
SYSPATH\classes\HTTP\Exception.php
SYSPATH\classes\Kohana\HTTP\Exception.php
SYSPATH\classes\HTTP\Exception\404.php
SYSPATH\classes\Kohana\HTTP\Exception\404.php
SYSPATH\classes\View.php
SYSPATH\classes\Kohana\View.php
SYSPATH\classes\Response.php
SYSPATH\classes\Kohana\Response.php
SYSPATH\classes\HTTP\Response.php
SYSPATH\classes\Kohana\HTTP\Response.php
SYSPATH\views\kohana\error.php
SYSPATH\classes\HTML.php
SYSPATH\classes\Kohana\HTML.php
SYSPATH\classes\Debug.php
SYSPATH\classes\Kohana\Debug.php
Loaded extensions (40)
Core
bcmath
calendar
com_dotnet
ctype
date
ereg
filter
ftp
hash
iconv
json
SPL
odbc
pcre
Reflection
session
standard
mysqlnd
tokenizer
zip
zlib
libxml
dom
PDO
Phar
SimpleXML
wddx
xml
xmlreader
xmlwriter
apache2handler
gd
mbstring
mysql
mysqli
pdo_mysql
pdo_sqlite
mhash
xdebug
$_SERVER

Àl dijo...

Lamento no poder ayudarte. Hace mucho que publique ese artículo que era una traducción de un buen artículo en inglés. Finalmente no me introduje en el mundo de Kohana por lo que no sé qué pueda estar funcionando mal, aunque quizás sea porque la versión actual difiere mucho de la de la fecha del artículo (2009).

Anónimo dijo...

ok. Seguire investigando si encuentro la solución aviso.

Parece que en esta versión se alojan en otros directorios los distintos archivos.

b dijo...

Lo intente con la versión mas reciente y viene bastante cambiado, y no funciono :( tendré que leer algo la wiki

clod dijo...

Recomiendo usar Codelobster para ello

Àl dijo...

Gracias por la aportación.

Publicar un comentario



Últimos links en indiza.com