wiki:DesarrolladoresFirstCakePHP

Rápida introducción a CakePHP 1.2

Nota: Este documento es sólo un apoyo al manual  principal de CakePHP.

CakePHP es un framework que implementa el patrón de diseño MVC. Para quienes han trabajado con Ruby on Rails (RoR), muchas cosas de CakePHP les resultarán muy familiares. La Wikipedia señala:

Modelo Vista Controlador (MVC) es un patrón de arquitectura de software que separa los datos de una aplicación, la interfaz de usuario, y la lógica de control en tres componentes distintos. El patrón MVC se ve frecuentemente en aplicaciones web, donde la vista es la página (X)HTML y el código que provee de datos dinámicos a la página.

  • Modelo: la lógica de la aplicación además del acceso a datos (SQL, LDAP, XML, ActiveRecord?, ORM) e integridad de la información (validación).
  • Vista: la interfaz para el usuario: WML para celulares y PDAs, pero más frecuentemente, (X)HTML para los navegadores
  • Controlador: recibe el input del usuario (un link o un formulario), por lo general el controlador consulta al Modelo y la información resultante la pasa a la Vista

Nota: En la programación orientada a objetos (OOP) a las funciones dentro de una clase se les conoce como "métodos", en CakePHP en ocasiones a los métodos de los controladores se les llama "acciones" (actions), pero es indistinto llamarlos de una manera o de otra.

Veamos la siguiente gráfica:

http://trac.chipotle-software.com/karamelo/attachment/wiki/DesarrolladoresFirstCakePHP/process.gif

1) El usuario da clíc sobre la liga HTML  http://server.org/tasks/index
2) El dispatcher (despachador) de CakePHP llama y ejecuta el controlador tasks_controller.php el cual (como se indica en el URL) tiene un método index()
3) Este método index() del controlador llama al modelo Task que hace una consulta a la base de datos SQL
4) El modelo regresa el resultado al controlador
5) El controlador ha concluido su trabajo y le pasa los datos a la vista
6) La vista se integra con el layout HTML
7) El usuario ve el resultado de pinchar sobre  http://server.org/tasks/index

Podría parecer complicado, pero no lo es, luego de algunos días de trabajar con CakePHP esta secuencia muestra su bien pensada lógica y uno se empieza a preguntar "¿Cómo pude trabajar de la otra manera durante tanto tiempo?".

Son muchas las ventajas del patrón MVC: sólidez, diseño, coherencia, modularidad, pero quizás las más importante es que hace mucho más fácil el mantenimiento y la cooperación entre varios desarrolladores. Algunas de las características de CakePHP:

  • Compatible con PHP4 y PHP5
  • Buena implementación de ActiveRecord?
  • Despachador de clases por URL (mejor apariencia)
  • Plantillas rápidas y flexibles
  • Ayudas (helpers) para AJAX, Javascript, HTML Formas y más (<- hace el trabajo menos tedioso)
  • Trabaja desde cualquier directorio sin moverle a Apache
  • Poderoso y sencillo sistema de validación
  • Andamiaje en el CRUD (Scaffolding)
  • Listas de control de acceso (ACL)
  • Data Sanitization (para evitar hackeos)
  • Seguridad, Sesiones, y manejo de llamado a componentes
  • Caché en las vistas

Una de las mejores características de CakePHP es que permite a los desarrolladores seguir los principios DRY (Don't repeat yourself) y KISS (Keep it simple stupid!). Además permite usar patrones de diseño preestablecidos.

Directorios

La ubicación de los archivos en CakePHP es bastante sencilla:

APP
   /views/          <-- vistas con extensión .ctp para cada método de los controladores van aqui
   /views/layouts/  <-- Los diseños generales del sitio con extensión .ctp van aquí  
   /models/         <-- Los modelos van aqui (user.php, entry.php, comment.php, etc)
   /controllers/    <-- Los controladores van aqui (users_controller.php, entries_controller.php, comments_controller.php, etc)
   /webroot/img/    <-- las imágenes públicas van aqui, se pueden crear subdirectorios para organizarlas
   /webroot/css/    <-- las hojas de estilo .css van aqui, se pueden crear subdirectorios para organizarlas
   /webroot/js/    <--  los archivos javascript .js van aqui  

La extensión .ctp para las vistas y los layouts quiere decir "cake template". A partir del directorio webroot inicia la parte "pública" de CakePHP. El resto de los directorio permanece oculto y trabaja "tras bambilanas".

URls

Como señalamos en el diagrama, en CakePHP la llamada al controlador, el método y sus variables se activan mediante el URL, por ejemplo, en lugar de un feo:

 http://project.myserver.org/index.php?controller=pages&method=show&order=DESC&limit=15

En cakePHP sería:

 http://project.myserver.org/pages/show/DESC/15

Este URL quiere decir que hay un controlador "pages", el cual posee un método "show" y estamos pasándole dos valores, DESC y 15. En CakePHP, esta clase sería así:

// File: app/controllers/pages_controller.php

class PagesController extends AppController
{
  public function show($order, $limit)
  {
    $conditions = null;
    $fields     = array('id', 'title', 'body', 'created');
    $ORrder     = 'Page.created ' . $order;
    $data       = $this->Page->findAll($conditions, $fields, $ORder, $limit); // $this->Page es el modelo
    
    $this->set('data', $data); // le paso la variable "$data" a la vista para que muestre el resultado
   }
}

Como decimos, el controlador es el que "enruta" el flujo de información hacia donde debe ir. Los controladores extienden (es decir son "hijas") de la clase madre AppController?.php que es parte del API de CakePHP.

Instalación de CakePHP

La instalación es rápida y muy sencilla:

 $sudo apt-get install cakephp-instaweb postgresql php5 php5-pgsql

cakephp-instaweb es un mini-server que usamos para desarrollar en CakePHP sin meternos en problemas de configuración. Descargamos CakePHP. En estos días la versión estable es la 1.2:

$wget 'http://cakeforge.org/frs/download.php/695/1.2.0.7962.tar.gz/donation=complete' -O Cake12.tar.gz
$tar -zxvf Cake12.tar.gz 
$mv 1.2.0.7962  cakeprojects
$cd cakeprojects/app
$cakephp-instaweb

Veremos dentro de cakeprojects cuatro directorios, app, cake, docs, vendor. El directorio cake nunca lo tocamos pues contiene las librerías core del framework, vendor es donde debemos poner las librerías extras que usamos y que no son parte de CakePHP, por ejemplo las librerías PEAR. Docs... bueno, son los docs. En realidad, es en app donde trabajaremos. Dentro del directorio app esta el directorio webroot, este es el root de la aplicación, es decir, a partir de aqui es lo que está visible para los internautas. Se pueden tener muchos proyectos diferentes de CakePHP usando las mismas librerías del Core. Al ponernos en:

  http://localhost:3000

ya podemos ver a CakePHP. Debemos editar el archivo APP/config/core.php para cambiar el string Salt.

Ahora, como usuario postgres creamos nuestra base de datos PostgreSQL ( I love pgsql ;-) ):

 $createdb -E UNICODE DBKARAMELO

Editamos:

$gedit app/config/database.php.default

Salvamos este archivo como database.php, debemos editar para tener algo así:

class DATABASE_CONFIG
 {
  public $default = array(
                'driver'     => 'postgres',
                'persistent' => false,
                'host'       => 'localhost',
                'login'      => 'postgres',
                'password'   => 'x45RTsom3%&&lululuX0987Ge',
                'database'   => 'DBKARAMELO',
                'prefix'     => '',
        );


   public $test = array('driver' => 'postgres',
                        'connect' => 'pgsql_connect',
                        'host' => 'localhost',
                        'login' => 'user',
                        'password' => 'dsdfsd576hyu89AS45',
                        'database' => 'DBTEST',
                        'prefix' => '');
}
?>

La base de datos $test no la vamos a utilizar en este momento (está base de datos se utiliza para los  UnitTest de CakePHP). El archivo pg_hba.conf de PostgreSQL debe estar configurado para aceptar conexiones del localhost. Ahora el recargar la página debes ver el mensaje "Cake is able to connect to the database".

Helpers, Components y Behaviors

Una de las características que debe poseer todo Framework es la extensibiliad, es decir, debe existir una manera sencilla, documentada y establecida para extender los componentes del framework. Y no sólo eso sino que el framework ya debe venir con extensiones cuyas funcionalidades deben estar listas para usarse.

Los helpers son una solución de CakePHP para dar un formato completo, correcto y válido a las etiquetas (tags) HTML o XHTML, todo ello sin el engorro de escribir todo el tag. Generalmente los helpers comienzan con $html-> o $form-> para los tags normales o $ajax-> en el caso de los helpers de Ajax.

Una de las mejores características de CakePHP es que es fácilmente extendible, es decir, tu puedes crear tus propios helpers, pero eso lo veremos después.

Por ejemplo, para construir una liga de todos los días con el helper $html->link():

  echo $html->link('Ir a las frases', '/quotes/index', array('class'=>'small', 'title'=>'Describe liga'));

Lo que resulta en:

  <a href="/quotes/index" title="Describe liga" clas="small"> Ir a las frases < / a >

Para imprimir una imagen:

   echo $html->image('ruta/my_image.jpg', array("alt"=>"My Images", "title"=>"My Images"); ?>

Es importante señalar que $html->image() busca las imágenes en app/webroot/img/. Por supuesto estos elementos de liga e imagen se pueden mezclar para construir una liga usando una imagen:

   echo $html ->link(
                     html->image('ruta/myimages.jpg', array("alt"=>"My Images", "title"=>"My Images")), // la imagen de la liga  
                     '/quotes/index',             // el URL de la liga
                     array("class"=>"linksmall"),  // la clase de la liga
                     array(), False, False);           // esto le dice a $html->link() que tiene un tag de imagen dentro 

Por otro lado, para construir un formulario en una vista de CakePHP se usa el helper $form:

<?php
// archivo: app/views/pages/add.ctp

echo $html ->div('my_css_class', 'New Page');
echo $form->create('Page', array('action'=>'insert'));
echo $form->input('Page.title', array('size' => 50, 'maxlength' => 120, 'class'=>'formas'));
echo $form->end('Send');
?>

Nótese que en este formulario ya estamos considerando el modelo pues se incluye la validación: $form->error('Page.title', 'Title is required.'); que señala que CakePHP está considerando este campo del formulario como un campo obligatorio. Una vez más los usuarios de Ruby on Rails se sentirán en casa pues es la misma sintaxis.

Notemos también que los tags cortos de PHP, por seguridad, se evitan en el código de CakePHP. Si usamos lo Helpers de Cake, es casi seguro que podemos señalar nuestras páginas como páginas bien hechas y con xHTML válido.

Mi primer MVC

Ahora crearemos nuestro primer MVC. Primero (dentro de PostgreSQL) crearemos la tabla quotes y le agregaremos algunos registros. Por convención, en CakePHP las tablas tienen nombre en plural:

CREATE TABLE quotes (
id serial PRIMARY KEY,
quote varchar(150) NOT NULL,
author varchar(50) NOT NULL
);

INSERT INTO quotes ("quote", "author") VALUES ('Yo no hablo de venganzas ni perdones, el olvido es la única venganza y el único perdón.', 'Borges');
INSERT INTO quotes ("quote", "author") VALUES ('Uno no es lo que es por lo que escribe, sino por lo que ha leído.', 'Borges');
INSERT INTO quotes ("quote", "author") VALUES ('Creo que parte de mi amor a la vida se lo debo a mi amor a los libros.', 'Bioy Casares');
INSERT INTO quotes ("quote", "author") VALUES ('Ven a dormir conmigo: no haremos el amor, él nos hará.', 'Julio Cortazar');

Creamos el modelo para esta tabla:

# Archivo: app/models/quote.php

<?php 

class Quote extends AppModel {

 public $name = 'Quote';
 
 public $validate = array(
		       'quote' => array(
				         'rule'     => array('minLength', '8'),
                                         'message' => 'Mimimum 8 characters long'
				        ),
			'author' => array(
				         'rule'    => array('minLength', '4'),
                                         'message' => 'Mimimum 4 characters long'
				        ),
                         'user_id' => array(
                                      'rule'       => 'numeric',
               	                      'allowEmpty' => false,
	                              'on'         => 'create', // vaidate on INSERT action but not on UPDATE
	                              'required'   => true 
	                              )		 
			 );
}
?>

Nota: en los siguientes ejemplos usaré especificaciones como "private", "restricted", "public" que, como ya sabes, son exclusivas de la POO de PHP5, si estas programando en PHP4, omítelas.

El array $validate de los modelos (en este caso el modelo es Quote), indican que campos son obligatorios en la tabla.

Por convención, los archivos del modelo se nombran en singular así como también la clase, en este caso: quote.php como archivo y Quote como nombre de clase.

Ahora creamos el controlador:

<?php
//Archivo: app/controllers/quotes_controller.php
class QuotesController extends AppController
{
  public $helpers = array('Html', 'Form', 'Javascript');

  public function index() 
  {
     // $this->layout = 'green'; 
     $this->pageTitle = 'Estas son las frases'; // el titulo de la pagina

     $data = $this->Quote->findAll(); // hacemos una consulta a la BD usando el modelo, el método findAll() sin parámetros equivale  a "SELECT * FROM quotes"

     $this->set('data', $data); // Le paso el resultado de la consulta a la vista
  }
}
?>

Esta clase sólo posee un método: index(), si no hay indicación de método en el URL este es el método que CakePHP ejecuta por omisión. Hay que hacer notar que el controlador tiene definida el array $helpers, este array se usa para cargar a los helpers que vamos a usar en las vistas, en este caso llama a tres helpers: HtmlHelper?, FormHelper? y JavascriptHelper?.

La vista

Cada controlador en CakePHP tiene un directorio propio en APP/views para guardar sus vistas, en este caso el controlador quotes_controller.php debe tener un directorio APP/views/quotes, si no existe el directorio correspondiente debemos crearlo con el comando mkdir. Otro ejemplo: si tuviésemos un controlador entries_controller.php deberiamos crear un directorio APP/views/entries para guardar sus vistas.

A su vez, cada método del controlador tiene una vista que presenta el xHTML final al usuario, el nombre del archivo de la vista es el mismo que el del método más la extensión .ctp, es decir si el método se llama index(), el archivo de la vista debe llamarse index.ctp, si el método es listado() la vista debe llamarse listado.ctp, etcétera. En este caso:

<?php
//archivo: app/views/quotes/index.ctp

echo $html->div('title_class', 'Quotes');

echo $html->link('Nueva frase', '/quotes/add/');

foreach ($data as $val):
   echo $html->div('css_class');
      echo $html->link('Editar', '/quotes/edit/'.$val['Quote']['id']);
      echo ' '.$val['Quote']['quote'] . ' <i>'. $val['Quote']['author'].'</i>' .' ';
      echo $html->link('Borrar', '/quotes/delete/'.$val['Quote']['id']);
   echo '</div>';
endforeach;
?>

Ya tenemos nuestro MVC. Si ahora con Firefox nos colocamos en  http://localhost:3000/quotes/index veremos el listado de nuestras frases en PostgreSQL. Así de simple y potente es desarrollar con este framework. Al trabajar con CakePHP, debemos acostumbrarnos a consultar constantemente las clases de su API, pues así sabemos exactamente que métodos y atributos tiene cada clase padre, pues en CakePHP sólo creamos y trabajamos con clases hijas.

El layout

El layout es el template general del sitio, puede ser tan simple como:

<?php
   echo '<?xml version="1.0"?>';
   echo $html->docType();  
?>
<head>  
<?php
   echo $html->css('estilos'); 
?>
    <title><?php echo $title_for_layout; ?></title>
</head>
<body>
<div id="wrap">
    <div id="header">CakePHP is cool!</div>
    <div id="nav">
       <ul>
	   <li><a href="#">Link 1</a></li>
	   <li><a href="#">Link 2</a></li>
       </ul>
   </div>
    <div id="main"><?php echo $content_for_layout; ?></div>
    <div id="sidebar">Sidebar</div>
    <div id="footer">Footer &copy;</div>
</div>
</body>
</html>

Este código lo guardamos como APP/views/layouts/green.ctp.

Nota que la hoja de estilos app/webroot/css/estilos.css se incluye con el helper $html->css("estilos"). La variable $title_for_layout debe ser colocada en el método del controlador:

$this->pageTitle = 'Mi titulo';

La variable $content_for_layout es el resultado de la vista. Puedes tener tantos layouts como te hagan falta, sólo debes indicar en el controlador cuál vas a usar con la variable:

$this->layout = 'green';

Pero por omisión CakePHP busca el archivo APP/views/layouts/default.ctp

Debug

Muchas veces cuando hacemos una consulta el modelo como en:

$data = $this->Quote->findAll($conditions, $fields, $order, $limit);

queremos saber qué contiene el array $data, para saberlo podemos hacer un debug usando:

 die(debug($data));

La función debug() es fundamental al momento de desarrollar en CakePHP y se puede llamar en cualquier punto del MVC: vista, controlador o modelo.

Attachments