Parámetros en cakePHP

Vimos que el formato por defecto de enrutamiento es:

http://example.com/controller/action/param1/param2/param3

Por ejemplo, si yo accedo a:

http://localhost/cakeblog/autors/index/ola/k/ase

Dentro de la función index del controlador de autores tengo la variable $this->passedArgs que vale lo siguiente:

Array
(
[0] => ola
[1] => k
[2] => ase
)

Si yo paso parámetros con nombre el array es asociativo con el nombre del parámetro como clave. Ejemplo

http://localhost/cakeblog/autors/index/ola:5/k:7/ase:patata

Me da lo siguiente:

Array
(
[ola] => 5
[k] => 7
[ase] => patata
)

 

Ejercicio buscar por edad

Controlador:

 public function edad($inicio = 0, $fin = 30) {
 
 if (!empty($this->request->query['inicio'])) {
 $inicio = $this->request->query['inicio'];
 }
 if (!empty($this->request->query['fin'])) {
 $fin = $this->request->query['fin'];
 }

 $params = array(
 'conditions' => array('Autor.edad BETWEEN ? AND ? ' => array($inicio, $fin)),
 'recursive' => -1,
 'order' => 'Autor.edad',
 );
 $autores = $this->Autor->find('all', $params);

 $this->set(compact('autores', 'inicio', 'fin'));
 }

La vista:

<div class="autors view">
 <h2><?php echo __('Autores entre ' . $inicio . " y " . $fin); ?></h2>
 <?php
 echo $this->Form->create('Edad', array('type' => 'get',
 'url' => array('controller' => 'Autors', 'action' => 'edad')));
 echo $this->Form->input('inicio', array(
 'label' => 'Introduzca la edad de inicio',
 'default'=>$inicio,
 ));
 echo $this->Form->input('fin', array(
 'label' => 'Introduzca la edad final',
 'default'=>$fin,
 ));
 echo $this->Form->end('Buscar');
 ?>
 <?php
 foreach ($autores as $autor):
 ?>
 <dl>
 <dt><?php echo __('Id'); ?></dt>
 <dd>
 <?php echo h($autor['Autor']['id']) . " " . h($autor['Autor']['name']); ?>
 &nbsp;
 </dd>
 <dt><?php echo __('Edad'); ?></dt>
 <dd>
 <?php echo h($autor['Autor']['edad']); ?>
 &nbsp;
 </dd>
 </dl>
 <?php
 endforeach;
 ?>
</div>

Opción upload para autores

//En index.ctp:

 <?php echo $this->Html->link(__('Upload'), array('action' => 'upload', $autor['Autor']['id'])); ?>

//En el controlador

 public function upload($id = null) {
 if (!$this->Autor->exists($id)) {
 throw new NotFoundException(__('Invalid autor'));
 }
 if ($this->request->is(array('post', 'put'))) {
 

 $archivo = $this->request->data['Autor']['archivo'];
 move_uploaded_file($archivo['tmp_name'], WWW_ROOT . 'autores/' . $id . "/" . $archivo['name']);


 $this->Flash->success(__('Archivo subido.'));
 return $this->redirect(array('action' => 'index'));
 
 } else {
 $options = array('conditions' => array('Autor.' . $this->Autor->primaryKey => $id));
 $this->set('autor', $this->Autor->find('first', $options));
 }
 }

//La vista (entera)

<h2>Subiendo archivos para el autor <?=h($autor['Autor']['name'])?></h2>
<div class="autors form">
 
 <?php echo $this->Form->create('Autor', array('type' => 'file')); ?>
 <fieldset>
 <legend><?php echo __('Escoja el archivo a subir'); ?></legend>
 <?php
 
 echo $this->Form->file('archivo');
 ?>
 </fieldset>
 <?php echo $this->Form->end(__('Submit')); ?>
</div>
<div class="actions">
 <h3><?php echo __('Actions'); ?></h3>
 <ul>

 <li><?php echo $this->Form->postLink(__('Delete'), array('action' => 'delete', $this->Form->value('Autor.id')), array('confirm' => __('Are you sure you want to delete # %s?', $this->Form->value('Autor.id')))); ?></li>
 <li><?php echo $this->Html->link(__('List Autors'), array('action' => 'index')); ?></li>
 </ul>
</div>

Subir archivos en cakePHP

Para subir archivos en cakePHP debemos modificar la vista para que el formulario esté preparado para subir archivos:

<?php echo $this->Form->create('Country', array('type' => 'file')); ?>
 <fieldset>
 <legend><?php echo __('Add Country'); ?></legend>
 <?php
 echo $this->Form->input('country');
 echo $this->Form->file('archivo');
 echo $this->Form->input('last_update');
 ?>
 </fieldset>
<?php echo $this->Form->end(__('Submit')); ?>

Después en el controlador tenemos el archivo dentro de request->data, lo movemos igual que lo haríamos en php:

 public function add() {
 if ($this->request->is('post')) {
 $this->Country->create();
 if ($this->Country->save($this->request->data)) {
 $archivo = $this->request->data['Country']['archivo'];
 move_uploaded_file($archivo['tmp_name'], WWW_ROOT . 'files' . DS . $archivo['name']);
 $this->Flash->success(__('The country has been saved.'));
 return $this->redirect(array('action' => 'index'));
 } else {
 $this->Flash->error(__('The country could not be saved. Please, try again.'));
 }
 }
 }

Ejemplo ver archivos de carpeta del autor

En el controlador:

App::uses('Folder', 'Utility');
....
 public function view($id = null) {
 if (!$this->Autor->exists($id)) {
 throw new NotFoundException(__('Invalid autor'));
 }
 $options = array('conditions' => array('Autor.' . $this->Autor->primaryKey => $id));
 $this->set('autor', $this->Autor->find('first', $options));
 $dir = new Folder(WWW_ROOT . "autores/" . $id);
 $this->set('files', $dir->find(".*"));
 }

En la vista:

<h1>Archivos</h1>
 <ul>
 <?php
 foreach ($files as $file):
 ?>
 <li><?= $this->Html->link($file,"/autores/".$autor['Autor']['id']."/".$file) ?></li>
 <?php
 endforeach;
 ?>
 </ul>

 

Ejemplos callback

//Antes de guardar en la base de datos convierte la fecha de formato y al mail añade 'mailto:'
 public function beforeSave($options = array()) {
 if (isset($this->data['Autor']['fecha'])) {
 $this->data['Autor']['fecha'] = date("Y-m-d", strtotime($this->data['Autor']['fecha']));
 }
 if (isset($this->data['Autor']['mail'])) {
 $this->data['Autor']['mail'] = 'mailto:' . $this->data['Autor']['mail'];
 }
 }

//Después de recuperar los datos de la base de datos convierte la fecha de mysql a d/m/y y quita el 'mailto:' de los mails
 public function afterFind($results, $primary = false) {

 foreach ($results as $key => $val) {
 if (isset($results[$key]['Autor']['fecha'])) {
 $results[$key]['Autor']['fecha'] = date("d-m-Y", strtotime($results[$key]['Autor']['fecha']));
 }
 if (isset($results[$key]['Autor']['mail'])) {
 $results[$key]['Autor']['mail'] = str_replace('mailto:', '', $results[$key]['Autor']['mail']);
 }
 }
 return $results;
 }

//Filtra todos los resultados que no tengan el mail 'aaa.com'
 public function beforeFind($query) {
 $query['conditions']['Autor.mail LIKE'] = "%@aaa.com%";

 return $query;
 }

//Cuando añadimos un autor nuevo crea una carpeta con su id
 public function afterSave($created, $options = array()) {

 if ($created) {
 $dir = new Folder(WWW_ROOT . 'autores/' . $this->data['Autor']['id'], true);
 }
 }

//Impedimos que se borre el autor con id 3
 public function beforeDelete($cascade = true) {
 
 if ($this->id == 3) {
 return false;
 } else {
 return true;
 }
 }

//Cuando borramos un autor eliminamos su carpeta
 public function afterDelete(){
 $folder = new Folder();
 $folder->delete(WWW_ROOT . 'autores/' . $this->id);
 }

 

Funciones de callback del modelo en cakephp

En cakephp disponemos de los siguientes callbacks predefinidos:

beforeFind(array $query)

afterFind(array $results, boolean $primary = false)

beforeValidate(array $options = array())

afterValidate()

beforeSave(array $options = array())

afterSave(boolean $created, array $options = array())

beforeDelete(boolean $cascade = true)

afterDelete()

En algunos disponemos de los datos en

$this->data o $this->id

Poner un calendar para la fecha

Para poner un calendar en una fecha tenemos varias opciones. Quizá la más sencilla sea incluir jquery y jquery ui. Para hacer esto debemos añadir las siguientes líneas en default.ctp:

 echo $this->Html->script("https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js");
 echo $this->Html->css('https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css');
 echo $this->Html->script("https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js");

Una vez hecho, sólo tenemos que cambiar la vista donde esté nuestra fecha, haciéndola de tipo texto y añadiendo el datepicker:

<script>
$(function() {
 $("#calendario").datepicker({
 dateFormat: "dd-mm-yy",
 });
});
</script>
......
 echo $this->Form->input('fecha', array(
 'id' => 'calendario',
 'type' => 'text'
 ));

Para que nos convierta la fecha al formato de mysql tenemos que ponerlo en el beforesave del modelo:

  public function beforeSave($options = array()) {
 if (isset($this->data['Autor']['fecha'])) {
 $this->data['Autor']['fecha'] = date("Y-m-d", strtotime($this->data['Autor']['fecha']));
 }
 
 }

 public function afterFind($results, $primary = false) {

 foreach ($results as $key => $val) {
 if (isset($results[$key]['Autor']['fecha'])) {
 $results[$key]['Autor']['fecha'] = date("d-m-Y", strtotime($results[$key]['Autor']['fecha']));
 }
 }
 return $results;
 }

Ejercicio validaciones

Creemos esta tabla en nuestra base de datos:

CREATE TABLE `autors` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(45) NOT NULL,
 `mail` varchar(200) NOT NULL,
 `edad` int(11) DEFAULT NULL,
 `password` varchar(20) DEFAULT NULL,
 `fecha` date DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

En nuestro modelo de datos tendremos que poner las siguientes validaciones:

Nombre y mail no pueden estar en blanco

mail tiene que ser un mail válido

La edad tiene que ser un número natural entre 18 y 120

La contraseña debe ser alfanumérica y entre 6 y 20 caracteres de longitud

La fecha una fecha en formato d-m-y

Solución:

 public $validate = array(
 'name' => array(
 'notBlank' => array(
 'rule' => array('notBlank'),
 'message' => 'El nombre no puede estar en blanco',
 ),
 ),
 'mail' => array(
 'notBlank' => array(
 'rule' => array('notBlank'),
 'message' => 'El mail no puede estar en blanco',
 ),
 'mail' => array(
 'rule' => array('email'),
 'message' => 'Debe ser un email válido',
 ),
 ),
 'edad' => array(
 'natural' => array(
 'rule' => array('naturalNumber'),
 'message' => 'Números positivos y enteros',
 ),
 'rango' => array(
 'rule' => array('range',17.9,120.1),
 'message' => 'La edad debe estar entre 18 y 120',
 ),
 ),
 'password' => array(
 'alfanumerica' => array(
 'rule' => array('alphaNumeric'),
 'message' => 'La contraseña debe ser alfanumérica',
 ),
 'rango' => array(
 'rule' => array('lengthBetween',6,20),
 'message' => 'La contraseña debe tener entre 6 y 20 caracteres de longitud',
 ),
 ),
 'fecha' => array(
 'fecha' => array(
 'rule' => array('date', 'dmy'),
 'message' => 'Debe ser una fecha',
 ),
 ),
 );