Tabla de Contenidos

CakePHP

Instalación

Configuración

$Route->connect('/', array('controller' => 'users', 'action' => 'display', 'home'));

El primer parámetro es el directorio en cuestión que estamos tratando (/) controller y action las páginas controlador con su respectiva acción. A partir de ahí serán los parámetros a la acción. En este caso 'home' es el primero.

Modelos, Vistas y Controladores

Nombres de clase y de archivos

Ejemplo para el elemento Post que tiene en la base de datos una tabla correspondiente llamada posts:

Controladores


Por ejemplo: /app/controllers/videos_controller.php

class VideosController extends AppController
{
    function view($id)
    {}
    function rent($customer_id, $video_id)
    {}
    function search($query)
    {}
}

Será accedido como:

Métodos

Variables

class ProductsController extends AppController
{
    var $beforeFilter = array('checkAccess');
    function checkAccess() {}
    function index() {}
}

Aquí se llamaría antes a checkAccess que a index

Parámetros

Los siguientes serían la xxx de $this→params['xxx']:

Modelos

Un modelo es un acceso a una base de datos (más concretamente a una tabla), su nombre es el de la tabla en singular (por ejemplo la tabla users tendrá el modelo user). También contendrá datos de validación y métodos específicos para esa tabla.
Los modelos se guardan en /app/models y han de heredar de la clase AppModel. Para acceder a ellos, en el controlador correspondiente, tendrás un objeto a dicho modelo. Un ejemplo de modelo sería el siguiente:

<?php
class User extends AppModel {
    var $name = 'User';
    var $validate = array();
    var $hasMany = array('Image' => array('className' => 'Image'));
 
    function makeInactive($uid) {...}
}
?>

Si existen métodos que se comparten entre dos o más modelos puedes crear tu propio modelo por defecto en app/app_model.php.

Definir funciones propias

Por ejemplo métodos para esconder u ocultar los post de un blog:

<?php
class Post extends AppModel {
   var $name = 'Post';
   function hide ($id=null) {
      if ($id) {
          $this->id = $id;
          $this->saveField('hidden', '1');
      }
   }
   function unhide ($id=null) {
      if ($id) {
          $this->id = $id;
          $this->saveField('hidden', '0');
      }
   }
}
?>

Funciones para recoger datos

$this->Post->findByTitle('My First Blog Post');
$this->Author->findByLastName('Rogers');
$this->Specimen->findAllByKingdom('Animalia');
function view($id) {
        $this->set('image', $this->Image->find("id = $id")); // Imágen mostrada
        $this->set('neighbours', $this->Image->findNeighbours(null, 'id', $id));
}
$this->set('Roles', $this->Role->generateList(null, 'role_name ASC', null, '{n}.Role.id', '{n}.Role.role_name'));
/*
Devolvería:
array(
    '1' => 'Account Manager',
    '2' => 'Account Viewer',
    '3' => 'System Manager',
    '4' => 'Site Visitor'
);
*/
function posterFirstName() {
  $ret = $this->query("SELECT first_name FROM posters_table WHERE poster_id = 1");
  $firstName = $ret[0]['first_name'];
  return $firstName;
}

Indicando condiciones

Podemos especificar las condiciones mediante un array que luego pasaríamos a un método find como $this→Post→find($conditions);, ejemplos:

$conditions = array("Post.title" => "This is a post");
              array("Post.title" => "<> This is a post");
              array("Post.title" => array("First post", "Second post", "Third post")); 
                                    array ("or" => array ("Post.title" => array("First post", "Second post", "Third post"), "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))));
              array("Author.name" => "Bob", "or" => array ("Post.title" => "LIKE %magic%", "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks")));

Guardando datos

Para guardar datos sólo has de pasar al método save del modelo un array del estilo:

Array (
    [ModelName] => Array (
            [fieldname1] => 'value'
            [fieldname2] => 'value'
    )
)

Es muy sencillo utilizar el helper HTML ya que los datos los trae formateados para ser leidos por CakePHP ya que sólo has de añadir algo así: $html→input('Model/fieldname');.
Los datos enviados desde un form se guardan automáticamente en el array $this→data del controlador.
Un ejemplo de una función de editar:

function edit($id) {
   if (empty($this->data)) {
        $this->Property->id = $id;  // El modelo se carga automáticamente en $this->Property
        $this->data = $this->Property->read();
   } else {
      if ($this->Property->save($this->data['Property'])) 
         $this->flash('Your information has been saved.', '/properties/view/'.$this->data['Property']['id'], 2);
      // Si algunos campos no son válidos o el guardado fallase, el formulario se volvería a mostrar
   }
}

La validación se hace de forma automática, pero si no quisieses que al guardar se validase tendrás que hacer la llamada así: save($data, false).
Otras funciones muy útiles:

Llamadas internas del modelo

Existen funciones que pueden ser sobreescritas y serán llamadas en el momento indicado, según la acción a la que correspondan:

Variables del modelo

Asociaciones

Vistas

Las vistas son la parte de una página correspondiente a una acción de un controlador.

Los layouts


Al crear un layout has de indicar donde se colocará el código del controlador, para ello has de mostrar la variable $content_for_layout (también existe $title_for_layout).

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title><?php echo $title_for_layout?></title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
</head>
<body>
 
<div id="header">
    <div id="menu">...</div>
</div>
 
<?php echo $content_for_layout ?>
 
<div id="footer">...</div>
 
</body>
</html>

Para indicar qué título se ha de mostrar en $title_for_layout sólo has de asignar un valor a la variable $pageTitle:

class UsersController extends AppController
{
    function viewActive()
    {
        $this->pageTitle = 'View Active Users';
    }
}

Puedes definir un layout para un controlador cambiando su variable $layout, si quieres que una acción tenga un layout distinto tendrás que asignarlo dentro del método de la clase.

class CoverController extends AppController {
	var $layout = "cover";
	function index () {
		$this->layout = "default";
	}
...

Elements

Los elementos son esas pequeñas porciones de código que se muestran repetidas veces en todo el site (banners, menús extra, controles de navegación…). Básicamente son pequeñas vistas que pueden ser incluidas en otras vistas.
Han de ser ubicados en /app/views/elements como ficheros .thtml. y tienen acceso a los datos declarados para ser usados en la vista.
Para mostrar un elemento, en la vista:

<?php echo $this->renderElement('helpbox');?>

Puedes pasar un array de datos como segundo parámetro a este método.

 <?php echo $this->renderElement('helpbox', array("helptext" => "Oh, this text is very helpful.")); ?> 

Vistas de error

Puedes personalizar los mensajes de error creando los ficheros correspondientes a las vistas que CakePHP tiene definidas para los casos en los que un error aparece, están en /app/views/error estas son:

Las plantillas por defecto están en cake/libs/view/template/errors y puedes copiarlas al directorio de tu aplicación y luego editarlas.
Por defecto muchas de las vistas no serán mostradas cuando el DEBUG sea más grande que 0.

Otros elementos

Helpers

Son funciones que se necesitan a menudo en las vistas.

HTML

Las funciones de este helper se utilizan para generar tags html, y es accesible utilizando $html. Además, si AUTO_OUTPUT (variable asignada en /app/config/core.php) está a true el helper automáticamente mostrará la tag sin tener que poner código php con echos. Aunque puedes cambiar esto específicamente para una función dándole el valor deseado en el parámetro $return.
Las funciones que usa este helper están definidas en tags.ini.php, si quieres editarlas sólo tendrás que copiar /cake/config/tags.ini.php en /app/config/.

Funciones (muy explícitas por sí solas como para ponerme yo a explicarlas) para crear tags:

Más funciones HTML, pero estas para crear formularios:

$html->radio('Note/status', array('1' => 'Complete', '2' => 'In Progress'), null, array('value' => '1'));

Cómo utilizar los helpers?

Los helpers generan automáticamente los tags, se rellenan si ocurre un error y muestran los menajes de error. Imaginemos que estamos montando una aplicación para tomar notas, en el NotesController tendremos una acción de edición:

   function edit($id)
   {
      //Lo primero es mirar si los datos se han enviado correctamente
      if (!empty($this->data['Note'])) {
         // Aquí intentaríamos validar el formulario
         if ($this->Note->save($this->data['Note'])) {
            // Si se ha guardado correctamente llevaríamos al usuario a un lugar concreto
            $this->flash('Your information has been saved.', '/notes/edit/' . $id);
            exit();
         } else {
            // Si no generaríamos un error
            // llamaríamos a $this->Note->validates($this->data['Note']); si no estamos guardando
            // mostraríamos los errores
            $this->validateErrors($this->Note);
            // Y llamaríamos ya al método para mostrar la página
            $this->render();
         }
      }
      // Si no hemos recibido datos es que los queremos editar, por lo tanto mostrar la info
      $this->set('note', $this->Note->find("id = $id"));
      $this->render();
   }

En el modelo Note tendríamos un id de nota, un id del editor y el cuerpo de la nota. La vista mostraría los datos de la nota y permitiría al usuario introducir nuevos y estaría en app/views/notes/edit.thtml:

<?php echo $html->formTag('/notes/edit/' . $html->tagValue('Note/id')) ?>
 
<table cellpadding="10" cellspacing="0">
<tr>
   <td align="right">Body: </td>
   <td>
      <!-- Mostramos la nota y los errores encontrados  -->
      <?php echo $html->textarea('Note/body', array('cols'=>'60', 'rows'=>'10')); ?>
      <?php echo $html->tagErrorMsg('Note/body', 'Please enter in a body for this note.') ?>
   </td>
</tr>
<tr>
   <td></td>
   <td>
      <!-- Datos ocultos en el form  -->
      <?php echo $html->hidden('Note/id')?>
      <?php echo $html->hidden('note/submitter_id', $this->controller->Session->read('User.id'))?>
   </td>
</tr>
</table>
<?php echo $html->submit()?>
</form>

Algunas funciones necesitan que se les indique un $fieldName (por ejemplo tagErrorMsg), esto permite a CakePHP saber qué data le estás pasando y validarla correctamente, para ello se pasa en un string con valor “nombreModelo/nombreCampo”, por ejemplo, para añadir un título a la nota:

<?php echo $html->input('Note/title') ?>
<?php echo $html->tagErrorMsg('Note/title', 'Please supply a title for this note.')?>

Los mensajes de error se muestran en una div del estilo: <div class=“error_message”></div>

Ajax

El helper de Ajax de CakePHP utiliza los scripts de script.aculo.us, por lo que para poder utilizarlos deberías tener las librerías en /app/webroot/js e incluirlas en la vista. También hay que tener en cuenta que la mayoría de estas funciones reciben un parámetro de opciones, este es un array de claves (url, update, confirm…) con valores, de esta forma se le asignan las propiedades.

JavaScript

Numeros

Texto

Fecha hora

Scaffolding

El Scaffolding es la capacidad que tiene CakePHP de agilizar el pantallas de inserción, consulta y adición a la base de datos generando el código.
Se analiza la base de datos y se auto-genera el código.
Sólo has de añadir $scaffold; la variable al controlador:

<?php
class CategoriesController extends AppController
{
    var $scaffold;
}
?>

Validación

   var $validate = array(
      'login' => '/[a-z0-9\_\-]{3,}$/i',
      'password' => VALID_NOT_EMPTY,
      'email' => VALID_EMAIL,
      'born' => VALID_NUMBER
   );

Sanitization

Sanitize es la clase dentro de cake que te permite filtrar las entradas a la base de datos, impidiendo que se lancen algunos carácteres que no quieres.

uses('sanitize');
$mrClean = new Sanitize();

Métodos según lo que se quiera 'limpiar':

Sesiones

CakePHP puede salvar las sesiones de tres formas distintas, para indicar cual es la deseada hay que editar /app/config/core.php y cambiar el valor de CAKE_SESSION_SAVE por 'php' (que las maneja php), 'database' (las maneja la base de datos) o 'cake' (las maneja el mismo CakePHP).
Para utilizarlas puede hacer uso de las siguientes funciones:

Cuando accedes a la session desde la view necesitarás usar el helper de Sesiones:

<p>Nombre: <?php $session->read('user.username'); ?> </p>

Cuando accedes a la sesión desde un controlador tienes el objeto Session dentro de este:

if (!$this->Session->check('user')) {
	$this->redirect('/cover/index');
        exit();
}

Cuando almacenas un array de claves valor, tendrás que acceder a los datos mediante un string:

$theUser = $this->User->findByUsername ($this->data['User']['username']);
$this->Session->write('user', $theUser['User']);
$this->set('name', $this->Session->read ('user.username'));

Components

Un componente se crea en el directorio app/controllers/components/

class FooComponent extends Object
{
    var $someVar = null;
    var $controller = true;
    function startup(&$controller)
    {}
    function doFoo()
    {
        $this->someVar = 'foo';
    }
}

La función startup se llama al iniciar, si no quieres que sea usada o llamada pon la variable de clase $disableStartup a true.
En el controlador se agregará el componente:

var $components = array('Foo');

Y lo usuará:

$this->Foo->doFoo();

Componente Request Handler

Este componente viene con CakePHP y controla las peticiones AJAX, para usuarlo ha de ser declarado como un valor dentro de la variable del controlador $components, una vez esté declarado puede ser accedido mediante la variable RequestHandler:

class PostsController extends AppController {
    var $components = array('RequestHandler');
    function beforeFilter () {
        if ($this->RequestHandler->accepts('html'))
        ....

Sus funciones son:

Puede que alguna vez queramos eliminar datos de la salida, para ello tenemos las siguientes funciones que eliminan tags:

setAjax es otra función muy util porque se usa para detectar automáticamente peticiones de ajax y asignar el layout del controlador como una vista AJAX:
El fichero list.thtml:

<ul>
<? foreach ($things as $thing):?>
<li><?php echo $thing;?></li>
<?endforeach;?>
</ul>

En el controlador:

function list() {
    $this->RequestHandler->setAjax($this);
    $this->set('things', $this->Thing->findAll());
}

Plugins

Estructura:

/app
    /plugins
        /pizza
            /controllers
            /models
            /views
            /pizza_app_controller.php  
            /pizza_app_model.php       

En /app/plugins/pizza/pizza_app_controller.php:

<?php
class PizzaAppController extends AppController
{}
?>

Y en /app/plugins/pizza/pizza_app_model.php

<?php
class PizzaAppModel extends AppModel
{}
?>
class PizzaOrdersController extends PizzaAppController
{
    var $name = 'PizzaOrders';
    function index()  {}
    function placeOrder()  {}
}

Montar una página

Portada

Podemos crear la portada a nuestro site creando el archivo /app/views/pages/home.thtml ya que es tal como está configurado por defecto en el routes.php. Pero también podemos crear la nuestra propia creando por ejemplo un /app/views/pages/cover.thtml y cambiando la configuración de la ruta en routes.php por $Route→connect ('/', array('controller'⇒'pages', 'action'⇒'display', 'cover'));.

Apariencia por defecto

Por defecto, toda página que crees aparecerá dentro de una página en la que se lee “CakePHP Rapid Development”, ese es el layout por defecto que está en cake/libs/view/templates/layouts/default.thtml, no es necesario que lo borres, simplemente con que crees el app/views/layouts/default.thtml ya lo tendrás.

Añadir CSS

Los archivos de estilo .css que quieras añadir a tu aplicación deberás hacerlo en la carpeta app/webroot/css, luego, para añadirlos a tu layout, en el head, haz (si por ejemplo se llama generic.css el archivo de estilos):

<?php echo $html->css('generic'); ?>

Añadir contenido

Para añadir contenido a un layout tienes varias variables en el helper de html que te serán de ayuda:

Para añadir links o direciones dentro del site utiliza $html→url para que te construya el path:

<form action="<?php echo $html->url('/users/login'); ?>" method="post">

Un vistazo al acceso a DB

Lectura

Imaginemos que tienes una DB de usuarios, cada uno con un perfil, es decir, dos tablas users y profiles en la que profiles se enlaza a users mediante un campo users_id dentro de esta. Cuando quieres mostrar un perfil para un usuario concreto, en el controlador puedes hacer un método del estilo:

function showprofile ($id) {
	$this->set('profile', $this->Profile->findByUsers_id($id));
}

Este busca en los perfiles por el campo users_id y lo asigna para la vista en una variable llamada profile, ahora para acceder a ella desde la vista:

<td>Nombre:</td> <td><?php echo $profile['Profile']['realname']; ?></td>

Campos de la DB en el formulario

Cuando vayas a editar algún perfil probablemente prefieras mostrar un formulario con los campos actuales ya colocados en los cuadros de texto y preparados para ser editados, para que CakePHP lo haga automáticamente deberás asignar a la variable del controlador $data el perfil:

$this->data = $this->Profile->findByUsers_id($this->Session->read('user.id'));

Luego podrás acceder a esta mediante el helper de html:

<tr>
  <td id="fieldname">Nombre Real:</td>
  <td><?php echo $html->input('Profile/realname', array('size' => 20)); ?></td>
</tr>

Esto nos sirve básicamente para indicar que un campo del formulario corresponde a un campo de la base de datos.
También puedes asignar estos campos manualmente en el controlador si editas la variable $data antes de llamar a la vista.

$this->data = $this->Profile->findByUsers_id($this->Session->read('user.id'));
$this->data['Profile']['users_id'] = $this->Session->read('user.id');

Guardar y editar

Tanto para guardar como para editar existe la función save, a esta le pasas los datos del modelo que, si el controlador ha sido llamado desde un formulario, los datos estarán en la variable $data.
La única diferencia a la hora de guardar y actualizar es que para actualizar es obligatorio indicar el id (que el modelo ha de tener).

$this->Profile->id = $this->data['Profile']['id'];
$this->Profile->save($this->data['Profile']);

Puedes guardar y editar los registros manualmente:

$actualizar = array();
$actualizar['User'] = array();
$actualizar['User']['login'] = 'danguer1';
$actualizar['User']['password'] = 'hola';
$this->id = $id_guardado;
$this->save($actualizar);

Acciones sobre la BD

Contar registros

Por ejemplo desde el controlador:

if ($this->Profile->findCount ("users_id = $id") == 0) // Perfil no definido
{ ... }

O desde el modelo con una función que después podrá ser llamada desde el controlador como: $this→User→existsUsername('juan24');:

   function existsUsername ($username) {
   	return ($this->findCount ("User.username = '$username'") > 0);
   }

Otros ejemplos:

$this->findCount("User.login='danguer' AND Article.title LIKE '%php%'");
Otras consultas
function hasProfile ($id) {
   $ret = $this->query("select COUNT(id) as count from profiles where users_id = " . $id);
   $num = $ret[0][0]['count'];
   return ($num > 0);
}

Otras cosillas a tener en cuenta

Ejemplos

Creación de validación de usuario (login)

  1. Crear la tabla users, esta tendrá campos como username, password
  2. Crear el modelo User.
  3. Crear la view app/views/users/login.thtml, a esta se le añadirán los cuadros de texto para la introducción de nombre y password.
  4. Crear el controlador de usuarios /app/controllers/users_controller.php.
    1. Añadir al controlador de usuarios un método login, ver más abajo. En este se recoge al usuario y si el login es correcto se escriben sus datos en la sesión.
    2. Añadir al controlador de usuarios un método de logout, ver más abajo. En este únicamente se elimina al usuario de la sesión.
  5. Crear un /app/app_controller.php, el controlador general de la aplicación donde añadiremos la función que chequeará si el usuario está logueado (checkSession). Para ello mira en la sesión y si no existe llama a exit(), nota que es una llamada a exit, lo que cancela cualquier ejecución y redirige al usuario al home del site.
  6. Ahora puedes añadir a cualquier otro controlador (de mensajes, de posts…) un método llamado beforeFilter, este se llamará automáticamente antes de cada llamada a cualquier método del controlador, si añades ahí una llamada al checkSession será obligatorio hacer un login para editar los posts, mensajes… O puedes únicamente colocar el checkSession al inicio de los métodos que desees.

Método login del controlador de usuarios:

function login() {
	if (!empty($this->data)) {
		$theUser = $this->User->findByUsername ($this->data['User']['username']);
		$bEmptyPassword = empty($this->data['User']['password']);
		$bSamePassword = ($theUser['User']['password'] == $this->data['User']['password']);
		if ((!$bEmptyPassword) && ($bSamePassword)) {
			$this->Session->write('user', $theUser['User']);
			$this->redirect("/users/mainpage");
			return;
		}
	}
	$this->set("error", true);
	$this->redirect ("/cover/index/1");
}

Método de logout del usuario:

function logout () {
	$this->Session->del('user');
	$this->redirect("/cover/index");
}

Controlador general de la aplicación:

class AppController extends Controller {
    function checkSession() {
        if (!$this->Session->check('user')) {
            $this->redirect('/');
            exit();
        }
    }
}

Método beforeFilter:

function beforeFilter() {
    $this->checkSession();
}

Tips & Tricks

Páginas estáticas

Para crear páginas estáticas has de crearlas en /app/views/pages como archivo .thtml. Para acceder a ellas sería: <ruta>/pages/<nombre>, por ejemplo si se crea una página llamada /views/pages/about.thtml será accesible mediante http://www.example.com/pages/about.

Variables globales

Podemos usar un objeto Configure para guardar lo que queramos y será accesible desde cualquier lugar de CakePHP.

$config =& Configure::getInstance();
$config->myVar = 'test';

Controlador sin modelo

Puedes crear un controlador sin modelo de base de datos asignando un array vacío a la variable $uses:

  <?php
    class HellosController extends AppController {
        var $name = 'Hellos';
        var $uses = array();
 
        function index() {
        }
    }    
  ?>

Notas

Convenciones de la Base de Datos

Ficheros