miércoles, 29 de octubre de 2014

Ejemplos de Triggers en MySQL

En esta entrada voy a poner ejemplos prácticos de casos que podemos resolver con triggers en MySQL. Los triggers son pequeños programas que ejecuta la base de datos respondiendo a acciones sobre la misma.  En este caso no me voy a enrollar explicando ni teoría, ni código... voy a partir directamente de los ejemplos que son autoexplicativos.

Para verlo partiremos de una base de datos con la siguiente estructura:


Qué tenemos aquí?

Pues es bien sencillo, tenemos una base de datos de una filmoteca personal, en la que podemos asociar cada vídeo a una tabla de actores (varios actores por vídeo) y a las categorías a las que pertenece la peli (que también pueden ser más de una, p.ej. animación / terror). Además tanto actores como categorías tienen un contador que nos indica el número vídeos que tienen relacionados.

Borrado en cascada

Imaginamos que borramos un vídeo. Deberíamos también borrar las tablas de relaciones con actores y categorías. Esto lo podríamos hacer por código desde luego, pero lo podemos poner también como un trigger de la tabla video. Esto tiene la ventaja de que reduce accesos al servidor. Además la Base de Datos trabaja más rápido ya que se entiende consigo misma. Si además borramos vídeos desde el gestor de base de datos phpMyAdmin, ya no tendremos que preocuparnos por las relaciones.

Esto sería así:

DROP TRIGGER IF EXISTS `deleteVideo`;
DELIMITER //
CREATE TRIGGER `deleteVideo` AFTER DELETE ON `Video`
 FOR EACH ROW begin
DELETE FROM relVideoCategory WHERE relVideoCategory.idVideo = old.idVideo;
        DELETE FROM relVideoActor WHERE relVideoActor.idVideo = old.idVideo;
END
//
DELIMITER ;

Actualización de un registro

Cada vez que relacionamos un vídeo con un actor, creamos una entrada en RelVideoActor. Podemos utilizar este hecho para que un nuevo registro en RelVideoActor, aumente el contador de actores. Y es más podemos hacer que una baja de ese registro disminuya el contador. 

delimiter |
CREATE TRIGGER inserRelVideoCategory AFTER INSERT ON relVideoCategory
  FOR EACH ROW
  BEGIN
     UPDATE categories SET categoryCount = categoryCount + 1 WHERE idCategory = NEW.idCategory;
  END;
|

delimiter ;


delimiter |
CREATE TRIGGER deleteRelVideoCategory AFTER DELETE ON relVideoCategory
  FOR EACH ROW
  BEGIN
     UPDATE categories SET categoryCount = categoryCount - 1 WHERE idCategory = OLD.idCategory;
  END;
|

delimiter ;

Autodestrucción

Quizá querríamos también mantener nuestra tabla de actores de manera dinámica. Es decir, los actores sólo permanecerían en la tabla mientras hubiera películas en las que aparecieran. Si su contador llegase a cero, el actor, ejecutaría su mejor papel suicidándose.

Pues bien... podría poneros aquí el código y demás y no daría error... pero al ejecutarlo nos diría que nones, que lo del suicidio no está bien visto por los ojos del señor, ya que no podemos borrar un registro de la misma tabla que dispara el trigger.

Así que deberemos acudir al trigger que elimina la relación y decrementa el contador y contratar ahí a nuestro sicario:

delimiter |
CREATE TRIGGER deleteRelVideoActor AFTER DELETE ON relVideoActor
  FOR EACH ROW
  BEGIN
     UPDATE actor SET actorCount = actorCount - 1 WHERE idActor = OLD.idActor;
     DELETE FROM actor WHERE idActor = OLD.idActor AND actorCount = 0;
  END;
|

delimiter ;

Disable Triggers

Otro caso... imaginemos que queremos borrar categorías, pero ya tenemos vídeos asignados a las mismas.  Podemos  (y no es propaganda del coletas):
  • Comprobar si hay vídeos asignados a la categoría, y si ese es el caso impedir la baja.
  • Borrar la categoría y también todos les registros de RelVideoCategory que la contengan. (que es lo que vamos a hacer)
Relamente no hace falta que ponga el código, sería como en el primer ejemplo de este artículo, el borrado en cascada aunque no funcionaría... ¿y por qué?

Pensad en lo que haríamos:
  1. Borraríamos la categoría cosa que dispararía el borrado de RelVideoCategory
  2. Borraríamos RelVideoCategory cosa que dispararía el borrado de categorías...    :-)
Es decir, la cosa está en que deberíamos impedir que al borrarse RelVideoCategory se disparara su trigger... y para eso necesitamos un comando disable triggers que MySQL no tiene...  :-)

Resolveremos el trigger con el siguiente trick:

DELIMITER ;;
CREATE TRIGGER deleteCategory AFTER DELETE ON category
  FOR EACH ROW begin
        SET @disable_triggers = 1;
  DELETE FROM relVideoCat WHERE relVideoCat.idCategory = old.idCategory;
                SET @disable_triggers = NULL;
END ;;
        
DELIMITER ;

DELIMITER ;;
CREATE TRIGGER deleteRelVideoCat AFTER DELETE ON relVideoCategory
       FOR EACH ROW
        BEGIN
             IF @disable_triggers IS NULL THEN
        UPDATE category SET categoryCount = categoryCount - 1 WHERE idCategory = OLD.idCategory;
             end if;
        END ;;
DELIMITER ;




Y hasta aquí estos pequeños apuntes sobre disparadores. Como siempre esperando a que a alguien le sea útil.

Os dejo que tengo prácticas de tiro.

domingo, 5 de octubre de 2014

Codeigniter Multilenguaje

Codeigniter soporta por sí mismo el multilenguaje, sin embargo tiene un problema en cuanto a los motores de búsqueda, y es que sólo indexarán vuestra página en el lenguaje por defecto. 

Para que indexen en cada uno de los lenguajes que tengamos disponibles, deberíamos crear la típica estructura url amigable que estáis hartos de ver navegando por ahí:

Ej:

http://mytube.honor.es/es/login    ==>  'Español'
http://mytube.honor.es/en/login   ==>  'Inglés'
http://mytube.honor.es/ca/login   ==>  'Catalán'


Internationalization (i18n) for CodeIgniter 2.x

Esta utilidad que reside aquí, permite hacer exactamente esto. Podéis seguir las instrucciones en inglés o bien descargaros los archivos ya creados aquí y seguir leyendo en castellano cómo configurarlo. 

Los programas

Si no lo habéis hecho en el párrafo anterior, nos descargamos los programas de aquí.

Hay tres archivos:

MY_Lang.php
MY_Config.php
MY_language_helper.php   (Este programa falta en muchos paquetes que os podáis descargar por ahí)

Copiaremos los dos primeros en:

./application/core

y el helper en:

./application/helpers


No obstante, deberemos realizar una pequeña modificación en el fichero MY_Lang.php que consistirá en indicar en qué carpeta alojaremos cada lengua.


Siguiendo el ejemplo de las URL amigables al principio del artículo la modificación a realizar justo al principio del archivo, sería:

// languages

var $languages = array(

'en' => 'en',
'ca' => 'ca',
'es' => 'es'
);


Es importante reseñar que las abreviaciones de las lenguas no son triviales y corresponden a una normativa ISO, así que busca primero aquí cómo se representan las lenguas que vayas a usar.

Quitar el index.php

Si os fijáis, en las estructuras de ejemplo que hemos puesto arriba no aparece el index.php por ninguna parte, cosa que favorece la indexación en los motores de búsqueda. Hay mucha información en la web sobre esto y no voy a profundizar al no ser el cometido de este artículo. Yo lo he resuelto creando un archivo de nombre .htaccess en la raíz de mi site.

<Ifmodule mod_rewrite.c> 
     RewriteEngine On 
     RewriteCond %{REQUEST_FILENAME} !-f 
     RewriteCond %{REQUEST_FILENAME} !-d 
     RewriteRule ^(.*)$ /index.php/$1 [L] 
</IfModule>



<Ifmodule mod_rewrite.c> 

    ErrorDocument 404 /index.php
</IfModule>

Modificar archivos de configuración

Por una parte debemos realizar una modificación en el archivo config.php ubicado en la carpeta ./application/config consistente en precisamente eliminar el archivo index.php.

$config['index_page'] = '';

Por otro lado debemos modificar el archivo routes.php también ubicado en la misma carpeta, añadiendo a lo que tengamos las líneas siguientes con el fin de redireccionar la carpeta correspondiente. Fijaos que se refieren a los lenguajes que vamos a usar y seguimos utilizando la normativa ISO.


// URLs como '/es/login' -> usarán el controlador 'login'.

$route['^(es|ca|en)/(.+)$'] = "$2";



// '/es', '/ca' y '/en' URLs -> usarán el controlador por defecto.

$route['^(es|ca|en)$'] = $route['default_controller'];

Crear los archivos de lenguas

A partir de ahora debemos pensar en que ya no volveremos a usar textos en nuestras views, deberemos referenciar en su lugar una variable única para cada uno de los textos y crear un fichero  de traducción para cada lengua.

Por lo tanto, primero creamos la estructura de carpetas que vamos a usar dentro de ./application/language. 

En el ejemplo de las tres lenguas que usamos en el artículo, deberíamos crear:

./application/language/es/
./application/language/en/
./application/language/ca/

Según las características de nuestra aplicación nos puede convenir más crear un archivo de traducciones para cada controlador, uno general o hacer una cosa mixta. En el ejemplo crearemos uno general al que llamaremos global_lang.php. El nombre que le demos es indiferente con la única excepción de que debe terminar con el sufijo _lang. Este fichero lo debemos copiar en todas las carpetas de language.

./application/language/es/global_lang.php
./application/language/en/global_lang.php
./application/language/ca/global_lang.php

Este fichero deberá contener una entrada por variable con la siguiente estructura:

$lang['identificador_texto']='Texto_en_el_idioma_correspondiente';

Volviendo a nuestro ejemplo:

./application/language/es/global_lang.php

<?php
$lang['usuario']='Usuario';

$lang['registro']='Regístrate gratis';

?>

./application/language/en/global_lang.php


<?php
$lang['usuario']='User';
$lang['registro']='Sign up for free';
?>


./application/language/ca/global_lang.php


<?php
$lang['usuario']='Usari';
$lang['registro']='Registre gratuït';
?>



Creación del controlador

Sólo necesitaremos asegurarnos de llamar al helper language y cargar el formato del fichero antes de llamar al view. Esto sería un ejemplo para un posible login, usando nuestro fichero global_lang.php anterior:

function index()
 {

  $this->load->helper('language');
  $this->load->helper('url');

  // Carga el fichero de idioma. ATENCIÓN!!!  NO PONEMOS EL SUFIJO _lang!!!
  $this->lang->load('global');

  $this->load->view('login/login_view');

 }


Uso en las vistas

Es muy sencillo. Allí donde pondríamos el texto de nuestro ejemplo 'Regístrate Gratis', pondremos en su lugar:

<?php echo lang('registro'); ?>

Fijaos que es el nombre de la variable definida en el fichero global_lang.php


Remate final

Con todo lo que hemos visto ya funcionará el multilenguaje sólo!!!!

Pero como apunte final y colofón, dos utilidades que nos pueden ser (y lo serán) útiles.

Para saber en algún momento en qué lenguaje estamos:

$lenguaje = $this->lang->lang()

Esto lo podemos usar, por ejempo, para definir la lengua en nuestra vista:

<html lang="<?php echo $this->lang->lang();?>">

Otra utilidad interesante a tener en cuenta es el enlace necesario, para cambiar de lengua. Por ejemplo en una banderita con la bandera de la pérfida albión pondríamos:

anchor($this->lang->switch_uri('en'),'Muestra la página actual en inglés');




miércoles, 3 de septiembre de 2014

Estructura de computadors (05.573) - UOC

Actualizado Diciembre 2016

Para facilitaros el inicio del curso, esta vez os comparto mis apuntes de la asignatura Estructura de computadors de la UOC (Universitat Oberta de Catalunya), con el código de asignatura 05.573.

Además de los apuntes os adjunto los ejercicios, PAC'S y exámenes realizados durante el curso, así como exámenes de años anteriores.

A diferencia de otras asignaturas, no tenemos ejemplos de PAC's de años anteriores y no nos facilitan exámenes de muestra hasta finalizar el curso. Así que os comparto lo que tengo. Además, si queréis colaborar y mandarme vuestros materiales del año que estéis cursando al correo furnimanQUITAESTO@terra.com los incluiré y podrán ser de ayuda para todo el mundo.

Mis apuntes en Word por si los queréis modificar y en PDF para compatibilidad en distintas plataformas:

Apuntes en formato PDF

Si no os gustan mis apuntes, aquí adjunto otros de gente que ha cursado la asignatura y que he encontrado por la red

Apuntes de terceros

Os adjunto también un documento que sirve para traducir código en lenguaje C a CISCA. Supongo que lo distribuirán todos los años, pero aquí queda por si las moscas:


Por otro lado, el curso consta de una serie de ejercicios teóricos de los que no tenemos ningún ejemplo de años anteriores, espero que con esto podáis paliar el sufrimiento que tuve...  :-)


Lo mismo pasa con las PAC's que las encararemos 'a pelo'. Os adjunto las de años anteriores para que las uséis como ayuda.

Las prácticas creo que no aportan mucho dado los ejemplos que nos darán a lo largo del curso, pero por si alguien las quiere pongo las de varios años:

Prácticas

Y por último os adjunto soluciones de exámenes desde el 2012 al 2016.


Espero que os sea todo muy útil. Nos seguimos leyendo.

lunes, 12 de mayo de 2014

Apunts d'Enginyeria del Programari (UOC)

(Actualizado Enero 2017!!!)

Termina un nuevo curso y llegan los exámenes. Como va siendo habitual, os comparto mis apuntes para que hagáis con ellos los que os plazca. En esta ocasión comparto los apuntes de la asignatura Enginyeria del Programari (05.565) del Grau d'Informàtica de la UOC (Universitat Oberta de Catalunya).

Esta vez he encontrado mis apuntes en la red traducidos al castellano así que os comparto los apuntes en formato Word para que los podáis modificar y también en PDF para que puedan llegar a todo el mundo. Lo único que pido es que si los redistribuis mantengáis los enlaces a mi blog.


También un interesante documento de casos de uso, que aunque no sea de la UOC os puede ayudar un montón:

Descargar Casos de Uso

Y por supuesto una buena colección de PAC's, prácticas y exámenes.

Exámenes  

Podéis hacerme llegar vuestros exámenes y PAC's al correo furnimanQUITAESTO@terra.com obviamente quitando la parte en mayúscula de la dirección de correo.

Y nada más...  después de exámenes colgaré los apuntes de este año que se me está haciendo bastante cuesta arriba, la verdad...



lunes, 24 de marzo de 2014

Maggi Jugoso a la Sartén

Un artículo del Dr. Amor.

Sí, sí... ya se que éste es un blog relacionado con la informática. Entonces, ¿Qué pinta Maggi Jugoso a la Sartén en todo esto? Déjad que el Dr. Amor os de unos consejos:

Conozco a informáticos que son unas fieras en todo lo que hacen. Aprenden cualquier lenguaje en segundos, salen de marcha todos los días, duermen, son bien parecidos y además cocinan bien... si eres uno de estos odiosos personajes sin duda mi artículo no es para tí...

Ahora bien, si perteneces al grupo que necesita dedicar todo su tiempo a ésto, que no pueden salir y ni siquiera tiene tiempo ni ganas para cocinar... estos consejos podrían servirte...

Ya se que sobrevivís perfectamente. Que coméis todos los días y que unas salchichas de frankfurt no son misterio para vosotros... el problema es qué hacer cuando un día, una fémina, convencida vete a saber tú con qué taimadas argucias, accede a venir a vuestra casa y claro... debéis preparar algo de comer...

Desde luego el arroz tres delicias de congelados 'La Sirena' y los San Jacobos son una buena solución siempre que no os de por echar el arroz a hervir (creedme... el Dr. Amor sabe de lo que habla), pero esa chica realmente os gusta y la queréis impresionar...

Podéis seguir tirando del arroz si queréis, pero un San Jacobo ya es jugársela mucho, es ahí donde Maggi como rezaba su slogan nos quiere ayudar...

Para eso antes de la cita deberemos ir a comprar unas pechuguitas de pollo. Id a la pollería, no os arriesguéis. Además hay que promover el comercio de barrio. Pedidle al / la poller@ que os filetee la pechuga con filetes de centímetro y medio de grosor.  Después id a vuestra tienda de ultramarinos favorita y comprad un  Maggi Jugoso a la Sartén a las finas hierbas o al ajillo. (Si prevees que tienes posibilidades con la chica, el Dr. Amor te aconseja las finas hierbas.)

No os equivoquéis... esto es lo que buscamos:



Ahora sólo queda esperar a la hora de la comida o cena, abrir una botellita de vino (aunque el Dr. Amor  siempre recomienda el cava) y mientras charláis y tomáis una copa, vas preparando la comida o cena... (por favor, nada de velas ni ñoñerías de esas... puede pensar que buscáis algo más que amistad... ¬¬ )

Hacéis vuestro primero, y luego preparáis las pechugas... para ello...

a) Nos lavaremos las manos. (Las chicas aprecian estos detalles)

b) Pondremos a calentar la sartén a media potencia. Es fácil, la rosca tiene unos numeritos... cogéis el mayor y lo dividís entre dos.. esa será nuestra potencia y NO LA MOVEREMOS! 

c) Abríremos el sobre de Maggi jugoso a la sartén y cogeremos uno de los papyrus y lo extenderemos sobre la mesa que previamente habremos limpiado de migas y restos de café.

d) Colocaremos uno de nuestros filetes en medio y doblaremos el papel. Presionaremos para que el pollo quede bien impregnado.

En este punto, si no hemos sido previsores y nuestra pechuga está fileteada más fina, superpondremos varios filetes hasta alcanzar el grosor de un centímetro y medio. Como cocinamos sin aceite usando la grasa del pollo, éste se unirá consigo mismo creando una única pechuga!!!!


e) Repetiremos el paso d) con el otro filete.


f) Dejaremos que la sartén se caliente un poco más (que no humee ni se funda el mango, por favor) y colocaremos sobre ella nuestros dos filetes. Miraremos de reojo la hora... (eso ella no lo debe notar, tenéis que aparentar un control total sobre los fogones)

g) Pasados seis minutos giraréis los filetes. Sin miedo. Con la mano. Si lo hacéis rápido y seguros no os quemaréis y ella os empezará a mirar de otra manera. 

h) Preparad los platos y en seis minutos más sacáis los filetes y los colocáis sobre el plato retirando el papel. 

Mirad que fácil:


Mientras hacéis la pechugas, si sois capaces, podéis haber ido preparando una ensalada para acompañar... una 'florette' bien aliñada vale... creed al Dr. Amor.  Quedaréis ante ella como un hombre pragmático, bien provisto pero que improvisa sin complicarse en la cocina...  y en la vida.

Cuando pruebe vuestras pechugas verá que son deliciosas, jugosas y muy sanas!!!... No daréis crédito de que habéis sido los artífices!!!... 

De postre un heladito. Vainilla. Nada sofisticado. Y bueno... si no eructáis durante la comida y os habéis acordado de apagar el fuego... el Dr. Amor os garantiza una sobremesa que no vais a olvidar.

Feliz Maggi Jugoso a la Sartén a todos.




viernes, 21 de febrero de 2014

CraftyJS - Viewport (II) - Trucos de cámara

Como no podía ser de otra manera, e al igual que cuando me estaba ocupando de la animación, sale una nueva versión de CraftyJS y me lo cambian todo obligándome a reescribir el artículo por completo.

Por lo tanto, todo lo aquí escrito, sólo tiene validez a partir de la versión 0.6.2 de CraftyJS.

Continuamos el artículo del viewport donde lo dejamos viendo otras funcionalidades:

Viewport.pan

Si lo último que vimos fue cómo hacer que la cámara siguiera a nuestra entidad, ahora lo que haremos es desplazar la cámara por sí misma en un movimiento PANorámico.

La instrucción es bien simple:

Crafty.viewport.pan(pixeles_x, pixeles_y, tiempo_en_ms);

Es decir, que le indicamos al viewport cuantos píxeles queremos desplazar la cámara sobre cada eje y el tiempo expresado en milisegundos en que queremos que la cámara llegue a destino.

Se puede ver un ejemplo funcionando de esto aquí.  

Viewport.pan con una vuelta de rosca

Ahora le vamos a dar una vuelta de rosca a este tema, concretamente una vuelta de rosca de ratón. Haremos que nuestra cámara se mueva con lo que los albiones llaman el 'mouse wheel' y nosotros la ruedecilla del ratón.



Nosotros podemos interceptar eventos en CraftyJS con la función .bind que tiene la siguiente sintaxis:

Crafty.bind(evento, acción)

O sea, que podemos hacer lo siguiente:

Crafty.bind("MouseWheel", function(e) {
    var delta = (e.wheelDelta ? e.wheelDelta/120 : e.detail);
   Crafty.viewport.pan(-delta*30, -delta*20, 100);
    });

¿Qué hace esto?

Está claro que delta es un modificador que aplicaremos al desplazamiento del viewport, básicamente para que suba o baje en función del sentido en el que movamos la ruedecilla.

Bien, pues cuando interceptamos el evento de la rueda, éste tiene una propiedad llamada detail con valor +/- 1 si usamos Firefox u Opera o una propiedad wheelDelta con valor +/- 120 si usamos otro navegador.

Es por eso que preguntamos por la propiedad y la dividimos por 120 o la dejamos tal cual, con el objeto de acabar teniendo un +1 si la rueda sube y un -1 si la rueda baja.

Finalmente, dado que en Crafty el eje y incrementa de arriba hacia abajo, le cambiamos el signo para lograr el efecto deseado.

Podéis ver esto y un bizcocho aquí.

Zoom!

Otro efecto que podemos hacer con viewport es un zoom. El zoom además del uso que nos viene a la cabeza de forma intuitiva, puede ser una buena manera de adaptar nuestro juego al tamaño de la pantalla sobre el que se ejecute sin necesidad de redimensionar nada.

Crafty.viewport.scale

Sintaxis:

Crafty.viewport.scale(aumento)

El parámetro aumento es el multiplicador del zoom. Es decir, con un aumento 2, algo que mide 10 píxeles pasará a medir 20. Si ponemos un aumento de 0.5 (1/2), algo que mide 10 píxeles pasará a medir la mitad.

Si pasamos 0 o 1 a la función, todo adquirirá el tamaño normal.

Crafty.viewport.zoom

Sintaxis:

Crafty.viewport.zoom(aumento, pos_x, pos_y, tiempo)

Hace el zoom sobre un punto (x,y) en el tiempo que queramos (expresado en milisegundos). De todas formas aquí debemos tener en cuenta que el aumento es relativo. Es decir, al contrario que con scale que redimensiona al tamaño que queramos, zoom redimensiona sobre la última redimensión. Esto es especialmente importante si hacemos zoom de manera progresiva, ya que el aumento / disminución del tamaño sucederá de manera exponencial.
Ni que decir tiene, que a todo esto le podemos añadir el rollo de la ruedecilla de antes y que nos quede algo asín.
Espero que hayáis disfrutado este artículo tanto como yo escribiéndolo y otros formalismos que se os ocurran. Nos vemos a la próxima.

sábado, 1 de febrero de 2014

Introducción a CakePHP (III): Ver el contenido de un registro

Si llegáis por primera vez a esta especie de minitutorial de aficionado, os aconsejo que empecéis por el principio por aquí. Vimos en el capítulo anterior cómo ver los datos de una tabla. Ahora lo que intentaremos es acceder al detalle de un registro... la verdad es que misterio poco... vamos a verlo...

Vamos a incluir esto en nuestro ArticuloController.

public function ver($id = null) {
$this->Articulo->id = $id;
$this->set('articulo', $this->Articulo->read());

}

Vemos que la acción ver recibe un parámetro que asignará al modelo Artículo.
Después mediante set y bajo las variable artículo pasamos a la vista ver (recordemos que tiene el mismo nombre que la acción), el resultado de Articulo->read().

read(), si lo pasamos sin parámetros, devuelve un array con el registro que hemos seleccionado.

Es decir, con la base de datos de ejemplo que creamos en el primer capítulo, si llamáramos a lo siguiente http://misitio/cake20/articulos/ver/2 nos devolverá:

Array
        (
            [Articulo] => Array
                (
                    [id] => 2
                    [title] => Un título otra vez
                    [body] => Y sigue el cuerpo del artículo.
                    [created] => 2014-01-27 23:36:46
                    [modified] => 
                )

        )

Por otro lado... ya sabemos del capítulo anterior, que realmente esto tal como está daría error. Debemos definir la vista.

En nuestra carpeta /app/View/Articulos, creamos el archivo ver.ctp:

<!-- File: /app/View/Articulos/ver.ctp -->

<h1><?php echo $articulo['Articulo']['title'];?> </h1>
<p><small>Creado: <?php echo $articulo['Articulo']['created'];?></small></p>
<p><?php echo $articulo['Articulo']['body'];?></p>

Ahora ya si hacemos:
http://misitio/cake20/articulos/ver/2


Podremos ver lo que hemos cometido:

Un título otra vez

Creado: 2014-01-30 17:28:48
Y sigue el cuerpo del artículo.

HtmlHelpers

Los helpers son clases que nos ayudan a completar acciones habituales como realizar un link, crear un formulario, formatear texto y números, utilizar Javascript y AJAX...

Cuando queramos generar html de manera dinámica, podemos hacerlo como siempre: con echos de las etiquetas o dejar que CakePHP lo genere por nosotros. Para ello usaremos el HtmlHelper.

¿Qué queremos hacer?  Pues convertiremos el título de nuestro artículo en un enlace que nos llevará a otra pantalla en la que veremos el detalle del registro. Hemos visto una líneas más arriba que podíamos llamar al detalle de un registro con esto:  http://misitio/cake20/articulos/ver/id. Lo que tenemos que hacer es entonces que el enlace se genere dinámicamente referenciando al registro sobre el que se posiciona.

Para acometer este reto usaremos HtmlHelper::link

El formato es el siguiente:

link(cadena a mostrar,  destino, array de opciones)

El array de opciones son los parámetros que de por sí tiene link en html, por ejemplo:
Mención a parte requiere el destino. Podemos como en el ejemplo de arriba poner la url absoluta o relativa o un routing array.


Routing array

No se si a estas alturas merece la pena entrar al detalle con esto... creo que se verá mejor con un ejemplo de uso...

Recordamos de unas líneas más arriba que accedíamos al detalle de un registro así:
http://misitio/cake20/articulos/ver/id

Como ya sabemos la peculiar estructura de carpetas y nomenclaturas en CakePHP nos indica en la URL anterior que articulos es el controlador, view la acción e id el parámetro que está esperando la acción. Esto en nuestro array sería así:

array('controller' => 'articulos', 'action' => 'ver', $articulo['Articulo']['id']));

Fijaos que en este caso hemos obviado el tercer parámetro.

index.ctp

Finalmente... sintetizamos esto en nuestro index.ctp (que recordamos mostraba la tabla)...

<!-- File: /app/View/Articulos/index.ctp -->

<h1>Artículos del Blog</h1>

<table>
    <tr>
        <th>Identificador</th>
        <th>Título</th>
        <th>Creado</th>
    </tr>

<?php foreach ($articulos as $articulo): ?>
    <tr>
        <td><?php echo $articulo['Articulo']['id']; ?></td>
        <td><?php echo $this->Html->link($articulo['Articulo']['title'],                       array('controller' => 'articulos', 'action' => 'ver', $articulo['Articulo']['id'])); ?></td>
        <td><?php echo $articulo['Articulo']['created']; ?></td>
    </tr>
<?php endforeach; ?>

</table> 


Ahora sí...
http://misitio/cake20/articulos

Artículos del Blog

IdentificadorTítuloCreado
1El título2014-01-27 23:36:46
2Un título otra vez2014-01-27 23:36:46
3El título vuelve2014-01-27 23:36:46

Y con esto y un bizcocher...

jueves, 30 de enero de 2014

Introducción a CakePHP (II): Mostrar datos en una tabla

Suponemos que tenemos CakePHP instalado con una base de datos en la que hemos dado de entrada unos registros de prueba con la tabla articulos como indica aquí.

Bien, veamos el contenido de nuestra tabla articulos:

http://misitio/cake20/articulos

Error: ArticulosController could not be found.

Claro, no hemos definido el controlador para nuestro modelo Articulo. De hecho ni siquiera hemos definido el modelo!  Aunque este paso lo ha hecho CakePHP por nosotros heredando de la clase AppController. ¿Una buena praxis? En absoluto.

Definiendo el controlador

En el capítulo anterior dijimos que los controladores se crean en la carpeta app/Controller de CakePHP. Así que crearemos el controlador con la acción que queremos darle: ver_tabla.

En app/Controller creamos el fichero ArticulosController.php, y le damos el siguiente contenido:

<?php
class ArticulosController extends AppController {
public function ver_tabla() {
$this->set('articulos', $this->Articulo->find('all'));
}
?>

Probemos:
http://misitio/cake20/articulos

Error: The action index is not defined in controller ArticulosController

Increíble. Parece que el modelo que aún no hemos definido y que lo ha hecho CakePHP por nosotros, está esperando la acción index. Para que no desespere cambiamos el nombre a nuestra acción.

<?php
class ArticulosController extends AppController {
public function index() {
$this->set('articulos', $this->Articulo->find('all'));
}
}
?>

¿Qué hace el método set?
Dijimos que para mostrar los datos CakePHP usa algo llamado vistas. Set pasa a la vista una variable (primer parámetro) y su valor (segundo parámetro). 

En este caso le pasa el resultado del método find('all') de nuestro modelo Articulo, que lo que hace es generar una tabla con el contenido de todos los registros de la tabla articulos. ¿Y dónde le hemos dicho que coja esa tabla y no otra? En ningún sitio. Es por eso que es tan importante atenerse a la convención en la nomenclatura de las cosas.  :-)

Veamos pues que sucede:
http://misitio/cake20/articulos

Error: The view for ArticulosController::index() was not found.


Maldición... olvidamos crear la vista. Además si os fijáis en el mensaje de error, lo que nos pide CakePHP es una vista para la acción index.

Dijimos que las vistas se colocaban en la carpeta /app/View al que nosotros añadiremos otro nivel de carpetas para tener todas las vistas de cada controlador separadas.  Eso significa que crearemos una vista llamada index.ctp en la carpeta /app/View/Articulos.

Comentamos que el método set del controlador pasaba el contenido de una variable a la vista, ¿no?. Pues bien como contenido de la vista pongamos lo siguiente:

<pre>
     <?php print_r($articulos); ?>
</pre>

Podemos ver cómo sobre la variable artículo habíamos recibido un array con la estructura y contenido de la tabla articulos de la base de datos:

Array
(
    [0] => Array
        (
            [Articulo] => Array
                (
                    [id] => 1
                    [title] => El título
                    [body] => Esto es el cuerpo del artículo.
                    [created] => 2014-01-27 23:36:46
                    [modified] => 
                )

        )

    [1] => Array
        (
            [Articulo] => Array
                (
                    [id] => 2
                    [title] => Un título otra vez
                    [body] => Y sigue el cuerpo del artículo.
                    [created] => 2014-01-27 23:36:46
                    [modified] => 
                )

        )

    [2] => Array
        (
            [Articulo] => Array
                (
                    [id] => 3
                    [title] => El título vuelve
                    [body] => Esto es realmente excitante! No.
                    [created] => 2014-01-27 23:36:46
                    [modified] => 
                )

        )

)

Para acabar... si en la vista recibimos la tabla... nada nos impide cambiar el contenido de la vista por esto:

<!-- File: /app/View/Articulos/index.ctp -->

<h1>Artículos del Blog</h1>

<table>
    <tr>
        <th>Identificador</th>
        <th>Título</th>
        <th>Creado</th>
    </tr>

<?php foreach ($articulos as $articulo): ?>
    <tr>
        <td><?php echo $articulo['Articulo']['id']; ?></td>
        <td><?php echo $articulo['Articulo']['title']; ?></td>
        <td><?php echo $articulo['Articulo']['created']; ?></td>
    </tr>
<?php endforeach; ?>

</table> 

Ahora sí...
http://misitio/cake20/articulos


Artículos del Blog

IdentificadorTítuloCreado
1El título2014-01-27 23:36:46
2Un título otra vez2014-01-27 23:36:46
3El título vuelve2014-01-27 23:36:46

Lo flipasssssssssss....