Contenido

Kohana: Tutorial 1

Con este artículo comienzo una serie de tutoriales de Kohana.

Kohana es un Framework PHP, es decir: un entorno de trabajo “prefabricado” que ya nos soluciona algunos de los problemas más típicos de la programación.

Como me gusta avanzar programando, programaremos desde la primera línea. Para ello vamos a tener como objetivo la creación de un “ToDoList Manager”, es decir: vamos a crear una web que nos permita gestionar tareas.

En el ejemplo se accederá a base de datos. Debo decir que me ha costado mucho encontrar ejemplos válidos que accedan a base de datos.

PHP

Prerrequisitos

Voy a utilizar Kohana versión 3.0.8. Seguramente sea compatible con las siguientes.

Un poquito de teoría

Kohana sigue un patrón Modelo-Vista-Controlador (MVC). Hay mucho escrito sobre este patrón, por lo que no me extenderé demasiado:

  • Modelo: Los datos tal cual se guardan.
  • Vista: La representación en la pantalla.
  • Controlador: La lógica de negocio.

Configuración

Vamos a configurar Kohana. Hemos desplegado el framework y vamos al fichero application/bootstrap.php, en el que vamos a habilitar algunas cosas. No podéis copiar a ciegas; aquí sólo os doy un ejemplo de lo que tengo yo puesto. Tened también en cuenta que no lo pego entero, ya que nos valen la mayor parte de los valores por defecto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php
Kohana::init(array(
  'base_url'   => '/~miguel/kohana',
  'index_file' => 'index.php',
));

Kohana::modules(array(
  'database'   => MODPATH.'database',   // Database access
  'orm'        => MODPATH.'orm',        // Object Relationship Mapping
  ));
?>

Como es sencillo, voy rápido: Le decimos a Kohana dónde está nuestra aplicación y cuál es el archivo que va a renderizar las cosas. Como véis, yo lo tengo en /~miguel/kohana. Después habilitamos algunos módulos, que servirán para acceder a base de datos.

Si sois tan pijos como yo y os gustan las URL’s limpias, mejor si indicáis el “index_file” de la manera que se indica arriba y después creáis un .htaccess como el siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# Turn on URL rewriting
RewriteEngine On

# Installation directory
RewriteBase /~miguel/kohana/

# Protect hidden files from being viewed

  Order Deny,Allow
  Deny From All


# Protect application and system files from being viewed
RewriteRule ^(?:application|modules|system)\\b.* index.php/$0 [L]

# Allow any files or directories that exist to be displayed directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# Rewrite all other URLs to index.php/URL
RewriteRule .* index.php/$0 [PT]

Finalmente, configuramos la base de datos en el archivo modules/database/config/database.php. Ya tenéis un patrón y es autoexplicativo. Centraros en ‘default’ y olvidaros de ‘alternate’ (o borradlo si queréis).

El modelo

El modelo es lo que existe en la base de datos, básicamente. Así que comenzamos por crear una base de datos:

1
2
3
4
5
6
7
8
9
CREATE TABLE `tasks` (
  'id' int(11) NOT NULL AUTO_INCREMENT,
  'title' varchar(60) NOT NULL,
  'description' longtext NOT NULL,
  'created' datetime NOT NULL,
  'duedate' datetime DEFAULT NULL,
  PRIMARY KEY ('id'),
  UNIQUE KEY 'title' ('title')
) ENGINE=MyISAM AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;

Ahora tenemos que decirle a Kohana que use esta tabla. Para ello basta crear el archivo application/classes/model/task.php:

1
2
3
4
5
<?php defined("SYSPATH") OR die("No Direct Script Access");
class Model_Task extends ORM
{
}
?>

Bien. Paremos. ¿Para qué queremos un archivo vacío?

Realmente no está vacío. Para empezar, estamos restringiendo el acceso. Todos nuestros archivos PHP salvo las vistas deberían comenzar así, por seguridad. Además, estamos heredando de ORM, y esta clase padre ya nos dará todo lo que necesitamos: accederá a la base de datos y verá qué campos necesitamos aquí.

Personalmente, tanto automatismo no me gusta demasiado y prefiero indicarle qué tabla es la que quiero emplear:

1
2
3
4
5
6
<?php defined("SYSPATH") OR die("No Direct Script Access");
class Model_Task extends ORM
{
  public $_table_name = "tasks";
}
?>

Y hemos terminado el modelo. Fácil, ¿no?

El Controlador

Creamos el archivo application/classes/controller/task.php:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php defined('SYSPATH') OR die('No Direct Script Access');

class Controller_Task extends Controller
{
  public function __construct(Kohana_Request $request)
  {
    parent::__construct($request);
  }

  public function action_index ()
  {
    $vista = View::factory('task_index');
    $vista->set ('task_list', $this->get_tasklist());
    $this->request->response = $vista;
  }

  public function action_add ()
  {
    $vista = View::factory('task_add');
    $this->request->response = $vista;

    if ( ! isset($_POST) OR ! array_key_exists('title', $_POST))
      return;

    if ( $this->create_task () )
      $vista->set ('message', 'Task added');
    else
      $vista->set ('message', 'Error al añadir');
  }

  private function create_task ()
  {
    $newtask = ORM::factory('task');
    $newtask->title = $_POST['title'];
    $newtask->description = $_POST['description'];
    $newtask->created = strftime('%Y-%m-%d %H.%M.%S');
    $newtask->duedate = $this->getAsDate($_POST['duedate']);
    $newtask->save();

    return $newtask->saved();
  }

  private function getAsDate ($value)
  {
    $result = null;
    if (isset ($value)) {
      $result = $value." 00.00.00";
    }
    print_r($result);
    return $result;
  }

  private function get_tasklist ()
  {
    $tasks = ORM::factory('task')->find_all();

    $result = array();

    foreach ($tasks as $task)
      $result[] = $task;

    return $result;
  }

}
?>

Analicemos un poco este código.

Lo primero de todo es la seguridad, por lo que evitamos el acceso directo.

A continuación se crea una clase Controller_Task que hereda de Controller. El nombre de la clase está sujeto a una “convención de nombres”:https://kohanaframework.org/guide/about.conventions, y heredamos de Controller porque somos un controlador :D

El constructor no tiene mucho misterio: tenemos que implementarlo y llamamos al constructor padre. No tenemos nada más que inicializar, por lo que podemos terminar.

La función action_index es, como el nombre indica, una acción. Será la acción por defecto que se ejecute cuando alguien realice una solicitud sobre el controlador. Si simplificamos su código hasta un simple echo 'hola';, podremos comprobar que todo funciona accediendo a la misma dirección que pusimos en la configuración, en ‘base_url’, seguido del nombre básico del controlador, es decir, ’task’. En mi caso, la dirección es https://localhost:/~miguel/kohana/task. Si no habéis activado las URLs limpias, tendréis que indicar quién debe renderizar esa página, accediendo a algo como https://localhost:/~miguel/kohana/index.php/task.

Como vemos, la acción action_index crea una vista a partir de una plantilla y la devuelve como respuesta. Veremos más sobre estas vistas en el apartado siguiente.

La acción action_add va a tratar de crear una tarea nueva. Además, voy a reutilizar esta función. Si viene sin datos, sólo mostrará la ventana. Si viene con datos, creará una nueva tarea. Por ello, lo primero que hago es crear la vista que voy a devolver, ya que es común a ambos casos de uso, y después trato de insertarla, guardándome un mensaje en cada caso.

La función create_task es quien realmente crea la tarea. Para ello hace uso del ORM, obteniendo los valores de la variable $_POST. Las fechas tengo que modificarlas a mano para que tengan el formato esperado por mysql (“YYYY-MM-DD hh.mm.ss”).

La función get_tasklist serializa la lista de tareas para pasársela a la vista.

Cosas importantes

Aquí hay dos cosas importantes: el uso de la base de datos y el paso de parámetros con la vista.

Para usar la base de datos, estamos usando el ORM. Éste está “bastante bien documentado”:https://kohanaframework.org/guide/api/ORM.

Para obtener datos de la vista, los obtenemos de la variable $_POST, ya que éste ha sido el método que he decido utilizar para el envío de datos (y suele ser el más recomendable). Los nombres de los campos son los que he puesto en la vista y que veremos más adelante.

Para enviar datos a la vista, lo que hago es establecerlos al objeto “View” con la función “set”. Esto funcionará como una tabla Hash cuando esté en la vista.

Las vistas

Antes de entrar en materia, como no me gusta repetirme, crearemos un par de archivos. Todas las vistas deben ir en application/views.

Los dos archivos a crear no tienen mayor misterio y no entraré a describirlos:

header.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="https://www.w3.org/1999/xhtml">
<head>
  <title>Vista para Kohana PHP
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <link href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
  <script type="text/javascript">
    Date.firstDayOfWeek = 1;
    Date.format = 'yyyy/mm/dd';
      $(document).ready(function() {
        $(".date").datepicker({ dateFormat: 'yy-mm-dd', firstDay: 1 });
      });
  </script>
</head>
<body>

footer.php

1
2
</body>
</html>

Sí, hago algo de uso de jquery, pero no os asustéis que sólo es para la inserción de fechas. El resto de la aplicación será horrible :-P

Vamos con el índice:

task_index.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php include 'header.php'; ?>

<table>
  <thead>
    <tr>
      <th>Title
      <th>Creation
      <th>Due date
      <th>Planned date
      <th>Completed
    </tr>
  </thead>
  <tbody>
  <?php
  foreach ($task_list as $task)
  {
    echo "";
    echo ''.$task->title."";
    echo "".$task->created."";
    echo "".$task->duedate."";
    echo "";
  }
  ?>
  </tbody>
</table>

<?php echo "".html::anchor("task/add","Add new task"); ?>

<?php include "footer.php"; ?>

Como en la función del controlador “action_index” insertamos la variable $task_list, ahora podemos recuperarla y recorrerla. De esta manera creamos una vista horrible. Dejo como tarea ponerla bonita, ya que eso no es el tema de este tutorial.

Nos queda poder añadir tareas (task_add.php):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php include 'header.php'; ?>
<?php

if (isset($message))
{
  print $message;
}

print html::anchor('task','List of tasks');

print form::open();
print form::label('title', 'Título');
print form::input('title');
print form::textarea('description');
print form::label('duedate', 'Due Date:');
print form::input('duedate', null, array ('class'=>'date') );
print form::submit('submit', 'Send');
print form::close();
?>

<?php include 'footer.php'; ?>

En este caso he creado un formulario utilizando las funciones de Kohana. Como véis, los nombres de los campos se corresponden con los que utilicé en la función “create_task”.

Fin

Como decían en en 1,2,3… “hasta aquí puedo leer”. Como primera aplicación me parece mucho más interesante que el triste “hola mundo”, ya que tiene todos los componentes de una aplicación real: modelo, vista y controlador (el “hola mundo” no tiene más que controlador), acceso a bases de datos, etc.

No he entrado con el “update_task” ya que no es más complicado que el “create_task”. Lo dejo como ejercicio.

Habrá segunda parte de este tutorial.

Referencias

No hay mucha documentación de Kohana. Sin embargo, el ORM de Kohana sí está bien documentado y no creo que tengáis mayor problema con esa parte. Siempre prefiero utilizar sistemas que me eviten el SQL a mano, ya que éste puede tener “sql-injection”.

Acordáos de seguir las convenciones. También podéis echar un vistazo a otros tutoriales, como el de Ajaxman o el de Christian Gilè, sin contar los tutoriales de la propia página de Kohana.

Espero que os haya resultado interesante.