Autenticación cakePHP 3

En la cabecera del modelo Usuarios ponemos lo siguiente

 App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth');

Y dentro de la clase la siguiente función:

 public function beforeSave($options = array()) {
 if (isset($this->data[$this->alias]['password'])) {
 $passwordHasher = new BlowfishPasswordHasher();
 $this->data[$this->alias]['password'] = $passwordHasher->hash(
 $this->data[$this->alias]['password']
 );
 }
 return true;
}

Autenticación cakePHP 2

En la cabecera de UsuariosController ponemos lo siguiente

 App::uses('BlowfishPasswordHasher', 'Controller/Component/Auth');

Y dentro de la clase la siguiente función:

 public function beforeFilter() {
 parent::beforeFilter();
 // Allow users to register and logout.
 $this->Auth->allow('add');
}

Autenticación cakePHP 1

Ponemos lo siguiente en la cabecera del AppController:

 App::uses('AuthComponent', 'Controller/Component');

Y esto dentro de la clase:

  public $components = array(
 'Session',
 'Auth' => array(
 'loginRedirect' => array(
 'controller' => 'entradas',
 'action' => 'index'
 ),
 'logoutRedirect' => array(
 'controller' => 'usuarios',
 'action' => 'login'
 ),
 'authenticate' => array(
 'Form' => array('userModel' => 'Usuario', 'passwordHasher' => 'Blowfish')
 ),
 'loginAction' => array('controller' => 'usuarios', 'action' => 'login')
 )
 );

También podemos permitir ciertas acciones si ponemos lo siguiente en el beforeFilter:

   $this->Auth->allow('index');

Autenticación cakePHP 4

La vista en Usuarios:

<div class="usuarios form">
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('Usuario'); ?>
 <fieldset>
 <legend>
 <?php echo __('Please enter your username and password'); ?>
 </legend>
 <?php echo $this->Form->input('username');
 echo $this->Form->input('password');
 ?>
 
 </fieldset>
<?php echo $this->Form->end(__('Login')); ?>
</div>

Crear cakephp rest client

Para probar si funcionan  bien los webservices creados vamos a crear una web que los ‘consuma’. En cualquier cakephp (puede ser el mismo) crearemos un controlador ClienteController:

 <?php

App::uses('AppController', 'Controller');
App::uses('HttpSocket', 'Network/Http');

class ClienteController extends AppController {

 public $components = array('Security', 'RequestHandler');
//Aquí pondréis la url de vuestra web
 public $url = "http://localhost/cakeempresa/";

 public function index() {
 
 }

 public function rest_index() {
 $link = $this->url . 'rest_productos.json';

 $data = null;
 $httpSocket = new HttpSocket();
 $response = $httpSocket->get($link, $data);
 $this->set('response_code', $response->code);
 $this->set('response_body', $response->body);

 $this->render('/Cliente/respuesta');
 }

 public function rest_view($id) {

 $link = $this->url . 'rest_productos/' . $id . '.json';

 $data = null;
 $httpSocket = new HttpSocket();
 $response = $httpSocket->get($link, $data);
 $this->set('response_code', $response->code);
 $this->set('response_body', $response->body);

 $this->render('/Cliente/respuesta');
 }

 public function rest_add() {

 $link = $this->url . "rest_productos.json";
 $data = null;
 $httpSocket = new HttpSocket();
 $data['Producto']['precio'] = 74;
 $data['Producto']['proveedor_id'] = 1;
 $data['Producto']['referencia'] = 'Extra';
 $response = $httpSocket->post($link, $data);

 $this->set('response_code', $response->code);
 $this->set('response_body', $response->body);

 $this->render('/Cliente/respuesta');
 }

 public function rest_edit($id) {

 $link = $this->url . 'rest_productos/' . $id . '.json';
 $data = null;
 $httpSocket = new HttpSocket();
 $data['Producto']['precio'] = 27;
 $data['Producto']['referencia'] = 'Actualizada referencia';
 $response = $httpSocket->put($link, $data);
 $this->set('response_code', $response->code);
 $this->set('response_body', $response->body);

 $this->render('/Cliente/respuesta');
 }

 public function rest_delete($id) {

 // remotely post the information to the server
 $link = $this->url . 'rest_productos/' . $id . '.json';
 $data = null;
 $httpSocket = new HttpSocket();
 $response = $httpSocket->delete($link, $data);
 $this->set('response_code', $response->code);
 $this->set('response_body', $response->body);

 $this->render('/Cliente/respuesta');
 }

}

Vista en View\Cliente\index.ctp:

 
<h1>Acciones</h1>
<p>
Escoja
<ul>
<li><?php echo $this->Html->link('Lista Productos', array('controller' => 'cliente', 'action' => 'rest_index')); ?></li><li><?php echo $this->Html->link('Ver producto con ID 1', array('controller' => 'cliente', 'action' => 'rest_view', 1)); ?></li>
<li><?php echo $this->Html->link('Añadir producto', array('controller' => 'cliente', 'action' => 'rest_add')); ?></li>
<li><?php echo $this->Html->link('Actualizar producto 2', array('controller' => 'cliente', 'action' => 'rest_edit', 2)); ?></li>
<li><?php echo $this->Html->link('Borrar producto 3', array('controller' => 'cliente', 'action' => 'rest_delete', 3)); ?></li></ul> </p>

Vista en View\Cliente\respuesta.ctp:

 
<h1>Código respuesta</h1>
<p><?php echo $response_code; ?></p>

<h1>Cuerpo respuesta</h1>
<p><?php echo $response_body; ?></p>

Crear Cakephp Rest

Añadimos las siguientes líneas en routes.php (antes de’ require CAKE . ‘Config’ . DS . ‘routes.php’;’):

  Router::mapResources('rest_productos');
 Router::parseExtensions();

Y añadimos el controlador restProductos:

class RestProductosController extends AppController {

 public $uses = array('Producto');
 public $components = array('RequestHandler');

 public function index() {

 $productos = $this->Producto->find('all');
 $this->set(array(
 'productos' => $productos,
 '_serialize' => array('productos')
 ));
 }

 public function add() {
 $this->Producto->create();
 if ($this->Producto->save($this->request->data)) {
 $message = 'Created';
 } else {
 $message = 'Error';
 }

 $this->set(array(
 'message' => $message,
 '_serialize' => array('message')
 ));
 }

 public function view($id) {

 $producto = $this->Producto->findById($id);
 $this->set(array(
 'producto' => $producto,
 '_serialize' => array('producto')
 ));
 }

 public function edit($id) {
 $this->Producto->id = $id;

 if ($this->Producto->save($this->request->data)) {
 $message = 'Saved';
 } else {
 $message = 'Error';
 }
 $this->set(array(
 'message' => $message,
 '_serialize' => array('message')
 ));
 }

 public function delete($id) {
 if ($this->Producto->delete($id)) {
 $message = 'Deleted';
 } else {
 $message = 'Error';
 }
 $this->set(array(
 'message' => $message,
 '_serialize' => array('message')
 ));
 }

}

Algunos ejemplos sobre el ejercicio empresa

Poner una validación personalizada para el NIF:

 'nif' => array(
 'notBlank' => array(
 'rule' => array('notBlank'),
 'message' => 'El nif no puede estar en blanco',
 ),
 'alfa' => array(
 'rule' => array('custom', '/^(X(-|\.)?0?\d{7}(-|\.)?[A-Z]|[A-Z](-|\.)?\d{7}(-|\.)?[0-9A-Z]|\d{8}(-|\.)?[A-Z])$/i'),
 'message' => 'Debe ser un nif válido',
 ),
 )

Los productos no se borran, sólo pasan a ser inactivos. En el delete no borramos el producto, solo pasamos el campo activo a 0:

 public function delete($id = null) {
 $this->Producto->id = $id;

 if (!$this->Producto->exists()) {
 throw new NotFoundException(__('Invalid producto'));
 }
 $this->request->allowMethod('post', 'delete');
 if ($this->Producto->saveField('activo', 0)) {
 $this->Flash->success(__('The producto has been deleted.'));
 } else {
 $this->Flash->error(__('The producto could not be deleted. Please, try again.'));
 }
 return $this->redirect(array('action' => 'index'));
 }

En el beforefind del modelo productos ponemos como condición que activo sea 1:

 public function beforeFind($query) {
 $query['conditions']['activo'] = 1;
 return $query;
 }

En la base de datos ponemos como valor por defecto de activo ‘1’ y eliminamos de cualquier vista la referencia al campo activo.

En la acción upload del controlador Productos compruebo que lo que me suben es una imagen:

 $archivo = $this->request->data['Producto']['archivo'];
 if (explode('/', $archivo['type'])[0] == 'image') {
 move_uploaded_file($archivo['tmp_name'], WWW_ROOT . 'img' . DS . $this->request->data['Producto']['referencia'] . DS . $archivo['name']);
 $this->Flash->success(__('Archivo subido.'));
 } else {
 $this->Flash->error(__('Solo se permiten imágenes.'));
 }

Para ver las imágenes que hemos subido dentro de la vista de producto. En el controlador pasamos las imágenes encontradas:

 public function view($id = null) {

 if (!$this->Producto->exists($id)) {
 throw new NotFoundException(__('Invalid producto'));
 }
 $options = array('conditions' => array('Producto.' . $this->Producto->primaryKey => $id));
 $producto = $this->Producto->find('first', $options);
 $this->set('producto', $producto);
 $dir = new Folder(WWW_ROOT . "img/" . $producto['Producto']['referencia']."/");
 $this->set('files', $dir->find(".*"));
 }

En la vista (view.ctp) añadimos las líneas que nos mostrarán las imágenes:

 <?php
 foreach ($files as $file):
 $this->Html->image($producto['Producto']['referencia']."/".$file);
 endforeach;
 ?>

Ejemplo componentes

class UtilidadesComponent extends Component {

 public $components = array();

 function areaCirculo($radio) {
 return pi() * $radio * $radio;
 }

 function Circulo($radio) {
 return 2 * pi() * $radio;
 }

 public function frase() {
 $spam = array('Muy bueno', 'Estupenda entrada', 'Eres un crack', 'Lo mejor que he leído', 'Ole y ole', 'Me ha gustado', 'Bastante bueno', 'Muy interesante', 'De gran utilidad', 'Está bastante bien', 'Me ha hecho pensar');
 $com = $spam[rand(0, count($spam) - 1)];
 return $com;
 }

 function importeConIva($cantidad) {
 return $cantidad * 1.21;
 }

}

Funciones globales

Éstas son las funciones globales disponibles en CakePHP. Muchas de ellas simplemente facilitan la llamada a funciones de PHP con nombres largos, pero otras (como vendor() y uses()) se pueden usar para incluir código o realizar otras funciones útiles. Lo más probable es que si estás buscando una función para realizar una tarea con mucha frecuencia, la encuentres aquí.

__

__(string $string_id, boolean $return =  false)

Esta función gestiona la localización en las aplicaciones CakePHP. El parámetro $string_id identifica la ID de una traducción, mientras que el segundo parámetro indica si se debe mostrar automáticamente la cadena (por defecto), o devolverla para su procesamiento (pasar el valor true para que esto suceda).

Visita la sección Localización e Internacionalización para más información.

am

am(array $uno, $dos, $tres...)

Combina todos los arrays pasados como parámetros y devuelve el array resultante.

config

Puede ser usado para cargar archivos desde el directorio config mediante include_once. La función checa si existe el archivo antes de incluir y regresa un booleano. Toma un número opcional de argumento.

Ejemplo: config('some_file', 'myconfig');

convertSlash

convertSlash(string $cadena)

Sustituye las barras (“/”) por subrayados (“_”) y elimina el primer y el último subrayados en una cadena. Devuelve la cadena convertida.

debug

debug(mixed $var, boolean $showHtml = false)

Si el nivel de depuración, variable de configuración DEBUG, es distinto de cero, se muestra $var. Si $showHTML es true, los datos se formatean para mostrarlos adecuadamente en los navegadores web.

env

env(string $key)

Obtiene una variable de entorno a partir de las fuentes disponibles. Alternativa si las variables $_SERVER o $_ENV están deshabilitadas.

También permite emular las variables PHP_SELF y DOCUMENT_ROOT en los servidores que no permitan su uso. De hecho, es una buena práctica usar env() en lugar de $_SERVER o getenv() (sobretodo si pensamos distribuir el código), ya que ofrece la misma funcionalidad y es totalmente compatible.

fileExistsInPath

fileExistsInPath(string $archivo)

Comprueba que el fichero $archivo está en el include_path actual de PHP. Devuelve un valor booleano.

h

h(string $texto, string $charset)

Alias de la función htmlspecialchars().

pr

pr(mixed $var)

Alias de la función print_r(), añadiendo la etiqueta <pre> a la salida.

stripslashes_deep

stripslashes_deep(array $valor)

Elimina recursivamente las barras invertidas de $valor. Devuelve el array modificado.

Traducción en cakePHP

CakePHP usa los archivos ‘po’ para manejar las traducciones. Para poder realizar las traducciones es conveniente usar un editor:

https://poedit.net/download

Lo primero que debemos hacer es obtener las cadenas de texto susceptibles de ser traducidas. Nos colocamos en la carpeta ‘app’ de nuestra aplicación y ejecutamos lo siguiente:

Console/cake i18n extract

Nos hace una serie de preguntas a las que podemos contestar con ‘intro’ escogiendo siempre la opción por defecto. En Locale nos va a generar varios archivos ‘.pot’ con las cadenas de las traducciones. A nosotros nos interesa ‘default.pot’.

En el poedit generamos un archivo nuevo, le llamaremos ‘default.po’. Añadimos las cadenas desde el default.pot y las traducimos. El archivo resultante lo podemos guardar en:

\app\Locale\eng\LC_MESSAGES\default.po

O bien en la carpeta local correspondiente, por ejemplo:

/app/Locale/esp/LC_MESSAGES/default.po

Para indicarle que estamos usando otra traducción debemos indicarlo con la siguiente línea en \app\Config\core.php:

Configure::write('Config.language', 'esp');