Mikel
(38 comentarios, 115 entradas)
Este usuario no ha compartido ninguna información de perfil
Entradas de Mikel
Cassandra y PHP para desarrolladores SQL: Instalación
0La 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:
apt-get update
apt-get upgradeUna vez actualizado el sistema empezamos a instalar.
INSTALANDO CASSANDRA
Lo primero que necesitamos hacer para instalar Cassandra es editar los repositorios de Debian:
- nano /etc/apt/sources.list
Una vez que se abra el editor añadimos las siguientes lineas:
deb http://www.apache.org/dist/cassandra/debian/ 08x main
deb-src http://www.apache.org/dist/cassandra/debian/ 08x mainComo 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.
apt-get updateNos dará un error en las claves gpg. Procedemos a actualizarlas con las siguientes instrucciones:
- gpg --keyserver pgp.mit.edu --recv-keys F758CE318D77295D
- gpg --export --armor F758CE318D77295D | sudo apt-key add -
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:
gpg --keyserver pgp.mit.edu --recv-keys 2B5C1B00
gpg --export --armor 2B5C1B00 | sudo apt-key add -Verificamos que tenemos el sistema actualizado.
- apt-get update
- apt-get upgrade
Actualizamos todos los paquetes que aparezcan.
Procedemos a instalar el paquete de la base de datos NoSQL Cassandra:
- apt-get install cassandra
Cassandra necesita a Java para funcionar para ello, una vez instalado Cassandra instalaremos el paquete sun-java6-jdk de la siguiente manera:
- apt-get install sun-java6-jdk
En el caso de que no encuentre el paquete verificar que los repositorios de Debian son correctos. Verifica la versión de Java con:
java -versionDebería aparecer algo así:
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)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:
update-alternatives --config javacOs 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:
dpkg-buildpackage -uc -usUna vez instalado procedemos a realizar la configuración de Cassandra:
Creamos varias carpetas necesarias si no han sido creadas automáticamente:
- mkdir -p {callouts,commitlog,data,saved,caches,staging}
- mkdir /var/log/cassandra
Damos permisos a esta carpeta:
chmod -R 777 /var/log/cassandra/Creamos el archivo /var/log/cassandra/system.log y le damos permisos:
chmod -R 777 /var/log/cassandra/system.log
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
- tar -xzvf thrift-0.7.0.tar.gz
Nos colocamos dentro de la carpeta de Thrift.
Antes de hacer nada ahí instalamos algunos paquetes que nos serán necesarios:
- apt-get install libboost-dev automake libtool flex bison pkg-config g++
Una vez instalados esos paquetes configuramos y construimos Thrift:
- ./configure
- make
Construimos la interfaz de PHP Thrift para Cassandra:
- ./compiler/cpp/thrift -gen php ../ruta-a-cassandra/interface/cassandra.thrift
Copiamos archivos necesario para que PHPCassa funcione:
- mkdir -p /usr/share/php/Thrift
- cp -R gen-php/ /usr/share/php/Thrift/packages/
- cp -R lib/php/src/* /usr/share/php/Thrift/
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:
- apt-get install php5-dev
Construimos la extensión:
- phpize
- ./configure --enable-thrift_protocol
- make
Copiamos la extensión a la siguiente carpeta (cuidado: dependiendo del sistema el nombre de la carpeta de destino puede variar):
- cp modules/thrift_protocol.so /usr/lib/php5/20060613/
Habilitamos el módulo creando el archivo /etc/php5/conf.d/thrift_protocol.ini con el siguiente texto:
- extension=thrift_protocol.so
Comprobamos la instalación:
- php -i | grep -v "PWD" | grep "thrift_protocol"
Si todo ha ido bien, reiniciamos Apache:
- /etc/init.d/apache2 restart
Iniciamos Cassandra:
- /opt/cassandra/bin/cassandra -f
Y probamos la consola para comprobar que todo ha ido correctamente:
- /opt/cassandra/bin/cassandra-cli -host localhost -port 9160
Debería indicarte algo así:
- Connected to: "Test Cluster" on localhost/9160
- Welcome to cassandra CLI.
INSTALANDO PHPCassa
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í:
- <?php
- $GLOBALS['THRIFT_ROOT'] = '/usr/share/php/Thrift/';
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
unidadesdemedida.com: ¿Cuántas piscinas olímpicas es un campo de fútbol?
0Desde hace bastante tiempo, los medios de comunicación han utilizado unidades de medida diferentes a las del Sistema Internacional (SI) para medir las nuevas infraestructuras o vehículos que el ser humano crea. Últimamente se ve incluso la utilización de unidades de medida especiales para el coste de los fichajes de deportistas, principalmente en el fútbol.
Para aquellos que desean saber cuántas piscinas olímpicas es un campo de fútbol entre otras unidades, no os perdáis mi nuevo proyecto que espero os guste aunque aun le quedan muchas unidades que añadir.
Cualquier sugerencia o crítica será bienvenida.
Symfony: Enrutamiento (II)
0Por defecto Symfony no muestra el archivo que hace de controlador (index.php, frontend_dev.php, etc) para cambiarlo vamos al archivo settings.yml aquí podemos indicar que no se muestre el nombre del controlador de la siguiente manera:
- prod:
- .settings:
- no_script_name: off
Hiperenlaces, botones y formularios
- / Opciones adicionales como array asociativo
- 'class' => 'miclasecss',
- 'target' => '_blank'
- )) ?>
-
- // Opciones adicionales como cadena de texto (producen el mismo resultado)
- <?php echo link_to('Mi artículo', 'articulo/ver?titulo=Economia_en_Francia','class=miclasecss target=_blank') ?>
- => <a href="/url/con/enrutamiento/a/Economia_en_Francia" class="miclasecss" target="_blank">Mi artículo</a>
- // URI interna
- <?php echo link_to('Mi artículo', 'articulo/ver?titulo=Economia_en_Francia') ?>
- => <a href="/url/con/enrutamiento/a/Economia_en_Francia">Mi artículo</a>
-
- // URI interna con parámetros dinámicos
- <?php echo link_to('Mi artículo', 'articulo/ver?titulo='.$articulo->getTitulo()) ?>
-
- // URI interna con anclas (enlaces a secciones internas de la página)
- <?php echo link_to('Mi artículo', 'articulo/ver?titulo=Economia_en_Francia#seccion1') ?>
- => <a href="/url/con/enrutamiento/a/Economia_en_Francia#seccion1">Mi artículo</a>
-
- // URL absolutas
- <?php echo link_to('Mi artículo', 'http://www.ejemplo.com/cualquierpagina.html') ?>
- => <a href="http://www.ejemplo.com/cualquierpagina.html">Mi artículo</a>
- // Enlace simple de texto
- <?php echo link_to('Mi artículo', 'articulo/ver?titulo=Economia_en_Francia') ?>
- => <a href="/url/con/enrutamiento/a/Economia_en_Francia">Mi artículo</a>
-
- // Enlace en una imagen
- <?php echo link_to(image_tag('ver.gif'), 'articulo/ver?titulo=Economia_en_Francia') ?>
- => <a href="/url/con/enrutamiento/a/Economia_en_Francia"><img src="/images/ver.gif" /></a>
-
- // Boton
- <?php echo button_to('Mi artículo', 'articulo/ver?titulo=Economia_en_Francia') ?>
- => <input value="Mi artículo" type="button" onclick="document.location.href='/url/con/enrutamiento/a/Economia_en_Francia';" />
-
- // Formulario
- <?php echo form_tag('articulo/ver?titulo=Economia_en_Francia') ?>
- => <form method="post" action="/url/con/enrutamiento/a/Economia_en_Francia" />
- <?php echo link_to('Borrar elemento', 'item/borrar?id=123', 'confirm=¿Estás seguro?') ?>
- => <a onclick="return confirm('¿Estás seguro?');"
- href="/url/con/enrutamiento/a/borrar/123.html">Borrar elemento</a>
-
- <?php echo link_to('Añadir al carrito', 'carritoCompra/anadir?id=100', 'popup=true') ?>
- => <a onclick="window.open(this.href);return false;"
- href="/url/con/enrutamiento/a/carritoCompra/anadir/id/100.html">Añadir al carrito</a>
-
- )) ?>
- => <a onclick="window.open(this.href,'popupWindow','width=310,height=400,left=320,top=0');return false;"
- href="/url/con/enrutamiento/a/carritoCompra/anadir/id/100.html">Añadir al carrito</a>
Symfony: El enrutamiento (I)
0Continuo con el libro de Symfony 1.4 en esta ocasión toca el enrutamiento.
Normalmente las urls que escribimos son así:
http://www.ejemplo.com/web/galeria/album.php?nombre=mis%20vacaciones
http://www.ejemplo.com/web/weblog/publico/post/listado.php
http://www.ejemplo.com/web/general/contenido/pagina.php?nombre=sobre%20nosotros
Y deberían ser así:
http://www.ejemplo.com/articulos/economia/2006/sectores-actividad.html
Principalmente porque son mucho más legibles para los usuarios y buscadores, y además evitamos mostrar información a un posible atacante.
El sistema de enrutamiento utiliza un archivo de configuración especial, llamado routing.yml, en el que se pueden definir las reglas de enrutamiento.
- articulo_segun_titulo:
- url: articulos/:tema/:ano/:titulo.html
- param: { module: articulo, action: permalink }
Todas las peticiones realizadas a una aplicación Symfony son analizadas en primer lugar por el sistema de enrutamiento (que es muy sencillo porque todas las peticiones se gestionan mediante un único controlador frontal). El sistema de enrutamiento busca coincidencias entre la URL de la petición y los patrones definidos en las reglas de enrutamiento. Si se produce una coincidencia, las partes del patrón que tienen nombre se transforman en parámetros de la petición y se juntan a los parámetros definidos en la clave param:.
Después, la petición se pasa a la acción permalink del módulo articulo, que dispone de toda la información necesaria en los parámetros de la petición para obtener el artículo solicitado.
El mecanismo de enrutamiento también funciona en la otra dirección. Para mostrar las URL en los enlaces de una aplicación, se debe proporcionar al sistema de enrutamiento la información necesaria para determinar la regla que se debe aplicar a cada enlace. Además, no se deben escribir los enlaces directamente con etiquetas <a> (ya que de esta forma no se estaría utilizando el sistema de enrutamiento) sino con un helper especial:
- // El helper url_for() transforma una URI interna en una URL externa
- <a href="<?php echo url_for('articulo/permalink?tema=economia&ano=2006&titulo=sectores-actividad') ?>">pincha aquí</a>
-
- // El helper reconoce que la URI cumple con la regla articulo_segun_titulo
- // El sistema de enrutamiento crea una URL externa a partir de el
- => <a href="http://www.ejemplo.com/articulos/economia/2006/sectores-actividad.html">pincha aquí</a>
-
- // El helper link_to() muestra directamente un enlace
- // y evita tener que mezclar PHP y HTML
- <?php echo link_to(
- 'pincha aqui',
- 'articulo/permalink?tema=economia&ano=2006&titulo=sectores-actividad'
- ) ?>
-
- // Internamente link_to() llama a url_for(), por lo que el resultado es el mismo
- => <a href="http://www.ejemplo.com/articulos/economia/2006/sectores-actividad.html">pincha aquí</a>
Symfony: El modelo (III)
0CONEXIONES A LA BASE DE DATOS
- > php symfony configure:database "mysql://login:password@localhost/blog"
- > php symfony --env=prod configure:database "mysql://login:password@localhost/blog"
- > php symfony --app=frontend configure:database "mysql://login:password@localhost/blog"
- > php symfony --name=otraconexion configure:database "mysql://login:password@localhost/blog"
Las opciones de conexión con la base de datos también se pueden establecer manualmente en el archivo databases.yml que se encuentra en el directorio config/.
- all:
- propel:
- class: sfPropelDatabase
- param:
- dsn: mysql://login:password@localhost/blog
- prod:
- propel:
- param:
- hostspec: mi_servidor_datos
- usuarioname: mi_nombre_usuario
- password: xxxxxxxxxx
-
- all:
- propel:
- class: sfPropelDatabase
- param:
- phptype: mysql # fabricante de la base de datos
- hostspec: localhost
- database: blog
- usuarioname: login
- password: passwd
- port: 80
- encoding: utf8 # Codificación utilizada para crear la tabla
- persistent: true # Utilizar conexiones persistentes
Los valores permitidos para el parámetro phptype corresponden a los tipos de bases de datos soportados por PDO:
- mysql
- mssql
- pgsql
- sqlite
- oracle
Si se utiliza una base de datos de tipo SQLite, el parámetro hostspec debe indicar la ruta al archivo de base de datos.
- all:
- propel:
- class: sfPropelDatabase
- param:
- phptype: sqlite
- database: %SF_DATA_DIR%/blog.db
EXTENDER EL MODELO
Los métodos del modelo que se generan automáticamente están muy bien, pero no siempre son suficientes. Si se incluye lógica de negocio propia, es necesario extender el modelo añadiendo nuevos métodos o redefiniendo algunos de los existentes.
AÑADIR NUEVOS MÉTODOS
Los nuevos métodos se pueden añadir en las clases vacías del modelo que se generan en el directorio lib/model/. Se emplea $this para invocar a los métodos del objeto actual y self:: para invocar a los métodos estáticos de la clase actual. No se debe olvidar que las clases personalizadas heredan los métodos de las clases Base del directorio lib/model/om/.
- <?
-
- class Articulo extends BaseArticulo
- {
- public function __toString()
- {
- return $this->getTitulo(); // getTitulo() se hereda de BaseArticulo
- }
- }
También se pueden extender las clases peer, como por ejemplo para obtener todos los artículos ordenados por fecha de creación:
- <?
-
- class ArticuloPeer extends BaseArticuloPeer
- {
- public static function getTodosOrdenadosPorFecha()
- {
- $c = new Criteria();
- $c->addAscendingOrderByColumn(self::CREATED_AT);
- return self::doSelect($c);
- }
- }
- <?
-
- foreach (ArticuloPeer::getTodosOrdenadosPorFecha() as $articulo)
- {
- echo $articulo; // Se llama al método mágico __toString()
- }
REDEFINIR MÉTODOS EXISTENTES
Si alguno de los métodos generados automáticamente en las clases Base no satisfacen las necesidades de la aplicación, se pueden redefinir en las clases personalizadas. Solamente es necesario mantener el mismo número de argumentos para cada método.
Por ejemplo, el método $articulo->getComentarios() devuelve un array de objetos Comentario, sin ningún tipo de ordenamiento. Si se necesitan los resultados ordenados por fecha de creación siendo el primero el comentario más reciente, se puede redefinir el método getComentarios(). Como el método getComentarios() original (guardado en lib/model/om/BaseArticulo.php) requiere como argumentos un objeto de tipo Criteria y una conexión, la nueva función debe contener esos mismos parámetros.
- <?
-
- public function getComentarios($criteria = null, $con = null)
- {
- {
- $criteria = new Criteria();
- }
- else
- {
- // Los objetos se pasan por referencia en PHP5, por lo que se debe clonar
- // el objeto original para no modificarlo
- $criteria = clone $criteria;
- }
- $criteria->addDescendingOrderByColumn(ComentarioPeer::CREATED_AT);
-
- return parent::getComentarios($criteria, $con);
- }
El método personalizado acaba llamando a su método padre en la clase Base, lo que se considera una buena práctica. No obstante, es posible saltarse completamente la clase Base y devolver el resultado directamente.
USO DE COMPORTAMIENTOS EN EL MODELO
Algunas de las modificaciones que se realizan en el modelo son genéricas y por tanto se pueden reutilizar. Por ejemplo, los métodos que hacen que un objeto del modelo sea reordenable o un bloqueo de tipo optimistic en la base de datos para evitar conflictos cuando se guardan de forma concurrente los objetos se pueden considerar extensiones genéricas que se pueden añadir a muchas clases.
Symfony encapsula estas extensiones en “comportamientos” (del inglés behaviors). Los comportamientos son clases externas que proporcionan métodos extras a las clases del modelo. Las clases del modelo están definidas de forma que se puedan enganchar estas clases externas y Symfony extiende las clases del modelo mediante sfMixer.
Para habilitar los comportamientos en las clases del modelo, se debe modificar una opción del archivo config/propel.ini:
propel.builder.AddBehaviors = true // El valor por defecto es false
Symfony no incluye por defecto ningún comportamiento, pero se pueden instalar mediante plugins. Una vez que el plugin se ha instalado, se puede asignar un comportamiento a una clase mediante una sola línea de código. Si por ejemplo se ha instalado el plugin sfPropelParanoidBehaviorPlugin en la aplicación, se puede extender la clase Articulo con este comportamiento añadiendo la siguiente línea de código al final del archivo Articulo.class.php:
Después de volver a generar el modelo, los objetos de tipo Articulo que se borren permanecerán en la base de datos, aunque será invisibles a las consultas que hacen uso de los métodos del ORM, a no ser que se deshabilite temporalmente el comportamiento mediante sfPropelParanoidBehavior::disable().
Desde la versión 1.1 de Symfony también es posible declarar los comportamientos directamente en el archivo schema.yml, incluyéndolos bajo la clave _behaviors.
La lista de plugins de Symfony disponible en el wiki incluye numerosos comportamientos http://trac.symfony-project.org/wiki/SymfonyPlugins#Behaviors. Cada comportamiento tiene su propia documentación y su propia guía de instalación.
SINTAXIS EXTENDIDA DEL ESQUEMA
Atributos
Se pueden definir atributos específicos para las conexiones y las tablas. Estas opciones se establecen bajo la clave _attributes.
- propel:
- _attributes: { noXsd: false, defaultIdMethod: none, package: lib.model }
- blog_articulo:
- _attributes: { phpName: Articulo }
Si se quiere validar el esquema antes de que se genere el código asociado, se debe desactivar en la conexión el atributo noXSD. La conexión también permite que se le indique el atributo defaultIdMethod. Si no se indica, se utilizará el método nativo de generación de IDs –por ejemplo, autoincrement en MySQL o sequences en PostgreSQL. El otro valor permitido es none.
El atributo package es como un namespace; indica la ruta donde se guardan las clases generadas automáticamente. Su valor por defecto es lib/model/, pero se puede modificar para organizar el modelo en una estructura de subpaquetes. Si por ejemplo no se quieren mezclar en el mismo directorio las clases del núcleo de la aplicación con las clases de un sistema de estadísticas, se pueden definir dos esquemas diferentes con los paquetes lib.model.business y lib.model.stats.
Ya se ha visto el atributo de tabla phpName, que se utiliza para establecer el nombre de la clase generada automáticamente para manejar cada tabla de la base de datos.
Las tablas que guardan contenidos adaptados para diferentes idiomas (es decir, varias versiones del mismo contenido en una tabla relacionada, para conseguir la internacionalización) también pueden definir dos atributos adicionales.
- propel:
- blog_articulo:
- _attributes: { isI18N: true, i18nTable: db_group_i18n }
DETALLE DE LAS COLUMNAS
La sintaxis básica ofrece dos posibilidades: dejar que Symfony deduzca las características de una columna a partir de su nombre (indicando un valor vacío para esa columna) o definir el tipo de columna con uno de los tipos predefinidos.
- propel:
- blog_articulo:
- id: # Symfony se encarga de esta columna
- titulo: varchar(50) # Definir el tipo explícitamente
Se pueden definir muchos más aspectos de cada columna. Si se definen, se utiliza un array asociativo para indicar las opciones de la columna.
- propel:
- blog_articulo:
- id: { type: integer, required: true, primaryKey: true, autoIncrement: true }
- name: { type: varchar(50), default: foobar, index: true }
- group_id: { type: integer, foreignTable: db_group, foreignReference: id, onDelete: cascade }
Los parámetros de las columnas son los siguientes:
- type: Tipo de columna. Se puede elegir entre boolean, tinyint, smallint, integer, bigint, double, float, real, decimal, char, varchar(tamano), longvarchar, date, time, timestamp, bu_date, bu_timestamp, blob y clob
- required: valor booleano. Si vale true la columna debe tener obligatoriamente un valor.
- default: el valor por defecto.
- primaryKey: valor booleano. Si vale true indica que es una clave primaria.
- autoIncrement: valor booleano. Si se indica true para las columnas de tipo integer, su valor se auto-incrementará.
- sequence: el nombre de la secuencia para las bases de datos que utilizan secuencias para las columnas autoIncrement (por ejemplo PostgreSQL y Oracle).
- index: valor booleano. Si vale true, se construye un índice simple; si vale unique se construye un índice único para la columna.
- foreignTable: el nombre de una tabla, se utiliza para crear una clave externa a otra tabla.
- foreignReference: el nombre de la columna relacionada si las claves externas se definen mediante foreignTable.
- onDelete: determina la acción que se ejecuta cuando se borra un registro en una tabla relacionada. Si su valor es setnull, la columna de la clave externa se establece a null. Si su valor es cascade, se borra el registro relacionado. Si el sistema gestor de bases de datos no soporta este comportamiento, el ORM lo emula. Esta opción solo tiene sentido para las columnas que definen una foreignTable y una foreignReference.
- isCulture: valor booleano. Su valor es true para las columnas de tipo culture en las tablas de contenidos adaptados a otros idiomas.
CLAVES EXTERNAS
Además de los atributos de columna foreignTable y foreignReference, es posible añadir claves externas bajo la clave _foreignKeys: de cada tabla.
- propel:
- blog_articulo:
- id:
- titulo: varchar(50)
- usuario_id: { type: integer }
- _foreignKeys:
- -
- foreignTable: blog_usuario
- onDelete: cascade
- references:
- - { local: usuario_id, foreign: id }
La sintaxis alternativa es muy útil para las claves externas múltiples y para indicar un nombre a cada clave externa.
- _foreignKeys:
- mi_clave_externa:
- foreignTable: db_usuario
- onDelete: cascade
- references:
- - { local: usuario_id, foreign: id }
- - { local: post_id, foreign: id }
INDICES
Además del atributo de columna index, es posible añadir claves índices bajo la clave _indexes: de cada tabla. Si se quieren crean índices únicos, se debe utilizar la clave _uniques:. En las columnas que requieren un tamaño, por ejemplo por ser columnas de texto, el tamaño del índice se indica entre paréntesis, de la misma forma que se indica el tamaño de cualquier columna.
- propel:
- blog_articulo:
- id:
- titulo: varchar(50)
- created_at:
- _indexes:
- mi_indice: [titulo(10), usuario_id]
- _uniques:
- mi_otro_indice: [created_at]
COLUMNAS VACÍAS
Cuando Symfony se encuentra con una columna sin ningún valor, utiliza algo de magia para determinar su valor.
- // Las columnas vacías llamadas "id" se consideran claves primarias
- id: { type: integer, required: true, primaryKey: true, autoIncrement: true }
-
- // Las columnas vacías llamadas "XXX_id" se consideran claves externas
- loquesea_id: { type: integer, foreignTable: db_loquesea, foreignReference: id }
-
- // Las columnas vacías llamadas created_at, updated at, created_on y updated_on
- // se consideran fechas y automáticamente se les asigna el tipo "timestamp"
- created_at: { type: timestamp }
- updated_at: { type: timestamp }
Para las claves externas, Symfony busca una tabla cuyo phpName sea igual al principio del nombre de la columna; si se encuentra, se utiliza ese nombre de tabla como foreignTable.
TABLAS I18N
Symfony permite internacionalizar los contenidos mediante tablas relacionadas. De esta forma, cuando se dispone de contenido que debe ser internacionalizado, se guarda en 2 tablas distintas: una contiene las columnas invariantes y otra las columnas que permiten la internacionalización.
Todo lo anterior se considera de forma implícita cuando en el archivo schema.yml se dispone de una tabla con el nombre cualquiernombre_i18n.
- propel:
- db_group:
- id:
- created_at:
-
- db_group_i18n:
- name: varchar(50)
- propel:
- db_group:
- _attributes: { isI18N: true, i18nTable: db_group_i18n }
- id:
- created_at:
-
- db_group_i18n:
- id: { type: integer, required: true, primaryKey: true, foreignTable: db_group, foreignReference: id, onDelete: cascade }
- culture: { isCulture: true, type: varchar(7), required: true, primaryKey: true }
- name: varchar(50)
COMPORTAMIENTOS
Los comportamientos son plugins que modifican el modelo de datos para añadir nuevas funcionalidades a las clases de Propel.
- propel:
- blog_articulo:
- titulo: varchar(50)
- _behaviors:
- paranoid: { column: deleted_at }
Optimizar el servidor web Apache
1Tenía intención de crear un post explicando la optimización de Apache, pero la verdad es que en la red ya existe bastante información al respecto por tanto os dejo dos links, uno explicando cada uno de los parámetros que se pueden modificar en el archivo httpd.conf de Apache y el otro con algunos ejemplos de optimización: – Definición de parámetros: http://www.codenb.com/optimizar-apache-16/ – Ejemplos: http://www.forosdelweb.com/f58/recetas-para-configuracion-apache-404961/
“Chuletas” para Symfony
0Más de uno conocerá esas imágenes o PDFs que, de forma resumida, muestran todos los métodos y funciones de los lenguajes de programación o de frameworks. Con Symfony eso no es diferente, además de ser muy útil en las primeras etapas de aprendizaje. Os dejo el link:
http://trac.symfony-project.org/wiki/CheatSheets
¡Qué lo disfrutéis!
Symfony: El modelo (I)
0Las 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.
- <?php
-
- public function getNombreCompleto()
- {
- return $this->getNombre().' '.$this->getApellidos();
- }
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:
- <?php
-
- public function getTotal()
- {
- $total = 0;
- foreach ($this->getProductos() as $producto)
- {
- $total += $producto->getPrecio() * $producto->getCantidad();
- }
-
- return $total;
- }
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/.
- 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:
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.
- <?php
-
- class Articulo extends BaseArticulo
- {
- }
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:
- <?php
-
- $articulo = new Articulo();
-
- // ...
- $titulo = $articulo->getTitulo();
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:
- <?php
-
- // $articulos es un array de objetos de la clase Articulo



