Estas en: Home > datos

Entradas etiquetadas con datos

Logo Cassandra

Cassandra y PHP para desarrolladores SQL: La consola

0

Cassandra dispone de una herramienta a través de la consola o shell para poder trabajar con ella. Parecida a la de MySQL.

Para acceder a la consola solo tendremos que escribir lo siguiente:

> cassandra-cli

Si todo va bien nos aparecerá algo parecido al siguiente texto:

Welcome to Cassandra CLI version 1.0.7

Type ‘help;’ or ‘?’ for help.
Type ‘quit;’ or ‘exit;’ to quit.

[default@unknown]

Ahora tenemos que conectar con la base de datos de la siguiente manera:

connect localhost/9160;

Fíjate que al final del comando hay un punto y coma. Hay que añadirlo, como si fuera una sentencia SQL, sino te aparecerán unos puntos suspensivos para que finalices el comando. Si te ocurre esto último, pon el punto y coma y pulsa enter. Te debería aparecer algo como lo siguiente:

Connected to: «Test Cluster» on localhost/9160

Hay una forma de resumir los pasos anteriores en una sola linea:

> cassandra-cli -h localhost -p 9160

Ahora vamos a mostrar los keyspaces que tiene la base de datos actualmente:

show keyspaces;

Recuerda poner el punto y coma.

Este comando te mostrará un listado de los keyspaces y sus column families que se encuentran en la BD, además de información relevante sobre las propiedades, tanto de los keyspaces como de sus column families correspondientes.

El objetivo de este post no es explicar con detalle cada propiedad de los keyspaces y las column families (he de admitir que muchas de ellas las desconozco), sino mostrar y enseñar la forma de trabajar por consola con Cassandra.

Vamos al lío.

 

CREAR UN KEYSPACE

Tan sencillo como la siguiente sentencia:

create keyspace my_keyspace;

Después de dos o tres segundos te mostrará algo como esto:

b511da50-88c0-11e1-0000-242d50cf1fff
Waiting for schema agreement…
… schemas agree across the cluster

 

ACCEDER A UN KEYSPACE

Igual de sencillo:

use my_keyspace;

Ahora que ya estamos dentro de nuestro keyspace, toca añadir column families.

 

CREAR COLUMN FAMILIES

Muy sencillo:

create column family my_column_family;

Añadamos datos.

 

AÑADIR DATOS A UNA COLUMN FAMILY

En MySQL utilizaríamos una sentencia INSERT para añadir información a una tabla concreta, en la que anteriormente habremos creado sus columnas. En Cassandra no hay que crearlas con antelación. (En realidad se puede utlizar un archivo para configurar el esquema del keyspace dando las propiedades adecuadas a cada column family y sus respectivas columnas, pero para iniciarnos en Cassandra y aprender su funcionamiento no es necesario).

Para no complicarnos mucho primero vamos a configurar la column family.

Las column families disponen de diferentes codificación de datos para guardar los datos, es decir, si guardamos información en UTF-8 y nuestra column family está configurada como ASCII o Bytes nuestros datos se guardarán, sí, pero al recuperarla solo veremos un batiburrillo de número y letras.

Para evitar esto configuramos la column family para que guarde los datos en UTF-8:

assume my_column_family keys as utf8;
assume my_column_family comparator as utf8;
assume my_column_family validator as utf8;

Ahora ya podemos crear nuestro primer registro:

set my_column_family[‘1’][‘nombre’] = ‘pepito’;
set my_column_family[‘1’][‘edad’] = ‘120’;

Si ejecutamos el siguiente comando:

get my_column_family[‘1’];

Nos devolverá las columnas que tuviese la fila con id = 1, junto con los correspondientes valores de esas columnas.

Esto es el funcionamiento básico de la consola de Cassandra. Hay muchos más comandos para obtener información de Cassandra y realizar algunas tareas más, para obtener la ayuda y ver estos comandos solo tienes que escribir lo siguiente:

help;

¿Fácil verdad? Por último, para salir de la consola:

quit;

Logo Cassandra

Cassandra y PHP para desarrolladores SQL: El modelo de datos

8

Si has trabajado con una base de datos relacional tal vez te resulte algo confuso al principio comprender el modelo de datos que usa Cassandra, intentaré ser lo más claro posible, pero si te surgen preguntas no dudes en dejarlas en los comentarios.

 

Columnas

El elemento más básico de la base de datos Cassandra es la columna, se compone de tres elementos: nombre de la columna, valor y timestamp. Os muestro un ejemplo como un array:

 array(
«nombre» => «email»,
«valor» => «webmaster@localhost.com»,

«timestamp» => time()

,

)

 

Super columnas

Es el conjunto de columnas con sus correspondientes valores:

array(
«nombre_superColumna» => array(

 

«usuario1» => array(
«nombre» => «email»,
«valor» => «webmaster@localhost.com»,
«timestamp» => time()
),
«usuario2» => array(
«nombre» => «email»,
«valor» => «email@email.com»,
«timestamp» => time()
),

 

«usuario3» => array(
«nombre» => «email»,
«valor» => «otroemail@otroemail.com»,
«timestamp» => time()
),

),

)

 

Familia de columnas

Es el conjunto de columnas o super columnas. Me explico:

Las column Family o  familia de columnas se puede configurar de dos maneras: como Super o como Simple. Si se elige la opción Simple, en la column family solo se podrán guardar columnas no super columnas. En cambio si la column family está configurada como Super podrá guardar, además de las columnas, las super columnas. Esta flexibilidad permite jugar con la base de datos y adaptarla a nuestras necesidades.

Ejemplo:

 array(
«name» => «ColumnFamily»,
array( «name» => «SuperColumn»,
array( «colums» )
),
)

Keyspace

El keyspace es nuestra base de datos, donde alojaremos todas las columFamilies que necesitemos.

Ejemplo:

array(
«name» => «keyspace»,
array(«name» => «columnFamily»,
array(
[…]
),
),
)

Cassandra y PHP para desarrolladores SQL: Instalación

6

La base de datos No-SQL Cassandra fue creada por Facebook a causa de la necesidad de disponer de una base de datos distribuida de alto rendimiento, flexible, tolerante a fallos, escalable y que fuese capaz de procesar grandes cantidades de datos, más tarde fue liberada bajo licencia Apache y actualmente es utilizada principalmente por empresas de internet con proyectos con un alto uso de base de datos como Twitter.

En esta serie de artículos iré desgranando la instalación, funcionamiento y desarrollo de aplicaciones en PHP con Cassandra, comparando las sentencias utilizadas para insertar, actualizar, etc de SQL con los métodos a utilizar en Cassandra. Por desgracia al ser algo completamente distinto a cualquier base de datos SQL tendré que añadir algo de teoría, pero intentaré que os resulte lo más ameno posible.

Al toro.

 

 

REQUISITOS

Para la realización de esta guía se utilizará Debian Squeeze como sistema operativo. En algunos de los links que dejo como bibliografía al final del artículo existen ejemplos para realizar la instalación en otros sistemas operativos, principalmente CentOS.

Doy por hecho que ya se dispone de un servidor web Apache con PHP instalado en la máquina de testeo que se esté usando. Las aplicaciones que se van a instalar han sido probadas en una máquina virtual Debian utilizando como aplicación de virtualización, VirtualBox instalado en un host Windows XP SP3.

En un servidor de producción recomiendo que tenga bastante RAM (por encima de 1GB, ya veremos más adelante el porqué. En la máquina virtual que uso de testeo dispone de 700 MB y funciona bien, pero en producción no es lo recomendable).

Instalaremos la base de datos Cassandra, el protocolo para conectar con ella y que nos generará el módulo para poder trabajar desde PHP, y PHPCassa que será la abstracción de la base de datos donde tenemos los métodos básicos para trabajar con Cassandra: insertar, actualizar, borrar, crear, etc.

 

PREPARANDO EL SISTEMA

Antes de empezar a instalar debemos actualizar el sistema operativos de la forma habitual:

[codesyntax lang=»bash» lines=»no» title=»Comandos para actualizar el sistema operativo»]

apt-get update
apt-get upgrade

[/codesyntax]

Una vez actualizado el sistema empezamos a instalar.

 

INSTALANDO CASSANDRA

Lo primero que necesitamos hacer para instalar Cassandra es editar los repositorios de Debian:

[codesyntax lang=»bash» title=»Editando los repositorios de Debian»]

nano /etc/apt/sources.list

[/codesyntax]

 

Una vez que se abra el editor añadimos las siguientes lineas:

[codesyntax lang=»bash» lines=»no» title=»Repositorios a añadir en sources.list»]

deb http://www.apache.org/dist/cassandra/debian/ 08x main
deb-src http://www.apache.org/dist/cassandra/debian/ 08x main

[/codesyntax]
Como podrás observar después de la ruta del repositorio se ha añadido «08x» estoy indica el número de versión a obtener, en este caso es la versión actual de cassandra al crear este post. No se indica el número de parche, es decir, actualmente se puede descargar Cassandra 8.4, el «.4» lo obviamos a la hora de los repositorios.

Si dentro de unos meses sale la versión «9.0» solo tendrás que cambiar el ocho por el nueve.
Volvemos a consola y actualizamos los repositorios.

[codesyntax lang=»bash» lines=»no» title=»Actualizamos los repositorios»]

apt-get update

[/codesyntax]

Nos dará un error en las claves gpg. Procedemos a actualizarlas con las siguientes instrucciones:

[codesyntax lang=»bash» title=»Instalamos las claves gpg»]

gpg --keyserver pgp.mit.edu --recv-keys F758CE318D77295D
gpg --export --armor F758CE318D77295D | sudo apt-key add -

[/codesyntax]

Ojo con el guión del final, hay que añadirlo sino dará error.

Con el paquete 0.7.5 nos da otro error de clave gpg así que también instalamos esta de la misma manera:

[codesyntax lang=»bash» lines=»no» title=»Instalamos otra clave gpg»]

gpg --keyserver pgp.mit.edu --recv-keys 2B5C1B00
gpg --export --armor 2B5C1B00 | sudo apt-key add -

[/codesyntax]

Verificamos que tenemos el sistema actualizado.

[codesyntax lang=»bash» title=»Volvemos a comprobar que tenemos el sistema actualizado»]

apt-get update
apt-get upgrade

[/codesyntax]

Actualizamos todos los paquetes que aparezcan.

Procedemos a instalar el paquete de la base de datos NoSQL Cassandra:

[codesyntax lang=»bash» title=»Instalamos el paquete Cassandra»]

apt-get install cassandra

[/codesyntax]

Cassandra necesita a Java para funcionar para ello, una vez instalado Cassandra instalaremos el paquete sun-java6-jdk de la siguiente manera:

[codesyntax lang=»bash» title=»Instalamos la versión de Java de Sun»]

apt-get install sun-java6-jdk

[/codesyntax]

En el caso de que no encuentre el paquete verificar que los repositorios de Debian son correctos. Verifica la versión de Java con:

[codesyntax lang=»bash» lines=»no» title=»Comprobamos la versión de Java»]

 java -version

[/codesyntax]

Debería aparecer algo así:

[codesyntax lang=»text» lines=»no» title=»Texto que devuelve Java al comprobar la versión»]

java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02)
Java HotSpot(TM) Client VM (build 16.3-b01, mixed mode, sharing)

[/codesyntax]

Se recomienda usar la versión de Java de Sun (Sun JDK) en vez de Open JDK debido a un bug encontrado en el mismo.

Si os muestra algo distinto a esto, escribid el siguiente comando:

[codesyntax lang=»bash» lines=»no» title=»Cambiando el paquete a utilizar para trabajar con Java»]

update-alternatives --config javac

[/codesyntax]

Os mostrará un listado con los diferentes paquetes instalados en el sistema que pueden ser utilizados para trabajar con Java. Nosotros usaremos el paquete de Sun, así que pulsa el número del paquete para que Debian lo utilice.

Por último, ejecutamos el siguiente comando para construir las dependencias:

[codesyntax lang=»bash» lines=»no» title=»Construimos el paquete»]

dpkg-buildpackage -uc -us

[/codesyntax]
Una vez instalado procedemos a realizar la configuración de Cassandra:

Creamos varias carpetas necesarias si no han sido creadas automáticamente:

[codesyntax lang=»bash» title=»Creamos las carpetas necesarias para Cassandra»]

mkdir -p {callouts,commitlog,data,saved,caches,staging}
mkdir /var/log/cassandra

[/codesyntax]
Damos permisos a esta carpeta:

[codesyntax lang=»bash» lines=»no» title=»Dando permisos»]

chmod -R 777 /var/log/cassandra/

[/codesyntax]

Creamos el archivo /var/log/cassandra/system.log y le damos permisos:

[codesyntax lang=»bash» lines=»no» title=»Dando permisos»]

chmod -R 777 /var/log/cassandra/system.log

[/codesyntax]

 

INSTALANDO THRIFT Y LA EXTENSIÓN PARA PHP

Thrift es tan solo el protocolo de conexión para poder utilizar Cassandra. Aunque en la extensión para PHP existen métodos para poder trabajar con cassandra, requieren bastante código por eso utilizaremos PHPCassa como abstracción de la base de datos.

Primero descargamos Thrift desde su web: http://thrift.apache.org/download/ y descomprimimos

[codesyntax lang=»bash» title=»Descomprimimos Thrift»]

tar -xzvf thrift-0.7.0.tar.gz

[/codesyntax]

Nos colocamos dentro de la carpeta de Thrift.

Antes de hacer nada ahí instalamos algunos paquetes que nos serán necesarios:

[codesyntax lang=»bash» title=»Paquetes necesarios para instalar Thrift»]

apt-get install libboost-dev automake libtool flex bison pkg-config g++

[/codesyntax]

Una vez instalados esos paquetes configuramos y construimos Thrift:

[codesyntax lang=»bash»]

./configure
make

[/codesyntax]

Construimos la interfaz de PHP Thrift para Cassandra:

[codesyntax lang=»bash»]

./compiler/cpp/thrift -gen php ../ruta-a-cassandra/interface/cassandra.thrift

[/codesyntax]

Copiamos archivos necesario para que PHPCassa funcione:

[codesyntax lang=»bash»]

mkdir -p /usr/share/php/Thrift
cp -R gen-php/ /usr/share/php/Thrift/packages/
cp -R lib/php/src/* /usr/share/php/Thrift/

[/codesyntax]

Cambiamos al directorio del protocolo de Thrif: ruta-a-thrift/lib/php/src/ext/thrift_protocol.

Instalamos el paquete para construir las extensiones de PHP:

[codesyntax lang=»bash»]

apt-get install php5-dev

[/codesyntax]

Construimos la extensión:

[codesyntax lang=»bash»]

phpize
./configure --enable-thrift_protocol
make

[/codesyntax]

Copiamos la extensión a la siguiente carpeta (cuidado: dependiendo del sistema el nombre de la carpeta de destino puede variar):

[codesyntax lang=»bash»]

cp modules/thrift_protocol.so /usr/lib/php5/20060613/

[/codesyntax]

Habilitamos el módulo creando el archivo /etc/php5/conf.d/thrift_protocol.ini con el siguiente texto:

[codesyntax lang=»bash»]

extension=thrift_protocol.so

[/codesyntax]

Comprobamos la instalación:

[codesyntax lang=»bash»]

php -i | grep -v "PWD" | grep "thrift_protocol"

[/codesyntax]

Si todo ha ido bien, reiniciamos Apache:

[codesyntax lang=»bash»]

/etc/init.d/apache2 restart

[/codesyntax]

Iniciamos Cassandra:

[codesyntax lang=»bash»]

/opt/cassandra/bin/cassandra -f

[/codesyntax]

Y probamos la consola para comprobar que todo ha ido correctamente:

[codesyntax lang=»bash»]

/opt/cassandra/bin/cassandra-cli -host localhost -port 9160

[/codesyntax]

Debería indicarte algo así:

[codesyntax lang=»text»]

Connected to: "Test Cluster" on localhost/9160
Welcome to cassandra CLI.

[/codesyntax]

 

INSTALANDO PHPCassa

ACTUALIZACIÓN 20/05/2012
Hace un par de semanas se publicó una nueva versión de phpCassa. Este tutorial está basado en la versión 0.8.a.2 de PHPCassa.

Te recomiendo que te descargues esta versión de PHPCassa para realizar el tutorial.
He actualizado el tutorial a la versión 1.0.a.3 de PHPCassa. Para seguir el tutorial actualizado entra en Cassandra y PHP para desarrolladores SQL.

Bajamos PHPCassa desde github: https://github.com/thobbs/phpcassa

Al descomprimir verás cuatro archivos PHP, copia esos archivos a una carpeta del proyecto en el que estés trabajando.

Abre el archivo «connection.php», la primera línea será una variable global: $GLOBALS[‘THRIFT_ROOT’], modifica su valor con la ruta hacia los archivos Thrift que copiamos con anterioridad. En mi caso se quedaría así:

[codesyntax lang=»php»]

<?php
$GLOBALS['THRIFT_ROOT'] = '/usr/share/php/Thrift/';

[/codesyntax]

Con esto finalizamos la instalación de Cassandra para que pueda ser utilizada por PHP.

Os dejo algunas páginas de interés:
CASSANDRA
http://cassandra.apache.org/
http://cassandra.apache.org/download/
http://wiki.apache.org/cassandra/
http://www.apache.org/dist/cassandra/debian/dists/
http://wiki.apache.org/cassandra/DebianPackaging
http://wiki.apache.org/cassandra/CloudConfig
http://cassandra-user-incubator-apache-org.3065146.n2.nabble.com/Cannot-connect-to-Cassandra-td6463518.html
http://sentidoweb.com/2010/03/18/instalar-cassandra-en-ubuntu.php
http://gettingstartedwithcassandra.blogspot.com/2011/06/exception-connecting-to-localhost9160.html
https://wiki.fourkitchens.com/display/PF/Using+Cassandra+with+PHP
http://es.paperblog.com/instalar-cassandra-en-ubuntu-78327/
http://www.unixmen.com/linux-tutorials/960-install-nosql-cassandra-db-in-ubuntu-via-ppa-repository
http://wiki.apache.org/cassandra/ClientOptions

THRIFT
http://thrift.apache.org/

 



PHPCassa
https://github.com/thobbs/phpcassa
http://groups.google.com/group/phpcassa?pli=1

Symfony: El modelo (II)

0

Cuando Symfony construye el modelo, crea una clase de objeto base para cada una de las tablas definidas en schema.yml. Cada una de estas clases contiene una serie de constructores y accesores por defecto en función de la definición de cada columna: los métodos new, getXXX() y setXXX() (donde XXX es el nombre del campo) permiten crear y obtener las propiedades de los objetos:

[codesyntax lang=»php» title=»Métodos generados en una clase objeto»]

<?php

$articulo = new Articulo();
$articulo->setTitulo('Mi primer artículo');
$articulo->setContenido('Este es mi primer artículo. \n Espero que te guste.');

$titulo    = $articulo->getTitulo();
$contenido = $articulo->getContenido();

[/codesyntax]

 

Para establecer el valor de varios campos a la vez, se puede utilizar el método fromArray(), que también se genera para cada clase objeto:

[codesyntax lang=»php» title=»El método fromArray() es un setter múltiple»]

<?php

$articulo->fromArray(array(
  'titulo'   => 'Mi primer artículo',
  'contenido' => 'Este es mi primer artículo. \n Espero que te guste.'
));

[/codesyntax]

[codesyntax lang=»php» title=»Las claves externas se traducen en getters especiales»]

<?php

// Relación de "muchos a uno"
echo $comentario->getArticulo()->getTitulo();
 => Mi primer artículo
echo $comentario->getArticulo()->getContenido();
 => Este es mi primer artículo.
    Espero que te guste.

// Relación "uno a muchos"
$comentarios = $articulo->getComentarios();

[/codesyntax]

La columna articulo_id de la tabla blog_comentario define implícitamente una clave externa a la tabla blog_articulo. Cada comentario está relacionado con un artículo y un artículo puede tener muchos comentarios. Las clases generadas contienen 5 métodos que traducen esta relación a la forma orientada a objetos, de la siguiente manera:

  • $comentario->getArticulo(): para obtener el objeto Articulo relacionado
  • $comentario->getArticuloId(): para obtener el ID del objeto Articulo relacionado
  • $comentario->setArticulo($articulo): para definir el objeto Articulo relacionado
  • $comentario->setArticuloId($id): para definir el objeto Articulo relacionado a partir de un ID
  • $articulo->getComentarios(): para obtener los objetos Comentario relacionados

[codesyntax lang=»php» title=»Las claves externas se traducen en un setter especial»]

<?php

$comentario = new Comentario();
$comentario->setAutor('Steve');
$comentario->setContenido('¡Es el mejor artículo que he leído nunca!');

// Añadir este comentario al anterior objeto $articulo
$comentario->setArticulo($articulo);

// Sintaxis alternativa
// Solo es correcta cuando el objeto artículo ya
// ha sido guardado anteriormente en la base de datos
$comentario->setArticuloId($articulo->getId());

[/codesyntax]

El método getArticulo() devuelve un objeto de tipo Articulo, que permite utilizar el método accesor getTitulo(). Se trata de una alternativa mucho mejor que realizar la unión de las tablas manualmente, ya que esto último necesitaría varias líneas de código (empezando con la llamada al método $comment->getArticuloId()).

La variable $comentarios contiene un array de objetos de tipo Comentario. Se puede mostrar el primer comentario mediante $comentarios[0] o se puede recorrer la colección entera mediante foreach ($comentarios as $comentario).

Al utilizar el constructor new se crea un nuevo objeto, pero no un registro en la tabla blog_articulo. Si se modifica el objeto, tampoco se reflejan esos cambios en la base de datos. Para guardar los datos en la base de datos, se debe invocar el método save() del objeto.

[codesyntax lang=»php»]

<?php

$articulo->save();

[/codesyntax]

[codesyntax lang=»php» title=»Borrar registros de la base de datos mediante el método delete() del objeto relacionado»]

<?php

foreach ($articulo->getComentarios() as $comentario)
{
  $comentario->delete();
}

[/codesyntax]

Si se conoce la clave primaria de un registro concreto, se puede utilizar el método retrieveByPk() de la clase peer para obtener el objeto relacionado.

[codesyntax lang=»php»]

<?php

$articulo = ArticuloPeer::retrieveByPk(7);

[/codesyntax]

En algunos casos, la clave primaria está formada por más de una columna. Es esos casos, el método retrieveByPK() permite indicar varios parámetros, uno para cada columna de la clave primaria.

También se pueden obtener varios objetos a la vez mediante sus claves primarias, invocando el método retrieveByPKs(), que espera como argumento un array de claves primarias.

 

Cuando se quiere obtener más de un registro, se debe utilizar el método doSelect() de la clase peer correspondiente a los objetos que se quieren obtener. Por ejemplo, para obtener objetos de la clase Articulo, se llama al método ArticuloPeer::doSelect().

El primer parámetro del método doSelect() es un objeto de la clase Criteria, que es una clase para definir consultas simples sin utilizar SQL, para conseguir la abstracción de base de datos.

Un objeto Criteria vacío devuelve todos los objetos de la clase.

[codesyntax lang=»php» title=»Obtener registros mediante Criteria con el método doSelect() (Criteria vacío)»]

<?php

$c = new Criteria();
$articulos = ArticuloPeer::doSelect($c);

// Genera la siguiente consulta SQL
SELECT blog_articulo.ID, blog_articulo.TITLE, blog_articulo.CONTENIDO,
       blog_articulo.CREATED_AT
FROM   blog_articulo;

[/codesyntax]

Para las selecciones más complejas de objetos, se necesitan equivalentes a las sentencias WHERE, ORDER BY, GROUP BY y demás de SQL. El objeto Criteria dispone de métodos y parámetros para indicar todas estas condiciones. Por ejemplo, para obtener todos los comentarios escritos por el usuario Steve y ordenados por fecha:

[codesyntax lang=»php» title=»Obtener registros mediante Criteria con el método doSelect() (Criteria con condiciones)»]

<?php

$c = new Criteria();
$c->add(ComentarioPeer::AUTOR, 'Steve');
$c->addAscendingOrderByColumn(ComentarioPeer::CREATED_AT);
$comentarios = ComentarioPeer::doSelect($c);

// Genera la siguiente consulta SQL
SELECT blog_comentario.ARTICULO_ID, blog_comentario.AUTOR, blog_comentario.CONTENIDO,
       blog_comentario.CREATED_AT
FROM   blog_comentario
WHERE  blog_comentario.autor = 'Steve'
ORDER BY blog_comentario.CREATED_AT ASC;

[/codesyntax]

as constantes de clase que se pasan como parámetros a los métodos add() hacen referencia a los nombres de las propiedades. Su nombre se genera a partir del nombre de las columnas en mayúsculas. Por ejemplo, para indicar la columna contenido de la tabla blog_articulo, se utiliza la constante de clase llamada ArticuloPeer::CONTENIDO.

Otro ejemplo del uso de Criteria con condiciones múltiples. En el ejemplo se obtienen todos los comentarios del usuario Steve en los artículos que contienen la palabra enjoy y además, ordenados por fecha.

 

[codesyntax lang=»php»]

<?php

$c = new Criteria();
$c->add(ComentarioPeer::AUTOR, 'Steve');
$c->addJoin(ComentarioPeer::ARTICULO_ID, ArticuloPeer::ID);
$c->add(ArticuloPeer::CONTENIDO, '%enjoy%', Criteria::LIKE);
$c->addAscendingOrderByColumn(ComentarioPeer::CREATED_AT);
$comentarios = ComentarioPeer::doSelect($c);

// Genera la siguiente consulta SQL
SELECT blog_comentario.ID, blog_comentario.ARTICULO_ID, blog_comentario.AUTOR,
       blog_comentario.CONTENIDO, blog_comentario.CREATED_AT
FROM   blog_comentario, blog_articulo
WHERE  blog_comentario.AUTOR = 'Steve'
       AND blog_articulo.CONTENIDO LIKE '%enjoy%'
       AND blog_comentario.ARTICULO_ID = blog_articulo.ID
ORDER BY blog_comentario.CREATED_AT ASC

[/codesyntax]

Además del método doSelect(), todas las clases peer tienen un método llamado doCount(), que simplemente cuenta el número de registros que satisfacen las condiciones pasadas como parámetro y devuelve ese número como un entero.

Las clases peer también incluyen métodos doDelete(), doInsert() y doUpdate() (todos ellos requieren como parámetro un objeto de tipo Criteria).

Por último, si solo se quiere obtener el primer objeto, se puede reemplazar el método doSelect() por doSelectOne(). Es muy útil cuando se sabe que las condiciones de Criteria solo van a devolver un resultado, y su ventaja es que el método devuelve directamente un objeto en vez de un array de objetos.

A veces, no es necesario obtener los objetos, sino que solo son necesarios algunos datos calculados por la base de datos. Por ejemplo, para obtener la fecha de creación de todos los artículos, no tiene sentido obtener todos los artículos y después recorrer el array de los resultados.

Para realizar consultas a la base de datos con PDO, es necesario realizar los siguientes pasos:

  • Obtener la conexión con la base de datos.
  • Construir la consulta.
  • Crear una sentencia con esa consulta.
  • Iterar el result set que devuelve la ejecución de la sentencia.

[codesyntax lang=»php» title=»Consultas SQL personalizadas con PDO»]

<?php

conexion = Propel::getConnection();
$consulta = 'SELECT MAX(%s) AS max FROM %s';
$consulta = sprintf($consulta, ArticuloPeer::CREATED_AT, ArticuloPeer::TABLE_NAME);
$sentencia = $conexion->prepare($consulta);
$sentencia->execute();
$resultset = $sentencia->fetch(PDO::FETCH_OBJ);
$max = $resultset->max;

[/codesyntax]

Normalmente, cuando una tabla tiene una columna llamada created_at, se utiliza para almacenar un timestamp de la fecha de creación del registro. La misma idea se aplica a las columnas updated_at, cuyo valor se debe actualizar cada vez que se actualiza el propio registro.

La buena noticia es que Symfony reconoce estos nombres de columna y se ocupa de actualizar su valor de forma automática. No es necesario establecer manualmente el valor de las columnas created_at y updated_at; se actualizan automáticamente. Lo mismo se aplica a las columnas llamadas created_on y updated_on.
[codesyntax lang=»php» title=»Las columnas created_at y updated_at se gestionan automáticamente»]

<?php

$comentario = new Comentario();
$comentario->setAutor('Steve');
$comentario->save();

// Muestra la fecha de creación
echo $comentario->getCreatedAt();
 => [fecha de la operación INSERT de la base de datos]

[/codesyntax]

Además, los getters de las columnas de fechas permiten indicar el formato de la fecha como argumento:

[codesyntax lang=»php»]

<?php

echo $comentario->getCreatedAt('Y-m-d');

[/codesyntax]

 

Symfony: El modelo (I)

0

Las bases de datos son relacionales. PHP 5 y Symfony están orientados a objetos. Para acceder de forma efectiva a la base de datos desde un contexto orientado a objetos, es necesaria una interfaz que traduzca la lógica de los objetos a la lógica relacional. Esta interfaz se llama ORM (object-relational mapping) o «mapeo de objetos a bases de datos», y está formada por objetos que permiten acceder a los datos y que contienen en sí mismos el código necesario para hacerlo.

La utilización de objetos en vez de registros y de clases en vez de tablas, tiene otra ventaja: permite añadir métodos accesores en los objetos que no tienen relación directa con una tabla. Si se dispone por ejemplo de una tabla llamada cliente con dos campos llamados nombre y apellidos, puede que se necesite un dato llamado NombreCompleto que incluya y combine el nombre y los apellidos. En el mundo orientado a objetos, es tan fácil como añadir un método accesor a la clase Cliente. Desde el punto de vista de la aplicación, no existen diferencias entre los atributos Nombre, Apellidos, NombreCompleto de la clase Cliente. Solo la propia clase es capaz de determinar si un atributo determinado se corresponde con una columna de la base de datos.

[codesyntax lang=»php» title=»Los métodos accesores en la clase del modelo permiten ocultar la estructura real de la tabla de la base de datos»]

<?php

public function getNombreCompleto()
{
  return $this->getNombre().' '.$this->getApellidos();
}

[/codesyntax]

Todo el código repetitivo de acceso a los datos y toda la lógica de negocio de los propios datos se puede almacenar en esos objetos. Imagina que se ha definido la clase CarritoCompra en la que se almacenan Productos (que son objetos). Para obtener el precio total del carrito de la compra antes de realizar el pago, se puede crear un método que encapsula el proceso de cálculo:

[codesyntax lang=»php» title=»Los métodos accesores ocultan la lógica de los datos»]

<?php

public function getTotal()
{
  $total = 0;
  foreach ($this->getProductos() as $producto)
  {
    $total += $producto->getPrecio() * $producto->getCantidad();
  }

  return $total;
}

[/codesyntax]

 

ESQUEMA DE BASE DE DATOS DE SYMFONY

Para crear el modelo de objetos de datos que utiliza Symfony, se debe traducir el modelo relacional de la base de datos a un modelo de objetos de datos. Para realizar ese mapeo o traducción, el ORM necesita una descripción del modelo relacional, que se llama «esquema» (schema). En el esquema se definen las tablas, sus relaciones y las características de sus columnas.

La sintaxis que utiliza Symfony para definir los esquemas hace uso del formato YAML. Los archivos schema.yml deben guardarse en el directorio miproyecto/config/.

[codesyntax lang=»text» title=»Ejemplo de schema.yml»]

propel:
  blog_articulo:
    _attributes:   { phpName: Articulo }
    id:
    titulo:        varchar(255)
    contenido:     longvarchar
    created_at:
  blog_comentario:
    _attributes:   { phpName: Comentario }
    id:
    articulo_id:
    autor:         varchar(255)
    contenido:     longvarchar
    created_at:

[/codesyntax]

 

SINTAXIS BÁSICA DE LOS ESQUEMAS

En el archivo schema.yml, la primera clave representa el nombre de la conexión. Puede contener varias tablas, cada una con varias columnas. Siguiendo la sintaxis de YAML, las claves terminan con dos puntos (:) y la estructura se define mediante la indentación con espacios, no con tabuladores.

Cada tabla puede definir varios atributos, incluyendo el atributo phpName (que es el nombre de la clase PHP que será generada para esa tabla). Si no se menciona el atributo phpName para una tabla, Symfony crea una clase con el mismo nombre que la tabla al que se aplica las normas del camelCase.

Las tablas contienen columnas y el valor de las columnas se puede definir de 3 formas diferentes:

  • Si no se indica nada, Symfony intenta adivinar los atributos más adecuados para la columna en función de su nombre y de una serie de convenciones. Por ejemplo, en el listado anterior no es necesario definir la columna id. Symfony por defecto la trata como de tipo entero (integer), cuyo valor se auto-incrementa y además, clave principal de la tabla. En la tabla blog_comentario, la columna articulo_id se trata como una clave externa a la tabla blog_articulo (las columnas que acaban en _id se consideran claves externas, y su tabla relacionada se determina automáticamente en función de la primera parte del nombre de la columna). Las columnas que se llaman created_at automáticamente se consideran de tipo timestamp. Para este tipo de columnas, no es necesario definir su tipo. Esta es una de las razones por las que es tan fácil crear archivos schema.yml.
  • Si sólo se define un atributo, se considera que es el tipo de columna. Symfony entiende los tipos de columna habituales: boolean, integer, float, date, varchar(tamaño), longvarchar (que se convierte, por ejemplo, en tipo text en MySQL), etc. Para contenidos de texto de más de 256 caracteres, se utiliza el tipo longvarchar, que no tiene tamaño definido (pero que no puede ser mayor que 65KB en MySQL). Los tipos date y timestamp tienen las limitaciones habituales de las fechas de Unix y no pueden almacenar valores anteriores al 1 de Enero de 1970. Como puede ser necesario almacenar fechas anteriores (por ejemplo para las fechas de nacimiento), existe un formato de fechas «anteriores a Unix» que son bu_date and bu_timestamp.
  • Si se necesitan definir otros atributos a la columna (por ejemplo su valor por defecto, si es obligatorio o no, etc.), se indican los atributos como pares clave: valor.

Las columnas también pueden definir el atributo phpName, que es la versión modificada de su nombre según las convenciones habituales (Id, Titulo, Contenido, etc) y que normalmente no es necesario redefinir.

 

LAS CLASES DEL MODELO

Como ya has podido leer, Symfony a través de Propel genera una clase para cada tabla. Para generar esas clases simplemente, una vez hayas terminado de crear el esquema del modelo, en la consola escribe:

> php symfony propel:build-model

Al ejecutar ese comando, se analiza el esquema y se generan las clases base del modelo, que se almacenan en el directorio lib/model/om/ del proyecto:

  • BaseArticulo.php
  • BaseArticuloPeer.php
  • BaseComentario.php
  • BaseComentarioPeer.php

Además, se crean las verdaderas clases del modelo de datos en el directorio lib/model/:

  • Articulo.php
  • ArticuloPeer.php
  • Comentario.php
  • ComentarioPeer.php

Sólo se han definido dos tablas y se han generado ocho archivos. Aunque este hecho no es nada extraño, merece una explicación.

Puede ser necesario añadir métodos y propiedades personalizadas en los objetos del modelo (piensa por ejemplo en el método getNombreCompleto()). También es posible que a medida que el proyecto se esté desarrollando, se añadan tablas o columnas. Además, cada vez que se modifica el archivo schema.yml se deben regenerar las clases del modelo de objetos mediante el comando propel-build-model. Si se añaden los métodos personalizados en las clases que se generan, se borrarían cada vez que se vuelven a generar esas clases.

Las clases con nombre Base del directorio lib/model/om/ son las que se generan directamente a partir del esquema. Nunca se deberían modificar esas clases, porque cada vez que se genera el modelo, se borran todas las clases.

Por otra parte, las clases de objetos propias que están en el directorio lib/model heredan de las clases con nombre Base. Estas clases no se modifican cuando se ejecuta la tarea propel:build-model, por lo que son las clases en las que se añaden los métodos propios.
[codesyntax lang=»php» title=»Archivo de ejemplo de una clase del modelo, en lib/model/Articulo.php»]

<?php

class Articulo extends BaseArticulo
{
}

[/codesyntax]

Articulo y Comentario son clases objeto que representan un registro de la base de datos. Permiten acceder a las columnas de un registro y a los registros relacionados. Por tanto, es posible obtener el título de un artículo invocando un método del objeto Articulo:

[codesyntax lang=»php» title=»Las clases objeto disponen de getters para los registros de las columnas»]

<?php

$articulo = new Articulo();

// ...
$titulo = $articulo->getTitulo();

[/codesyntax]

ArticuloPeer y ComentarioPeer son clases de tipo «peer«; es decir, clases que tienen métodos estáticos para trabajar con las tablas de la base de datos. Proporcionan los medios necesarios para obtener los registros de las tablas. Sus métodos devuelven normalmente un objeto o una colección de objetos de la clase objeto relacionada:

[codesyntax lang=»php» title=»Las clases «peer» contienen métodos estáticos para obtener registros de la base de datos»]

<?php

// $articulos es un array de objetos de la clase Articulo
$articulos = ArticuloPeer::retrieveByPks(array(123, 124, 125));

[/codesyntax]

Symfony, creando el archivo schema.yml para una base de datos ya existente

0

Sí, sigo dándole caña a Symfony… y cometiendo errores de novato xD.

Para ir probando Symfony con un proyecto real, he decidido utilizar una base de datos ya existente para generar una nueva versión del proyecto anterior, y como ya sabrás para que Symfony (bueno, en este caso Propel) genere las clases necesarias para trabajar con la BD es necesario que la estructura de ésta esté en el archivo schema.yml dentro de la carpeta /config del proyecto.

Para ello, primero hay que configurar la conexión a la base de datos en symfony de la siguiente manera:

symfony configure:database «mysql://login:password@localhost/basededatos»
symfony configure:database «mysql:host=localhost;dbname=nombreBD» root pass

Ahora le decimos a Propel que genere el archivo schema.yml a partir de la base de datos:

symfony propel:build-schema

Propel coge los datos de configuración de la BD que le hemos dado anteriormente.

Y con esto se genera el archivo schema.yml y symfony está listo para generar las clases.

 

PD1: Propel utiliza PDO para conectar con la BD, si por alguna razón os muestra un error diciendo que no encuentra el driver o algo por el estilo, simplemente id al archivo php.ini y habilitad la extensión pdo_mysql y reiniciar apache, esto lo debería arreglar.

PD2: Cada vez que tengas que generar el modelo recuerda de limpiar la caché de symfony para evitarte problemas:

symfony cc

PD3: Si al generar el modelo, Propel te devuelve un error de «tabla duplicada» o parecido, solo tienes que ir a la carpeta /config del proyecto y eliminar un archivo llamado generated-schema.yml (si no recuerdo mal).

Ir arriba