Archivo de Marzo, 2009

Imagen en un link – CakePHP

Gracias al helper Html y Form de cake, tenemos la posibilidad de usar la mayorias de los tags de HTML, la etiqueta ancla, img, etc, al desarrollar una aplicación algunas veces necesitamos que nuestras anclas no tengan un texto y en su caso utilizar una imagen, por ejemplo:

<a href="#"><img src="imagen.jpg" alt="" /></a>

En el caso de cake es similar solo es necesario utilizar la propiedad ‘escape’ => false, como se muestra en el siguiente ejemplo:

<?php echo $html->link($html->img('imagen.jpg'), array('controller' => 'controlador', 'action' => 'accion'), array('escape' => false))?>

La propiedad ‘escape’ => false tambien es necesaria cuando queremos mostrar acentos, por ejemplo:

<?php echo $html->link('Aplicaci&oacute;n', array(), array('escape' => false))?>
 
//devuelve Aplicación
 
<?php echo $html->link('Aplicaci&oacute;n', array())?>
 
//devuelve Aplicaci&oacute;n

Integridad de datos en CakePHP

La integirdad de datos en cualquier sistema es algo fundamental y debe tomarse en cuenta muy seriamente.

Trabajando en un sistema Help Desk (que despues les compartire), existen algunas relaciones por ejemplo un ticket pertenece (belongsTo) a un departamento, como puede ser el departamento de Soporte Tecnico, etc, y un departamento tiene muchos (hasMany) tickets, pero ¿que sucede cuando eliminamos algun departamento y este tiene tickets relacionados?, se corrompe la integridad de los datos ¿verdad?.

Para esto cakePHP cuenta con una propiedad en las asociaciones de modelos, dependent, veamos el ejemplo:

<?php
 
class Departamento extends AppModel
{
	var $hasMany = array(
	'Ticket' => array(
		'dependent' => true
	));
}
 
?>

Utilizando la propiedad ‘dependent’ => true estamos diciendo que es un modelo dependiente, esto quiere decir que al momento de eliminar algun departamento, automáticamente eliminara los tickets que tengan asignado ese departamento, y mantendremos la integridad de datos intacta.

RSS + CakePHP

Una aplicación que tenga RSS (Really Simple Syndication) ofrece mas posibilidades ya que no limita a las personas a ver el contenido desde la página web, puede ser desde un dispositivo móvil o un lector RSS eje. Google Reader.

CakePHP cuenta con un helper que nos facilita la creacion de contenido RSS, y consiste en 4 sencillos pasos y siguiendo el ejemplo del blog que ya hemos creado anteriormente http://cakephp1.com/2009/03/20/desarrollando-un-sencillo-blog-con-cakephp/.

Acitvar las extensiones RSS

Es en nuestro archivo /app/config/routes.php debemos agregar una línea para que cakePHP pueda parsear la extension en este caso .rss.

Router::parseExtensions('rss');

routes.php quedaría de la siguiente manera:

<?php
/* SVN FILE: $Id: routes.php 7945 2008-12-19 02:16:01Z gwoo $ */
/**
 * Short description for file.
 *
 * In this file, you set up routes to your controllers and their actions.
 * Routes are very important mechanism that allows you to freely connect
 * different urls to chosen controllers and their actions (functions).
 *
 * PHP versions 4 and 5
 *
 * CakePHP(tm) :  Rapid Development Framework (http://www.cakephp.org)
 * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @filesource
 * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
 * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
 * @package       cake
 * @subpackage    cake.app.config
 * @since         CakePHP(tm) v 0.2.9
 * @version       $Revision: 7945 $
 * @modifiedby    $LastChangedBy: gwoo $
 * @lastmodified  $Date: 2008-12-18 18:16:01 -0800 (Thu, 18 Dec 2008) $
 * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
 */
/**
 * Here, we are connecting '/' (base path) to controller called 'Pages',
 * its action called 'display', and we pass a param to select the view file
 * to use (in this case, /app/views/pages/home.ctp)...
 */
	Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
/**
 * ...and connect the rest of 'Pages' controller's urls.
 */
	Router::connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
	Router::parseExtensions('rss');
?>

Crear el layout RSS

El siguiente paso va ser crear un layout para los archivos .rss en /app/views/layouts/rss/ creamos un archivo default.ctp el resultado final seria /app/views/layouts/rss/default.ctp lo abrimos y añadimos el siguiente código:

<?php
echo $rss->header();
if (!isset($documentData)) {
	$documentData = array();
}
if (!isset($channelData)) {
	$channelData = array();
}
if (!isset($channelData['title'])) {
	$channelData['title'] = $title_for_layout;
} 
$channel = $rss->channel(array(), $channelData, $content_for_layout);
echo $rss->document($documentData,$channel);
?>

Es un layout predeterminado y con esto solamente va ser necesario enviar nuestra información a sindicalizar.

RequestHandler

En /app/controllers/posts_controller.php es necesario usar el componente RequestHandler (es necesario porque sin este la aplicación no sabria como manipular nuestro .rss)

var $components = array('RequestHandler');

posts_controller.php completo:

<?php
 
// app/controllers/posts_controller.php
App::import('Core', 'l10n');
class PostsController extends AppController
{
	var $name = 'Posts';
	var $components = array('RequestHandler');
 
	function beforeRender() {
 
		$this->L10n = new L10n();
		$this->L10n->get("eng"); //puede ser esp, fra, etc.
	}
 
	function index() {
 
		$posts = $this->Post->find('all');
		$this->set('posts',$posts);
	}
 
	function agregar() {
 
		if(!empty($this->data)) {
 
			if($this->Post->save($this->data)) {
 
				$this->Session->setFlash(__('El post se guardo correctamente', true));
				$this->redirect(array('action' => 'index'));
			}
		}
	}
 
	function editar($id = null) {
 
		if(!empty($this->data)) {
 
			if($this->Post->save($this->data)) {
 
				$this->Session->setFlash(__('El post se modifico correctamente', true));
				$this->redirect(array('action' => 'index'));
			}
		}
 
		$this->Post->id = $id;
		$this->data = $this->Post->read();
	}
 
	function eliminar($id = null) {
 
		if($this->Post->del($id)) {
 
			$this->Session->setFlash(__('El post se elimino correctamente', true));
			$this->redirect(array('action' => 'index'));
		}
	}
}
?>

La vista

El siguiente paso va ser crear una carpeta y archivo en /app/views/posts/ la carpeta con el nombre de rss y el archivo se llamara index.ctp obteniendo como resultado final /app/views/posts/rss/index.ctp.

Como el archivo lo nombramos index.ctp va ejecutar la funcion index() en el controlador PostsController, ahora en la vista solo añadimos lo siguiente:

<?php
$this->set('documentData', array(
'xmlns:dc' => 'http://purl.org/dc/elements/1.1/'));
 
$this->set('channelData', array(
'title' => __("Entradas", true),
'link' => $html->url('/', true),
'description' => __("Entradas", true),
'language' => 'es-mx'));
 
foreach($posts as $post):
 
	echo $rss->item(array(), array(
	'title' => $post['Post']['titulo'],
	'link' => array('controller' => 'posts', 'action' => 'editar',$post['Post']['id']),
	'description' => $post['Post']['contenido'],
	'pubDate' => $post['Post']['created']
	));
 
endforeach;
?>

Añadimos un titulo, descripcion, lenguaje, etc, despues solo recorremos el arreglo que tenemos disponible $posts gracias a nuestro metodo index() que esta definido en PostsController.

Resultado final: http://cakephp1.com/blog/posts/index.rss

Localización en cakePHP

Uno de los factores para que una aplicacion tenga éxito es en cuantos lenguajes se encuentra disponible, solo hechemos un vistazo a facebook (ya esta disponible en mas de 50 lenguajes), google, entre muchas más.

CakePHP cuenta con una estructura que nos permite internacionalizar nuestra aplicación, solo es necesario agregar los lenguajes que utilizara nuestro desarrollo, tomando el ejemplo de nuestro blog http://cakephp1.com/2009/03/20/desarrollando-un-sencillo-blog-con-cakephp/, vamos a traducir algunos valores a ingles.

El primer paso a seguir es agregar una carpeta dependiendo del idioma que vamos a utilizar (eng, esp, fra, etc.) en /app/locale/ quedaria de la siguiente manera /app/locale/eng/LC_MESSAGES/, cada carpeta de debe tener un archivo default.po que tiene la traduccion, el resultado final seria /app/locale/eng/LC_MESSAGES/default.po.

Para traducir algun texto solo hay que escribir el msgid y msgstr, veamos.

msgid "El post se guardo correctamente"
msgstr "Post saved"
 
msgid "El post se modifico correctamente"
msgstr "Post modified"
 
msgid "El post se elimino correctamente"
msgstr "Post deleted"
 
msgid "Entradas"
msgstr "Posts"
 
msgid "Agregar post"
msgstr "Add post"

En este caso no voy a traducir toda la aplicación, ya que solo es para cuestiones informativas. Algunos se estaran preguntando ¿pero de donde sale msgid?.

El siguiente paso va ser utiliza el metodo __() de cake, ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
/app/controllers/posts_controller.php
function agregar() {
 
	if(!empty($this->data)) {
 
		if($this->Post->save($this->data)) {
 
			$this->Session->setFlash(__('El post se guardo correctamente', true));
			$this->redirect(array('action' => 'index'));
		}
	}
}

Hechemos un vistazo a la linea 8 estamos utilizando el metodo __() y como segundo parametro le enviamos true para que no devuelva salida de echo. Utilizando este metodo ya es posible utilizar la internacionalizacion de cake, en este caso msgid seria El post se guardo correctamente’

¿Como seleccionar que lenguaje se va utilizar?
Primero necesitamos importar la libreria l10n del core de cake.

App::import('Core', 'l10n');

El siguiente paso va ser seleccionar el lenguaje que vamos a utilizar con la funcion beforeRender() en el controlador

function beforeRender() {
 
	$this->L10n = new L10n();
	$this->L10n->get("eng"); //puede ser esp, fra, etc.
}

Aqui seleccionamos el idioma a utilizar en este caso eng que seria ingles.

PostsController completo:

<?php
 
// app/controllers/posts_controller.php
App::import('Core', 'l10n');
class PostsController extends AppController
{
	var $name = 'Posts';
 
	function beforeRender() {
 
		$this->L10n = new L10n();
		$this->L10n->get("eng"); //puede ser esp, fra, etc.
	}
 
	function index() {
 
		$posts = $this->Post->find('all');
		$this->set('posts',$posts);
	}
 
	function agregar() {
 
		if(!empty($this->data)) {
 
			if($this->Post->save($this->data)) {
 
				$this->Session->setFlash(__('El post se guardo correctamente', true));
				$this->redirect(array('action' => 'index'));
			}
		}
	}
 
	function editar($id = null) {
 
		if(!empty($this->data)) {
 
			if($this->Post->save($this->data)) {
 
				$this->Session->setFlash(__('El post se modifico correctamente', true));
				$this->redirect(array('action' => 'index'));
			}
		}
 
		$this->Post->id = $id;
		$this->data = $this->Post->read();
	}
 
	function eliminar($id = null) {
 
		if($this->Post->del($id)) {
 
			$this->Session->setFlash(__('El post se elimino correctamente', true));
			$this->redirect(array('action' => 'index'));
		}
	}
}
 
?>

Archivo completo con más traducciones y utilizando el metodo __(), como se daran cuenta msgid es el primer parametro.

El msgid “Entradas” en nuestro archivo /app/locale/eng/LC_MESSAGES/default.po corresponde al archivo de la vista /app/views/posts/index.ctp

<h1><?__('Entradas')?></h1>

Como debe tener salida de echo no le pasamos el segundo parametro como en los ejemplos anteriores.

Resultado final http://cakephp1.com/blog/posts, como lo mencione anteriormente no esta traducida toda la aplicación ya que solo es para un fin de ejemplo.