Paginación en cakePHP: Paginator (helper en vistas)

Los datos recuperados por Paginator se muestran en las vistas de igual manera que los recuperados por otras funciones (como el find). Pero tenemos un helper que nos permite dibujar elementos de paginación de una manera sencilla.

Por ejemplo, para mostrar cabeceras que se puedan ordenar, podemos usar ‘sort:

echo $this->Paginator->sort('post_id');

Podemos ponerle un título alternativo:

echo $this->Paginator->sort('post_id', 'Código');

Para mostrar los números de página tenemos la función ‘numbers’:

echo $this->Paginator->numbers();

Tenemos muchas opciones:

  • before Insertar contenido antes de los números.

  • after Insertar contenido después de los números.

  • modulus Cuantos números de página, por defecto 8

  • separator Separador, por  defecto ` |`

  • tag El tag html que rodea los números, por defecto <span>.

  • first Si lo ponemos tendremos un enlace a la página inicial. Podemos poner el texto o el número de páginas a mostrar. El primero muestra un enlace con texto y el segundo las dos primeras páginas:

    echo $this->Paginator->numbers(array('first' => 'First page'));
    
    echo $this->Paginator->numbers(array('first' => 2, 'last' => 2));
  • last Igual que el anterior para las últimas.

  • ellipsis texto para la elipsis, por defecto ‘…’

  • class Si queremos una clase especial para los números.

  • currentClass La clase para la página actual. Por defecto es current.

    Para poner enlaces directos tenemos varias funciones: next, first… veamos como usarlas:

prev($title = ‘<< Previous’, $options = array(), $disabledTitle = null, $disabledOptions = array())

next($title = ‘Next >>’, $options = array(), $disabledTitle = null, $disabledOptions = array())

Añaden un enlace a la página siguiente o a la anterior. El primer parámetro es el título. Los siguientes son:

  • $options (mixed) Las opciones del elemento (class, tag…)
  • $disabledTitle (string) – El título cuando el enlace está deshabilitado (por estar en la primera página, caso del prev, por estar en la última, caso del next).
  • $disabledOptions (mixed) – Las opciones del elemento (class, tag…) cuando está deshabilitado.

Ejemplos:

echo $this->Paginator->prev(
  ' << ' . __('Anterior'),
  array(),
  null,
  array('class' => 'prev disabled')
);

echo $this->Paginator->prev(__('Anterior'), array('tag' => 'li'));

Para poner enlaces a la primera y última página, además de las opciones de numbers, tenemos las siguientes funciones:

first($first = ‘<< first’, $options = array())

last($last = ‘last >>’, $options = array())

Con las siguientes opciones:

  • tag Tag que rodea al elemento
  • after Contenido después del enlace
  • separator Separador entre enlaces
  • ellipsis Texto para la elipse

Para poder ver un contador tenemos:

counter($options = array())

Con las siguientes opciones en el texto:

  • {:page} – Página actual.
  • {:pages} – Total de páginas.
  • {:current} – Registro actual.
  • {:count} – Total de registros.
  • {:start} – Primer registro que se muestra.
  • {:end} – Último registro que se muestra.

Ejemplo:

echo $this->Paginator->counter(
    'Página {:page} de {:pages}, mostrando {:current} registros de un total de {:count} , empezando en {:start}, acabando en {:end}'
);

 

Validar campos en cakePHP

CakePHP permite validar los valores que se introducen en los campos. Para ello tenemos que definirlos en el modelo. Por ejemplo:

 public $validate = array(
        'login' => 'alphaNumeric',
        'email' => 'email',
        'born' => 'date'
    );

Aquí estamos indicando que ‘login’ debe ser alfanumérico, ’email’ de tipo mail y ‘born’ de tipo fecha.

Cada campo puede tener más de una regla, y estas puede ser complejas, incluyendo el mensaje que tenemos que mostrar:

 public $validate = array(
        'login' => array(
            'alphaNumeric' => array(
                'rule' => 'alphaNumeric',
                'required' => true,
                'message' => 'Sólo letras y números'
            ),
            'between' => array(
                'rule' => array('lengthBetween', 5, 15),
                'message' => 'Entre 5 y 15 caracteres'
            )
        ),
        'password' => array(
            'rule' => array('minLength', '8'),
            'message' => 'Mínimo ocho caracteres de longitud'
        ),
        'email' => 'email',
        'born' => array(
            'rule' => 'date',
            'message' => 'Debe ser una fecha',
            'allowEmpty' => true
        )
    );

Diferentes valores y su explicación:

'rule' => array('notBlank'), //La regla que vamos a aplicar (ver más adelante)
 //'message' => 'Para poner mensaje personalizado',
 //'allowEmpty' => false, //Permitir valor en blanco
 //'required' => false, //Requerido sí o no
 //'last' => false, // Parar validación despues de esta regla
 //'on' => 'create', // Limitar validación a 'create' o 'update'

Aquí está la lista de las diferentes validaciones.

public $validate = array(
    'login' => array(
        'rule' => 'alphaNumeric',
        'message' => 'Sólo letras y números'
    )
);
public $validate = array(
    'password' => array(
        'rule'    => array('lengthBetween', 5, 15),
        'message' => 'Longitud entre 5 y 15'
    )
);
public $validate = array(
    'myCheckbox' => array(
        'rule' => array('boolean'),
        'message' => 'Valores booleanos (0 o 1)'
    )
);
public $validate = array(
    'ccnumber' => array(
        'rule' => array('cc', array('visa', 'maestro'), false, null),
        'message' => 'Tarjeta de crédito, en este caso visa o maestro.'
    )
);
public $validate = array(
    'age' => array(
        'rule' => array('comparison', '>=', 18),
        'message' => 'El valor tiene que ser mayor o igual que 18.'
    )
);

public $validate = array(
    'age' => array(
        'rule' => array('comparison', 'greater or equal', 18),
        'message' => 'Igual que antes. Valores permitidos:  “is greater”, “is less”, “greater or equal”, “less or equal”, “equal to”, and “not equal”.'
    )
);
public $validate = array(
    'born' => array(
        'rule' => array('date', 'ymd'),
        'message' => 'Fecha en formato YY-MM-DD',
        'allowEmpty' => true
    )
);

/****
Los separadores pueden ser espacios, puntos, guiones o barras
‘dmy’ e.g. 27-12-2006 or 27-12-06 
‘mdy’ e.g. 12-27-2006 or 12-27-06 
‘ymd’ e.g. 2006-12-27 or 06-12-27 
‘dMy’ e.g. 27 December 2006 or 27 Dec 2006
‘Mdy’ e.g. December 27, 2006 or Dec 27, 2006 (coma opcional)
‘My’ e.g. (December 2006 or Dec 2006)
‘my’ e.g. 12/2006 or 12/06 
‘ym’ e.g. 2006/12 or 06/12 
‘y’ e.g. 2006 
***/
public $validate = array(
    'price' => array(
        'rule' => array('decimal', 2) //Dos números decimales

    )
);
public $validate = array(
    'email' => array(
        'rule' => array('email', true),
        'message' => 'Email válido, incluyendo host válido (true)'
    )
);
public $validate = array(
    'food' => array(
        'rule' => array('equalTo', 'cake'),
        'message' => 'Igual a cake'
    )
);
public $validate = array(
    'image' => array(
        'rule' => array('fileSize', '<=', '1MB'),
        'message' => 'El tamaño tiene que ser menor que 1Mb'
    )
);
public $validate = array(
    'function' => array(
         'allowedChoice' => array(
             'rule' => array('inList', array('Foo', 'Bar')),
             'message' => 'El valor tiene que estar en la lista'
         )
     )
 );
public $validate = array(
    'clientip' => array(
        'rule' => array('ip', 'IPv4'), // or 'IPv6' or 'both' (default)
        'message' => 'Tiene que ser una IP.'
    )
);
public $validate = array(
    'login' => array(
        'rule' => 'isUnique',
        'message' => 'Valor único en la base de datos.'
    )
);
public $validate = array(
    'login' => array(
        'rule' => array('maxLength', 15),
        'message' => 'La cadena no puede tener más de 15 caracteres.'
    )
);
public $validate = array(
    'login' => array(
        'rule' => array('minLength', 8),
        'message' => 'Cadena mínimo 8 caracteres.'
    )
);
public $validate = array(
    'salary' => array(
        'rule' => array('money', 'left'),
        'message' => 'Indicador de moneda.'
    )
);
public $validate = array(
    'cars' => array(
        'rule' => 'numeric',
        'message' => 'El valor debe ser numérico.'
    )
);
public $validate = array(
    'wheels' => array(
        'rule' => 'naturalNumber',
        'message' => 'Número natural sin incluir el 0.'
    ),
    'airbags' => array(
        'rule' => array('naturalNumber', true),
        'message' => 'Número natural incluyendo el 0.' 
), 
);
public $validate = array(
    'number' => array(
        'rule' => array('range', -1, 11),
        'message' => 'Valor numérico en un rango'
    )
);
public $validate = array(
    'website' => array(
        'rule' => 'url' //Comprueba que sea una url válida
    )
);

 

public $validate = array(
    'login' => array(
        'rule' => '/^[a-z0-9]{3,}$/i',
        'message' => 'Expresión regular, en este caso que empieza por tres caracteres alfanuméricos'
    )
);

Todas las tablas del ejemplo Blog

CREATE TABLE posts (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

/* Then insert some posts for testing: */
INSERT INTO posts (title, body, created)
    VALUES ('The title', 'This is the post body.', NOW());
INSERT INTO posts (title, body, created)
    VALUES ('A title once again', 'And the post body follows.', NOW());
INSERT INTO posts (title, body, created)
    VALUES ('Title strikes back', 'This is really exciting! Not.', NOW());
CREATE TABLE comments ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, subject VARCHAR(50), body VARCHAR(1500), post_id int, created DATETIME DEFAULT NULL, modified DATETIME DEFAULT NULL ); /* algunos valores de test */ INSERT INTO comments (subject,body,post_id,created) VALUES ('Good','Good article',1, NOW()); INSERT INTO comments (subject,body,post_id,created) VALUES ('What?','I don\'t understand',2, NOW()); INSERT INTO comments (subject,body,post_id,created) VALUES ('ola','ola k ase??',3, NOW());

CREATE TABLE themes (
 id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
 title VARCHAR(50),
 created DATETIME DEFAULT NULL,
 modified DATETIME DEFAULT NULL
);


/* algunos valores de test */
INSERT INTO themes (title,created)
 VALUES ('MVC', NOW());
INSERT INTO themes (title,created)
 VALUES ('cakePHP', NOW());
INSERT INTO themes (title,created)
 VALUES ('Depression', NOW());
CREATE TABLE tags (
 id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
 tag VARCHAR(50),
 created DATETIME DEFAULT NULL,
 modified DATETIME DEFAULT NULL
);


/* algunos valores de test */
INSERT INTO tags (tag,created)
 VALUES ('MVC', NOW());
INSERT INTO tags (tag,created)
 VALUES ('CakePHP', NOW());
INSERT INTO tags (tag,created)
 VALUES ('JS', NOW());

CREATE TABLE posts_tags (
 id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
 post_id INT,
 tag_id INT
);

Scaffold y Bake

Hasta la versión 3.0 de cakePHP se puede hacer ‘scaffold’ en los controladores. ¿Qué es esto? Es poner un ‘andamiaje’ que proporciona las funcionalidades básicas de altas, bajas y modificaciones (CRUD).

Para hacerlo basta poner en el controlador lo siguiente:

var $scaffold;

Y automáticamente, si el modelo está bien, obtenemos el entorno para manejar los datos.

En la versión 3.0 se ha eliminado por falta de uso. Es comprensible, ya que si queremos obtener la funcionalidad básica podemos hacerlo vía bake.

Para conseguirlo necesitamos tener en el path del sistema la ruta al php. Por ejemplo:

SET PATH=%PATH%;C:\xampp\php

Añadimos también la siguiente ruta;

Ruta de mi cake\app\Console\

Ejemplo:

C:\xampp\htdocs\cakeblog\app\Console

Colocarnos en la carpeta de la aplicación y ejecutar:

Ruta de mi cake\app\cake bake

Ejemplo:

c:\xampp\htdocs\cakeblog\app\cake bake

O

Ruta de mi cake\app\cake bake all

Esto nos genera modelos, vistas y controladores con toda la funcionalidad, listos para usar y modificar.

 

Ejemplo:

cd c:\xampp\htdocs\cakeblog\app

c:\xampp\htdocs\cakeblog\app\cake bake

c:\xampp\htdocs\cakeblog\app\cake bake all

Ejercicio: Mantenimiento tabla Themes

Añadamos la tabla themes a la base de datos y realicemos el mantenimiento:

CREATE TABLE themes (
 id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
 title VARCHAR(50),
 created DATETIME DEFAULT NULL,
 modified DATETIME DEFAULT NULL
);


/* algunos valores de test */
INSERT INTO themes (title,created)
 VALUES ('MVC', NOW());
INSERT INTO themes (title,created)
 VALUES ('cakePHP', NOW());
INSERT INTO themes (title,created)
 VALUES ('Depression', NOW());

Acción edit y vista

 public function edit($id = null) {
 //Parte donde se recuperan los datos enviados por la vista y se guardan en la BD
 if ($this->request->is(array('post', 'put'))) {
 if ($this->Post->save($this->request->data)) {
 $this->Flash->success(__('Post actualizado.'));
 return $this->redirect(array('action' => 'index'));
 }
 $this->Flash->error(__('Unable to update your post.'));
 } else {
 //Parte donde se recupera el Post y se manda a la vista
 if (!$id) {
 throw new NotFoundException(__('Invalid post'));
 }

 $post = $this->Post->findById($id);
 if (!$post) {
 throw new NotFoundException(__('Invalid post'));
 }

 if (!$this->request->data) {
 $this->request->data = $post;
 }
 }
 }
 <h1>Edit Post</h1>
<?php
echo $this->Form->create('Post');
echo $this->Form->input('title');
echo $this->Form->input('body', array('rows' => '3'));
echo $this->Form->input('id', array('type' => 'hidden'));
echo $this->Form->end('Save Post');
?>

Convenciones de nombres en cakephp

Aquí tenemos la lista de convenciones de nombres:

Convenciones de nombres en cakephp

Hay un problema. CakePHP cambia del plural al singular en controladores/modelos, y si tenemos una tabla como ‘Autores’ nos generará un modelo llamado ‘Autore’. Esto tiene dos soluciones, usar en estos casos un plural inglés en las tablas (autors) o añadir un par de líneas en /app/Config/bootstrap.php:

Inflector::rules('singular', array('rules' => array('/(.*)res$/i' => '\1r', '/(.*)nes$/i' => '\1n', '/(.*)des$/i' => '\1d', '/(.*)ses$/i' => '\1s'), 'irregular' => array(), 'uninflected' => array()));
Inflector::rules('plural', array('rules' => array('/(.*)r$/i' => '\1res', '/(.*)n$/i' => '\1nes', '/(.*)d$/i' => '\1des', '/(.*)s$/i' => '\1ses'), 'irregular' => array(), 'uninflected' => array() ) );
Visto aquí:

Rutas en CakePHP

Las rutas en cakephp tienen un formato definido:

http://example.com/controlador/accion/param1/param2/param3

Aunque también se pueden usar parámetros con nombres:

Ejemplos de todo:

URL: /monos/saltar
Dirige a: MonosController->saltar();

URL: /productos
Dirige a: ProductosController->index();

URL: /tareas/ver/45
Dirige a: TareasController->ver(45);

URL: /donaciones/ver/recientes/2001
Dirige a: DonacionesController->ver('recientes', '2001');

URL: /contenidos/ver/capitulo:modelos/seccion:asociaciones
Dirige a: ContenidosController->ver();
$this->passedArgs['capitulo'] = 'modelos';
$this->passedArgs['seccion'] = 'asociaciones';