Mostrando entradas con la etiqueta php. Mostrar todas las entradas
Mostrando entradas con la etiqueta php. Mostrar todas las entradas

martes, 9 de septiembre de 2008

Experimentado con ArgoUML y CodeIgniter

Llevaba bastante tiempo con ganas de probar Codeigniter. Después de comparar Symfony y Zend Framework hace unos cuantos meses, alguien me sugirió hacerlo. Por cierto con los avances que han tenido cada uno de estos proyectos dicha comparativa está prácticamente obsoleta.

La semana pasada empecé a trastear con CodeIgniter y volví a darle vueltas a la cuestión de que no tuviera una serie de comandos para ayudar con la generación de controladores, modelos, etc. Así que se me ocurrió utilizar mi querido ArgoUml para "pintar" las clases y después generarlas en los directorios del proyecto.

El genearador de código PHP

Entonces me di de bruces con lo que para mí es un viejo problema en la generación de clases de ArgoUML: añade el prefijo "class." al nombre de tu clase como nombre de fichero. Es decir, si tu clase se llama Clientes obtendrás un fichero de nombre class.Clientes.php. Me topé por primera vez con esto en un proyecto llevado a cabo en mi trabajo en el que la capa de presentación está hecha en Flex y el resto en PHP. Nos encontramos con la restricción, por parte de AmfPHP, de que el nombre de las clases y los ficheros que la contienen debe ser el mismo. Esta misma obligación nos la encontramos en CodeIgniter, o sea, nuestra clase Clientes deberá encontrarse en el fichero clientes.php.

Otro convenio utlizado por ArgoUML me terminaría afectado: el nombre de las clases generadas tiene antepuesto toda la cadena de paquetes de la que depende. Me explico: si nuestra clase Clientes pertenece al paquete modelo, tomará como nombre modelo_Clientes.

Estuve pensando como salvar esta dificultades pero las cosas que se me ocurrían no eran muy limpias. Finalmente opté por una solución que tampoco es como para estar orgulloso pero me pareció con bastante diferencia la mejor: recompilar el módulo generador de código PHP de ArgoUML a mi gusto.

¡Viva el código libre! Un par de cambios y a volar

Pues nada a navegar por la página de Argo y buscar el código fuente del subproyecto ArgoUML-PHP. No cuesta mucho dar con el repositorio SVN así que un checkout y ya tenía el código en mi PC.

Después de esto tengo que reconocer que la cosa fue bastante fácil, si no, os aseguro que la pereza hubiese podido conmigo. El código descargado viene con el archivo de proyecto para abrirlo en Eclipse. Nos encotraremos algún problema de dependencias con archivos .jar que podremos obtener de la propia instalación de Argo. Una vez resueltas sólo hay que abrir el fichero NameGenerator.java y localizar dos porciones de código.

La primera corresponde a la nomenclatura de las clases con el prefijo de paquetes. Hay que comentar el código tal como veis aquí:


public static final String generateClassifierName(Object modelElement) {
if (!Model.getFacade().isAClassifier(modelElement)) {
throw new ClassCastException(modelElement.getClass()
+ " has wrong object type, Classifier required");
}

String sName = Model.getFacade().getName(modelElement);

/*String sPackageName = generatePackageName(modelElement);
if (sName != null && sPackageName != null
&& sPackageName.length() > 0) {
sName = sPackageName + "_" + sName;
}*/

return sName;
}


El segundo cambio es el del nombre del fichero:


if (iMajorVersion > 4) {
if (Model.getFacade().isAInterface(modelElement)) {
sFilename += "interface.";
} else if (Model.getFacade().isAClass(modelElement)) {
sFilename += "class.";
} else {
sFilename += "unknown.";
}
}


se cambia la cadena "class." por la vacía y resuelto.

Después de hacer esto creamos nuestro .jar y sustituimos el existente en el directorio /ext de la intalación de ArgoUML. Al principio el programa no me lo cogía, al generar código no me aparecían las opciones PHP4 ni PHP5, finalmente lo resolví eliminado el archivo manifest del .jar.



Modelando

Lo primero que tendremos que hacer es imitar la parte de la estructura de CodeIgniter que nos interesa. Para ello crearemos un proyecto nuevo en Argo y después un paquete llamado models y otro llamado cotrollers. Hacemos esto para que llegados al momento de generar código asignemos como ruta nuestro path a Codeigniter/system/application y cada una de las clases vayan a su directorio correspondiente.



Aviso que necesitaremos añadir al path de PHP la ruta hasta CodeIgniter/system/application por la forma en la que Argo nos escribirá los requires entre clases.

Ya solo nos queda un quebradero de cabeza. Todas nuestras clases de controlador deberían heredar de la clase Controller de CodeIgniter pero si representamos la misma en nuestro modelo se nos creará un require de una clase que no debería existir (ya está definida en el framework). La solución que se me ocurrió para esto fue añadir una clase adaptador MiController que hereda de Controller de Codeigniter. Esta clase la generaremos sólo una vez ya que después le añadiremos en el editor "extends Controller" y si volvemos a generarla machacaremos el cambio y tendremos que volver a escribirlo. Después todas las clases controlador que creemos heredarán de MiController.


class MiController extends Controller
{
// --- ASSOCIATIONS ---


// --- ATTRIBUTES ---

// --- OPERATIONS ---

/**
* Short description of method __construct
*
* @access public
* @return mixed
*/
public function __construct()
{
// section -87--2--75--55--89caeb7:11c46a97fcc:-8000:00000000000008BA begin
parent::__construct();
// section -87--2--75--55--89caeb7:11c46a97fcc:-8000:00000000000008BA end
}

}


Lo mismo nos ocurrirá con las clases modelo pero la solución es exáctamente la misma.


class MiModelo extends model
{
// --- ASSOCIATIONS ---


// --- ATTRIBUTES ---

// --- OPERATIONS ---

/**
* Short description of method __construct
*
* @access public
* @author Depto. Web/WAP de Alvento Soluciones, <guillermo@alvento.com>
* @return mixed
*/
public function __construct()
{
// section -87--2--75--55-438df237:11c46b7f1db:-8000:0000000000000878 begin
parent::Model();
// section -87--2--75--55-438df237:11c46b7f1db:-8000:0000000000000878 end
}

} /* end of class MiModelo */


Una vez hecho esto podremos modelar tranquilamente creando nuestras clases, modificándolas en Argo y volviendo a generar el código sin perder lo que hayamos escrito en el IDE (como ya expliqué en su día)




viernes, 16 de mayo de 2008

Probando Netbenas Early Access for PHP

Llevo un par de días trabajando con Netbeans para probar el Early Access for PHP. La verdad es que no esta mal, aunque no sea la versión final no tiene mucho que envidiar a Eclipse + PDT, que es la comparación obligatoria.

La instalación es bastante sencilla y no interfiere con el NetBeans 6.0.x que ya tengo instalado para trabajar con Java. Además es una descarga ligerita, creo que no llega a los 20 megabytes.

Lo primero que he hecho es cargar algún proyecto existente, configurando en el wizzard la ruta a los fuentes. Lo normal es que te cree un subdirectorio con la información del proyecto y que tengas que retocar la ruta del servidor web para ejecutar la aplicación. Una vez hecho esto tendremos la navegación habitual de este IDE por proyecto o gestor "tradicional" de archivos, el navegador de clases, etc. Todo ello en el panel lateral izquierdo.

Además dispondremos de la paleta de objetos HTML, detección de errores, detección de código muerto (he probado a poner exit(); y return antes de algunas lineas en el interior de una función y no me ha funcionado) y más.


Todavía tienen cosas que pulir, la navegación hasta las declaraciones de funciones no va todavía muy bien, al menos en mi caso, cuando trato de llegar a una función de una clase creada por mi en un fichero distinto al que estoy trabajando. También le queda un poco al autocompletado de nuevo con clases propias ya que no me encuentra métodos estáticos ni funciones de objetos creados con anterioridad.

La integración de Xdebug también es fácil, se instala el debugger se retoca el php.ini para que pueda acceder el Netbeans y a correr, por ejemplo:
zend_extension_ts="c:/xdebug/php_xdebug-2.0.2-5.2.5.dll"
xdebug.remote_enable=1

Cuando mejoren esos pequeños aspectos estarán prácticamente a la par de PDT y ya tendremos que elegir uno a otro segun nos guste y, mas importante, en base a los plugins que queramos utilizar: SVN, diseño de BDs, UML, etc.

Yo de momento volveré a Eclipse pero estaré muy al corriente de como evoluciona esto.

miércoles, 7 de mayo de 2008

Multipeticioens con CURL

Parece que me estoy aficionando últimamente a meter el dedo en el ojo ajeno y quien me conoce sabe que tengo los dedos largos. Bromas a parte lo que me dispongo a hacer es criticar otro post de uno de esos blogs que leo con asiduidad. Tarde o temprano alguien me dará con la misma medicina.

El artículo en cuestión es este ya hablé de él y tiene su tiempo. Es cierto que a su autora le dan estopa en su propia casa y es curioso porque este tipo de cosas suelen ocurrir siempre de la misma forma: los primeros comentarios son positivos y luego se va enrareciendo la cosa y se convierten en criticas. Siempre aparece algún fanático que termina descalificando.

Tampoco voy a decir nada que ella no haya comentado:
No es un multithreading real y pude llegar a sobrecargar el servidor. De hecho esto último es lo que me frenó a la hora de adoptarlo. Mi servidor de pruebas se quedaba tostado si no intercalaba una llamada a usleep con unos cuantos microsegundos en el bucle do{}while. Por cierto la prueba consistía en que este servidor lanzase peticiones post contra si mismo.

Repito una vez más lo de siempre: hay que poner en duda las soluciones que encontremos y ponerlas a prueba en nuestro entorno. Lo que sea válido para algunos puede que no lo sea para nosotros.

martes, 29 de abril de 2008

Mi solución al problema de inserción masiva en SQL Server

El título de este artículo puede parecer un poco egocéntrico pero hay que tener en cuenta que es la continuación de este otro y que además me refiero a una solución valida al problema que planteé en el entorno de producción que me afecta.

Recapitulando un poco para el que no quiera leerse el post enlazado, el problema en cuestión era conseguir unos 50 000 inserciones en un SGBD MS SQLServer. Todo ello sin ejecutar cada vez un

INSERT INTO MiTabla ...

El caso es que dicho entorno de producción tiene una particularidad tal vez no muy deseable: el SGBD y el servidor web corren en la misma máquina. Debido a esto se pasaba por mi cabeza que tal vez un Bulk Insert fuese una buena solución pero no me lo terminaba de creer porque suponía que esa instrucción se convertiría posteriormente en mis 50 000 "insert into", con lo cual incluso perdería mas tiempo.

El código fuente que utilicé aproximadamente en mis pruebas es el siguiente:


define("MAX",100000);
define("TAM_BLOQUE",10000);
mssql_connect("miservidor","miusuario","mipass");
mssql_select_db("MiDB");


mssql_query("TRUNCATE TABLE Cola");
$t1=time();
$query="BULK INSERT Cola
FROM 'C:\\www\\pruebas\\envio.txt'
WITH
(
KEEPNULLS,
FIELDTERMINATOR = '|',
ROWTERMINATOR = '\r\n'
)";
$i=0;
$fecha=date();
while ($i < MAX){
$str=null;
while ($j < TAM_BLOQUE && $i<=MAX){
$str.="0|0|".(620000000+$i)."|".$fecha."||2|12|8|1|1|un texto mas o menos largo|hola|prueba|10|http://www.google.com|1|1|0|0|0|0|0|0|0||\r\n";
$i++;$j++;
}
$fd=fopen("envio.txt","w");
fwrite($fd,$str);
fclose($fd);
mssql_query($query);
}
$t2=time();
echo "Tiempo: ".($t2-$t1);


Como se puede observar hay dos bucles anidados para evitar hacer todo el trabajo de golpe en caso de que se superen ciertos límites. Esto es así porque durante las pruebas observe que el uso de CPU se sostenía cerca del 100% durante bastante tiempo si se mandaba un sólo archivo de golpe. Escribir varios archivos le daba oxígeno al servidor y no me hacía perder un tiempo considerable en la ejecución total del script.

Sin duda el hecho de que el Apache pudiese escribir en el disco duro que comparte con el SQL Server aceleró bastante las cosas, pero esto puede hacer que a muchos de vosotros no os sirva la solución. Si no es vuestro caso tal vez podáis montar una unidad lógica para obtener unas condiciones parecidas pero sufriréis una latencia adicional por ello (aclaro que yo no he probado esto último).


Para los más curiosos detallaré un poco el entorno de pruebas que utilicé: se trata de un servidor con un procesador Intel Pentium 4 a 2,66 GHz con 1 GB de RAM y SQL Server 2000.

Os recuerdo algo que dejé caer en el anterior post y que es la moraleja de todo esto: no os fiéis de nada que encontréis en un blog, el blogger no tiene por que ser un gurú aunque él mismo lo crea. Por supuesto que este artículo está incluido en la moraleja así que si os interesa ponedlo en duda y haced vuestras propias pruebas antes de darlo por bueno.

sábado, 5 de abril de 2008

Optimizando inserciones múltiples en SQL server y el mito del blogger gurú

Empezará por el final, por la moraleja: no hay que fiarse de todo aquél que escribe un blog y por supuesto tampoco hay que considerarlo un gurú. Esta afirmación no deja de ser paradójica ya que me incluye a mi mismo aunque ni mucho menos afirmo ser un gurú... pero en fin dejémoslo.

Imaginad que tenéis una tabla en una base de datos que se debe poblar de forma masiva con una información ya conocida que tras ser procesada mediante un script PHP o similar se materializará en miles de registros. En mySQL existe una bonita sintaxis que si no recuerdo mal es algo así como


INSERT INTO tabla (campo1,...,campoN)
(valor1_1,..., valor1_N),
...
(valorN_1,..., valorN_N)


Lamentablemente esto no existe en SQL Server que es el gestor de base de datos con el que me enfrento a diario, así que me puse a buscar en google alguna solución que me evitara lo que venimos haciendo hasta ahora:



INSERT INTO tabla (campo1,...,campoN) VALUES (valor1_1,..., valor1_N)
...

INSERT INTO tabla (campo1,...,campoN) VALUES (valorN_1,..., valorN_N)


Así es como fui a dar con un blog donde se proponía la siguiente opción como algo bastante más eficiente:


INSERT INTO tabla (campo1,...,campoN)
SELECT valor1_1,..., valor1_N
UNION ALL
SELECT valor2_1,..., valor2_N
...
UNION ALL

SELECT valorN_1,..., valorN_N


A partir de ahí un montón de comentarios al post original en plan, "¡fantástico!", "¡lo he probado con 200 registros y gano x ms!", que van degenerando poco a poco en los típicos "Lo he probado y me devuelve un error..." y la consiguiente respuesta "Has escrito mal esto o lo otro". Debido a esto y a que había más de 100 comentarios cometí mi primer error leyendo sólo una docena de ellos. El segundo fue no sospechar del operador UNION y el tercero simplemente darlo por bueno.

¿Y por qué no darlo por bueno? Era una solución escrita en un blog. Seguro que el tipo que aparece fotografiado es un gurú del SQL. Sólo había que ver el nombre del blog: SQLAuthority, como para no fiarse (leer esto imaginando la voz de Robert de Niro en Taxi Driver):
¡Joder tío! ¡Ese tipo es la puta autoridad del SQL, tío!
Tan convencido estaba de mi "descubrimiento" que escribí un mail a unos cuantos compañeros para que se aprovecharan de ello.

Afortunadamente un día después empecé a hacer algunas pruebas. A mi me interesaba que fuese rápido insertando unos 50.000 registros en una tabla con unos 20 campos, tres o cuatro de texto y el resto numéricos. Tras montar la consulta y ejecutarla en el servidor... sorpresa: el motor de la base de datos devuelve un error porque la consulta es demasiado grande. Empiezo a reducir la consulta para encontrar el límite: 25000 inserciones,10000, 5000... y así hasta que finalmente traga con 1000, bastante desesperanzador para mi propósito.

Aún así me empeñé en compararlo con el método que venía utilizando y... ¡era más lento! Tras varias pruebas siempre tardaba un par de segundos más. El método de siempre tardaba unos 8 segundos y el propuesto unos 10. Menuda decepción, además con el tradicional me ahorraba el problema del error con el tamaño de la consulta.

No me lo podía creer y la gente del blog maravillada con el invento. Volví al post y seguí leyendo. Había alguno que llegaba a las mismas conclusiones que yo pero la mayoría de la gente estaba encantada. El autor en un momento dado reconocía que existía el límite del tamaño de la consulta pero que el conseguía insertar así varios miles de registros.

En fin, a mi no me sirvió y acepto sugerencias a mi problema: quiero hacer unas 50.000 inserciones en una tabla sin tener que ejecutar 50.000 INSERTs y además que me resulte más rápido ¿alguna solución? Yo he dado con una que me sirve debido a las particularidades del entorno de producción que tenemos, o sea, no es una solución universal. La explicaré en otro post. Por cierto, envié un segundo mail a mis compañeros recomendando olvidar el primero.

domingo, 23 de marzo de 2008

Modelando con ArgoUML aplicaciones en PHP

Sobre ArgoUML

ArgoUML (Argo para abreviar) es una de esas aplicaciones que dan la impresión de no ser utilizadas por mucha gente pero que, sin embargo, continúan desarrollandose con un ritmo incansable. Para los que no lo sepan se trata de una herramienta CASE para modelar software en UML.

En mi caso elegí Argo porque quería modelar con una herramienta libre que me generase código en PHP. Además debería correr tanto en Windows como en Linux.

Pero antes de eso y después de probar unas cuantas herramientas veía difícil encontrar una aplicación que cumpliera estos tres requisitos: podía producir código PHP con Umbrello pero no corría en Windows, Visual Paradigm era hasta bonito pero no libre, etc. Finalmente llegué hasta Argo y aunque en una primer vistazo puede parecer una herramienta tosca en comparación con otras, cuando te acostumbras te das cuenta de que es bastante rápido trabajar con ella.


Para los fanáticos de Eclipse existe un proyecto paralelo llamado argoeclipse que nos permite tenerlo integrado en dicho IDE. Sin embargo este proyecto siempre va un paso por detrás de su hermano mayor y creo que cuando lo probé no generaba código PHP.


Modelando

Tenemos a nuestra disposición los diagramas de casos de uso, clases, secuencia, colaboración, estado y despliegue.


Particularmente suelo utilizar el de casos de uso para definir historias o partes del entorno de usuario, el de clases para el análisis, desarrollo y documentación.








Ocasionalmente uso el de actividades para definir algún proceso que me interesa plasmar por su complejidad y el de despliegue como apoyo de algún documento.


Dada su flexibilidad, no importa que tipo de modelado queramos hacer. Por ejemplo, para un diagrama de clases, si modelamos desde el punto de vista del diseño de aplicación podemos ocultar atributos y funciones:

Por el contrario podemos profundizar un poco más en el análisis y definir atributos y funciones de las clases:

Por último si modelamos orientado a la implementación conviene que veamos la privacidad y los parámetros que utilizamos:


También nos es muy útil para documentar independientemente del nivel al que modelemos, ya que podemos insertar documentación sobre las clases, atributos, funciones, etc. Cuando generémos el código se nos insertarán los comentarios de forma que más tarde podremos procesarlo mediante PHPDocumentor y obtener un pdf o páginas html de documentación.

En la práctica

Volviendo a los diagramas que se pueden ver más arriba imaginemos que después de definir los casos de uso y de hacer un modelado de las clases a nivel de diseño, vamos profundizando hasta el punto de vista de implementación. Llegados a este punto es el momento de generar código. Para ello nos vamos hasta nuestro diagrama de clases y elegimos en el menú Generar la opción Generar todas las clases... y elegimos las clases que queramos generar y la ruta donde se crearán los archivos.



Después de esto obtendremos un fichero por cada una de las clases, por ejemplo para la clase artículo tendremos:


if (0 > version_compare(PHP_VERSION, '5')) {
die('This file was generated for PHP 5');
}

/**
* include Comentario
*
* @author firstname and lastname of author, author@example.org
*/
require_once('class.Comentario.php');

/* user defined includes */
// section -84-16-6-101-32d97df8:117d1aeef8e:-8000:0000000000000846-includes begin
// section -84-16-6-101-32d97df8:117d1aeef8e:-8000:0000000000000846-includes end

/* user defined constants */
// section -84-16-6-101-32d97df8:117d1aeef8e:-8000:0000000000000846-constants begin
// section -84-16-6-101-32d97df8:117d1aeef8e:-8000:0000000000000846-constants end

/**
* Esta clase representa los artículos que se escriben para el blog.
*
* @access public
* @author firstname and lastname of author, author@example.org
*/
class Articulo
{
// --- ATTRIBUTES ---

/**
* Identificador único
*
* @access private
* @var Integer
*/
private $idarticulo = null;

/**
* Título del artículo
*
* @access private
* @var String
*/
private $titulo = null;

/**
* Texto del artículo
*
* @access private
* @var String
*/
private $cuerpo = null;

/**
* Fecha de creación
*
* @access private
* @var Integer
*/
private $fecha_creacion = null;

// --- OPERATIONS ---

/**
* En realidad esta función debería insertar un nuevo
* artículo en la BD pero de momento sólo tiene código de prueba
*
* @access public
* @author firstname and lastname of author, author@example.org
* @param String titulo
* @param String cuerpo
* @return Boolean
*/
public function nuevoArticulo( String $titulo, String $cuerpo)
{
$returnValue = null;

// section -84-16-6-100--4073085f:118d2dc4603:-8000:000000000000086C begin
// section -84-16-6-100--4073085f:118d2dc4603:-8000:000000000000086C end

return $returnValue;
}

} /* end of class Articulo */



Como se puede observar hay comentarios introducidos por nosotros y unas extrañas marcas que nos permitirán volver a generar código sin perder lo que hayamos escrito entre ellas. Por ejemplo añadimos la instrucción:


$returnValue=true;


o sea:


public function nuevoArticulo( String $titulo, String $cuerpo)
{
$returnValue = null;

// section -84-16-6-100--4073085f:118d2dc4603:-8000:000000000000086C begin
$returnValue=true;
// section -84-16-6-100--4073085f:118d2dc4603:-8000:000000000000086C end

return $returnValue;
}


Ahora volvemos a Argo, añadimos otra función y volvemos a generar código:



*/

if (0 > version_compare(PHP_VERSION, '5')) {
die('This file was generated for PHP 5');
}

/**
* include Comentario
*
* @author firstname and lastname of author, author@example.org
*/
require_once('class.Comentario.php');

/* user defined includes */
// section -84-16-6-101-32d97df8:117d1aeef8e:-8000:0000000000000846-includes begin
// section -84-16-6-101-32d97df8:117d1aeef8e:-8000:0000000000000846-includes end

/* user defined constants */
// section -84-16-6-101-32d97df8:117d1aeef8e:-8000:0000000000000846-constants begin
// section -84-16-6-101-32d97df8:117d1aeef8e:-8000:0000000000000846-constants end

/**
* Esta clase representa los artículos que se escriben para el blog.
*
* @access public
* @author firstname and lastname of author, author@example.org
*/
class Articulo
{
// --- ATTRIBUTES ---

/**
* Identificador único
*
* @access private
* @var Integer
*/
private $idarticulo = null;

/**
* Título del artículo
*
* @access private
* @var String
*/
private $titulo = null;

/**
* Texto del artículo
*
* @access private
* @var String
*/
private $cuerpo = null;

/**
* Fecha de creación
*
* @access private
* @var Integer
*/
private $fecha_creacion = null;

// --- OPERATIONS ---

/**
* En realidad esta función debería insertar un nuevo
* artículo en la BD pero de momento sólo tiene código de prueba
*
* @access public
* @author firstname and lastname of author, author@example.org
* @param String titulo
* @param String cuerpo
* @return Boolean
*/
public function nuevoArticulo( String $titulo, String $cuerpo)
{
$returnValue = null;

// section -84-16-6-100--4073085f:118d2dc4603:-8000:000000000000086C begin
$returnValue=true;
// section -84-16-6-100--4073085f:118d2dc4603:-8000:000000000000086C end

return $returnValue;
}

/**
* Esta función debería borrar el artículo identificado mediante id.
*
* @access public
* @author firstname and lastname of author, author@example.org
* @param Integer id
* @return mixed
*/
public function borraArticulo( Integer $id)
{
// section -84-16-6-100--4073085f:118d2dc4603:-8000:000000000000088C begin
// section -84-16-6-100--4073085f:118d2dc4603:-8000:000000000000088C end
}

} /* end of class Articulo */

?>


¡Nuestro código sigue ahí!

Por último pasamos el proyecto por PHPDocumentor y obtenemos la documentación correspondiente.



Y eso es básicamente todo, si trabajáis en Web con PHP, os gusta modelar y tenéis tiempo para ello, ArgoUML es una gran elección.

miércoles, 19 de marzo de 2008

Habemus facebook

Llevo un par de semanas trasteando con facebook por culpa, en parte, de mi amigo Yorch. La aplicación en si es bastante difícil de manejar, te pierdes un poco por sus menús hasta que te acostumbras pero una vez lo haces la verdad es que es bastante completa.

El caso es que hace unos cuantos días estaba curioseando por los grupos y me llevé la sorpresa de no encontrar más que un par de grupos sobre PHP en español y además vinculados a la red "Spain" y, en su nombre, a ciudades. Así que se me ocurrió la ¿genial? idea de crear el grupo PHP y desarrollo web en español por supuesto en la red global.

Estoy publicando allí enlaces a posts de este blog que me parecen relevantes y aprovecho para invitar a todos a que hagan lo mismo ya que la publicación está permitida a todos, claro que es necesario tener cuenta en facebook.

lunes, 17 de marzo de 2008

Zend Technologies publica Zend Framework 1.5

Desde hace un par de días se venía rumoreando que estaba próximo el lanzamiento de ZF 1.5 que finalmente se ha producido hoy.

Si se lee el comunicado observamos como se nombran unas cuantas grandes marcas que están utilizando este framework y cómo se está potenciando la integración de los APIs de Google. Todo parece indicar que se trata de una estrategia de Zend Technologies para atraer a los desarrolladores.

Sin embargo el producto sigue siendo inferior a otros competidores en cuanto a potencia y sobre todo comunidad. Sigo pensando que Zend debe potenciar este último factor con acciones como integrar ZF en PDT.


Technorati Tags: , , ,

miércoles, 12 de marzo de 2008

Symfony vs Zend Framework. 4- ¿Cuál nos quedamos?

Artículos de la serie:
  1. Definición del problema
  2. Comencemos con Symfony
  3. Turno de Zend Framework
  4. ¿Cuál nos quedamos?
Después de que en la primera parte definieramos los requisitos de una pequeña aplicación y las implementásemos en los dos siguientes capítulos con los correspondientes frameworks, llega el momento de sacar unas cuantas conclusiones. Recordemos que el punto de vista de esta comparación es el de alguien que sólo ha dedicado unas cuantas horas a cada uno de los dos, así que debemos ser conscientes de que solo hemos visto la punta del iceberg y que tal vez por ello las conclusiones no sean del todo justas.

Forma de trabajo

En primer lugar hay ciertas diferencias a la hora de trabajar con ellos. Symfony hace uso de la linea de comandos para facilitarnos muchas tareas monótonas mintras que ZF todavía no lo incorpora. Cuando este último posea esta carácterística habrá que analizar si realmente equivaldría al otro en cuanto a funcionalidades. Por otro lado en Symfony tendremos que crear y retocar archivos YAML para ciertas cosas como validar formularios y como configuración de diversos aspectos.

A parte de esto, la forma de trabajar será muy parecida debido a que ambos responden a un patrón MVC y tendremos que recurrir continuamente a modificar clases relativas a acciones, revisar archivos plantilla, etc.

Curva de aprendizaje

Aquí toma clara ventaja ZF porque símplemente hemos de documentarnos a través de la guía de referencia para conocer las clases existentes y sus funciones. En Symfony, además de lo anterior, tendremos que familiarizarnos con Yaml, averiguar que podemos hacer y que no con los comandos de CLI y manejarnos por diferentes archivos de configuración, lo que implica familiarizarse con la estructura de archivos del proyecto.

La guerra de los plugins

Es posible encontrar una gran cantidad de plugins no oficiales para Symfony los cuales se pueden instalar, como no, haciendo uso de la linea de comando. Por otro lado ZF no distingue como plugins una serie de clases que tendrían que ver con la utilización de APIs externos, ya que forman parte de la distribución, pero sí que los identifica como servicios (Zend_Service_Amazon, Zend_Service_Flickr, etc) Es posible que nuestra elección deba centrarse en este aspecto si queremos comenzar un proyecto de tipo mashup y aquí Symfony tiene ventaja por la gran cantidad de recursos que pone a nuestra disposición.

Comandos sí o no

ZF necesita incorporar los comandos de CLI para llegar a la altura de Symfony, deberían darle un empujón definitivo a ese aspecto. De todos modos hay partidarios de no utilizarlos así que este hecho puede ser determinante para la gente que se posicione al respecto.

Otros aspectos

Otros puntos a favor de Symfony, aunque no he llegado a probarlos, son el soporte que proporciona para tests unitarios y funcionales, la integración de subversion y la sincronización de código entre los entornos de desarrollo, pruebas y producción.

Conclusiones

Desde mi punto de vista Symfony goza de ventaja sobre ZF por poseer los comandos de CLI , la gran cantidad de plugins exsitentes, el entorno de desarrollo con el menú flotante de información de debug, soporte para tests, etc.

Además de todo esto puede incluir a ZF como una biblioteca de clases. De hecho puede hacer esto mismo con otros frameworks. Para incluir ZF haríamos esto:

Declarar en settings.yml
.settings:
zend_lib_dir: /usr/local/zend/library/
Después extenderíamos la rutina autoload
.settings:
autoloading_functions:
- [sfZendFrameworkBridge, autoload]

Además Symfony cuenta con una gran comunidad que a parte de los plugins puede ofrecer algún extra interesante como Symfoclipse que nos permite usar los comandos de este a través de Eclipse en un menú contextual.



Por contra ZF goza de la simplicidad de aprendizaje que hemos comentado anteriormente, la libertad a la hora de definir nuestras estructuras de archivos y la flexibilidad a la hora de instanciar clases que comentamos en el capítulo precedente.

Algo que podría hacer Zend Technologies para impulsar su framework sería integrarlo con el entorno PDT de Eclipse. Estaría muy bien encontrar en el menú de creación de proyectos algo así como "Nuevo proyecto Zend Framework" y que al elegirlo nos creara una estructura de archivos inicial para adelantarnos trabajo, además, es algo que ya incorpora Zend Studio. En segundo lugar, por supuesto, tiene que potenciar como sea a la comunidad de desarrolladores para que creen mas "clases servicio". Es cierto que están haciendo un gran esfuerzo y que ZF crece rápidamente, está ganando muchos adeptos y por tanto están surgiendo muchas webs que lo utilizan.


Estamos ante dos rivales destinados a luchar una larga batalla y aunque, para mí, uno tenga ventaja, estoy seguro de que el otro irá recortando la diferencia. Deberemos elegir uno u otro en función de las situación que nos encontremos: sí se desea simplicidad y no perder mucho tiempo en la fase de aprendizaje ZF será lo más apropiado. Por el contrario, Symfony es mucho más completo y dispone de muchos, muchos más plugins. Hoy por hoy si tuviera que escoger uno de ellos elegiría Symfony pero ya veremos dentro de unos meses.

viernes, 7 de marzo de 2008

Descubriendo Eclipse PDT

Hasta hace un par de años iba cambiando de entorno de programación PHP cada pocos meses. La idea era encontrar uno satisfactorio que fuese libre pero ninguno me gustaba.

Casi todo el que conocía trabajaba con Dreamweaver y, normalmente, haciendo uso de una licencia no muy legal. Siempre he intentado encontrar herramientas libres para el trabajo diario y personal pero eso es otra historia. Así llegue hasta PHPDesigner el cual ya se acercaba bastante a lo que quería. Si no recuerdo mal la versión 2005 era una demo que se podía usar ilimitadamente pero lamentablemente cada nueva versión estaba más restringida en su uso.

Finalmente di con el proyecto PDT de Zend (de acuerdo que fonéticamente, en español, el nombre no es muy acertado pero lo pasaremos por alto) Se trata de un plugin para Eclipse que nos permite usar este IDE para programar nuestras aplicaciones en PHP. Además tiene un hermano mayor, eso si, de pago en versión plugin o como aplicación independiente: Zend Studio.

Centrándonos en PDT, el resultado es muy bueno: tiene su explorador de clases y funciones, puedes integrar debuggers, el manual online o descargado de PHP, etc. Además hay pequeñas cosas muy interesantes de esas a las que te acostumbras y ya no puedes vivir sin ellas: autocompletado de funciones, detección de errores sintácticos y algo tan cómodo como que te lleve a la definición de una funcion haciendo ctrl+click en una llamada a la misma, aunque esté en otro fichero.

Cuando escribamos la cabecera de una nueva función veremos como se nos crean justo encima de ella unas líneas de comentario que documentan nuestra función y sus parámetros así que si, posteriormente, lo procesamos con PHPDocumentor tendremos un bonito manual de nuestro proyecto.

Volviendo al autocompletado, no sólo reconoce las funciones propias del lenguaje sino que además, si en alguno de nuestros ficheros hemos creado una clase, nos autocompletará las funciones de la misma cuando la instanciemos en otro archivo. Por cierto podemos forzar el autocompletado con ctrl+espacio.


Por otro lado heredamos todas las funcionaliades propias de Eclipse como por ejemplo el marcar una línea de fichero con un "TODO".

En fin que para todo aquel que no se vea obligado a meterse en tareas de diseño y maquetado html la elección es clara en favor de PDT y además es software libre.


Technorati Tags: , , , ,

sábado, 1 de marzo de 2008

Symfony vs Zend Framework. 3- Turno de Zend Framework

Artículos de la serie:
  1. Definición del problema
  2. Comencemos con Symfony
  3. Turno de Zend Framework
En el capítulo anterior vimos como implementar nuestra aplicación mediante Symfony, ahora llega el turno de Zend Framework (ZF)

ZF es bastante reciente y ha tenido un desarrollo muy intenso desde su concepción. En el momento de escribir estas líneas tenemos disponible la RC1 de la versión 1.5. Una de las novedades anunciadas a finales del año pasado fue la inclusión de comandos para facilitar la creación del modelo de al aplicación, etc. pero parece que finalmente no va a estar disponible o, al menos no he encontrado referencias posteriores sobre ello.

Documentándonos

Como siempre lo primero es buscar información. ZF no se queda cojo en cuanto a recursos en este aspecto: podemos recurrir a la web oficial, donde encontraremos accesos a la documentación del API, la guía de referencia, etc. También es interesante vigilar Zend developer Zone ya que podremos encontrar tutoriales sobre el framework.

Como nota curiosa destacar que muchas de las personas envueltas en su desarrollo cuentan con blogs interesantes en los que abordan, por ejemplo, características futuras.

De todos modos para nuestro experimento nos hemos basado en un tutorial bastante interesante en el que se ejemplifica como hacer una herramienta en la que dar de alta álbumes musicales. Lo interesante es que hasta ahora este tutorial se a venido actualizando con cada nueva versión de ZF. En la actualidad se corresponde con la versión 1.4.5.

http://akrabat.com/wp-content/uploads/getting-started-with-the-zend-framework_145.pdf


Preparativos

Antes de comenzar con el desarrollo tenemos que asegurarnos que nuestro apache tenga cargado modulo rewrite y permita el uso del mismo, así que buscaremos por nuestro httpd.conf y tal vez tengamos que descomentar la linea en la que aparece:

LoadModule rewrite_module modules/mod_rewrite.so (dependiendo de la version de apache)
También nos aseguraremos de tener la directiva AllowOverride All

Siguiendo el tutorial mencionado, vamos creando los directorios necesarios con los archivos .htaccess que básicamente nos serviran para prohibir el acceso al directorio o, en otros casos, para desactivar el modulo rewrite de Apache.

Base de datos

Nuestra base de datos fue creada durante el capítulo anterior así que nos ahorraremos este paso. De todos modos, en caso cotnrario el proceso sería el mismo al que la mayoría estamos acostumbrados: optaríamos por diseñar en phpMyAdmin, Mysql Workbench o similar.

Bootstrap

De nuevo siguiendo el tutorial no tenemos mas que copiar un poco de código para llegar a tener nuestro index.php despachando todas las peticiones en el directorio raíz de nuestra aplicación. En nuestro caso finalmente quedo así:
<?php
error_reporting
(E_ALL|E_STRICT);
date_default_timezone_set('Europe/London');
set_include_path('.' . PATH_SEPARATOR . './library'

. PATH_SEPARATOR . './application/models/'. PATH_SEPARATOR . get_include_path());
include
"Zend/Loader.php";
Zend_Loader::loadClass('Zend_Controller_Front');

Zend_Loader::loadClass('Zend_Config_Ini');
Zend_Loader::loadClass('Zend_Registry');
Zend_Loader::loadClass('Zend_Db');

Zend_Loader::loadClass('Zend_Db_Table');
// load configuration
$config = new Zend_Config_Ini('./application/config.ini', 'general');

$registry = Zend_Registry::getInstance();
$registry->set('config', $config);
// setup database
$db = Zend_Db::factory($config->db->adapter,
$config->db->config->toArray());
Zend_Db_Table::setDefaultAdapter($db);

// setup controller
$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setBaseUrl('/zf/bloggart/');
$frontController->setControllerDirectory('./application/controllers');
// run!
$frontController->dispatch();


Vemos que queda estructurado en una primera zona donde se cargan las clases a utilizar mediante la instrucción

Zend_Loader
::loadClass('miclase');


A continuación se carga y se registra un pequeño archivo de configuración (config.ini) que en nuestro caso sólo inicializa el acceso a la base de datos.

$config = new Zend_Config_Ini('./application/config.ini', 'general');

En tercer lugar viene la instanciación de la base de datos

$db = Zend_Db::factory($config->db->adapter,
$config->db->config->toArray());
Zend_Db_Table::setDefaultAdapter($db);


Después se prepara el ojeto controlador de la aplicación indicado el directorio raíz de la misma y el directorio donde encontraremos los archivos controloadores.

$frontController = Zend_Controller_Front::getInstance();
$frontController->throwExceptions(true);
$frontController->setBaseUrl('/zf/bloggart/');
$frontController->setControllerDirectory('./application/controllers');


Por ultimo escribiremos la instrucción que despacha las peticiones a nuestra aplicación.

$frontController->dispatch();

Hasta aquí podríamos haber agradecido algún comando que nos hiciera casi todo esto ya que parece algo rutinario y mecánico. Por otro lado, al no tener esa ayuda, obtenemos una gran flexibilidad a la hora de definir nuestra estructura de directorios.

Modelo y acciones

Nuestro modelo se encontrará en /application/models y tendrá dos archivos: Articulo.php y Comentario.php Una vez más nadie habrá creado por nosotros esos archivos pero para nuestros propósitos serán muy simples. En el caso de Articulo.php será:

<?php
class Articulo extends Zend_Db_Table
{
protected
$_name = 'blog_articulo';
}
?>

y Comentario.php será análogo.

Básicamente estamos creando las clases que contendrán todos los datos de las tablas de la base de datos del mismo nombre. Conseguimos esto haciendo que hereden de la clase Zend_Db_Table que por supuesto es nativa de ZF.

En cuanto a las acciones, quedará definidas dentro de /application/controllers/ Para el ejemplo se han creado IndexController.php y ComentarioController.php. El primero corresponde a las acciones que muestran, crean, modifican y borran artículos. El segundo hace el mismo papel pero para los comentarios. Como ejemplo aquí está el código de las acciones del index:
<?php
class IndexController extends Zend_Controller_Action
{
function
init()
{
$this->view->baseUrl = $this->_request->getBaseUrl();
Zend_Loader::loadClass('Articulo');
}
function
indexAction()
{
$this->view->title = "Janfri Bloggart";
$articulo = new Articulo();
$this->view->articulos = $articulo->fetchAll(null,'idarticulo desc');
}

function
addAction()
{
$this->view->title = "Añadir artículo nuevo";
if (
$this->_request->isPost()) {
Zend_Loader::loadClass('Zend_Filter_StripTags');
$filter = new Zend_Filter_StripTags();
$titulo = $filter->filter($this->_request->getPost('titulo'));
$titulo = trim($titulo);
$cuerpo = trim($filter->filter($this->_request->getPost('cuerpo')));
if (
$titulo != '' && $cuerpo != '') {
$data = array(
'titulo' => $titulo,
'cuerpo' => $cuerpo,
);
$articulo = new Articulo();
$articulo->insert($data);
$this->_redirect('/');

return;
}
}

$articulo = new Articulo();
$this->view->articulo = $articulo->createRow();
// additional view fields required by form
$this->view->action = 'add';
$this->view->buttonText = 'Add';
}

function
editAction()
{
$this->view->title = "Editar artículo";
$articulo = new Articulo();
if (
$this->_request->isPost()) {
Zend_Loader::loadClass('Zend_Filter_StripTags');
$filter = new Zend_Filter_StripTags();
$idarticulo = (int)$this->_request->getPost('idarticulo');
$titulo = $filter->filter($this->_request->getPost('titulo'));
$titulo = trim($titulo);
$cuerpo = trim($filter->filter($this->_request->getPost('cuerpo')));
if (
$idarticulo !== false) {
if (
$titulo != '' && $cuerpo != '') {
$data = array(
'titulo' => $titulo,
'cuerpo' => $cuerpo,
);
$where = 'idarticulo = ' . $idarticulo;
$articulo->update($data, $where);
$this->_redirect('/');
return;
} else {
$this->view->articulo = $articulo->fetchRow('idarticulo='.$idarticulo);
}
}
} else {
// album id should be $params['id']
$idarticulo = (int)$this->_request->getParam('id', 0);

if (
$idarticulo > 0) {
$this->view->articulo = $articulo->fetchRow('idarticulo='.$idarticulo);
}
}
// additional view fields required by form
$this->view->action = 'edit';
$this->view->buttonText = 'Update';
}

}

Las plantillas

Ahora convertiremos nuestro único archvo HTML en plantillas .phtml que colocaremos en la ruta /application/views/ El trabajo a realizar es muy parecido al que se hizo en el capítulo anterior:
  • Preparamos dos plantillas para la cabecera y el pie de cada página.
  • Una plantilla para el bloque lateral derecho que en una aplicación real tendríamos que trabajar más, pero aquí queda como código estático.
  • Para organizarnos creamos un directorio para las plantillas relacionadas con los artículo y otro para los comentario. En cada uno de ellos tendremos index.phtml, add.phtml, edit.phtml y delete.phtml. Además las plantillas de creación y edición llaman a otra auxiliar que contiene única y exclusivamente el html relativo al formulario.
Como muestra aquí tenemos:

index.phtml
<?php echo $this->render('header.phtml'); ?>
<div id="content">
<?php foreach($this->articulos as $articulo) : ?>
<div class="post">
<h2 class="title"><a href="#"><?php echo $this->escape($articulo->titulo);?></a></h2>
<div class="entry"><?php echo $this->escape($articulo->cuerpo);?></div>
<p class="meta"><span class="posted">Posted by <a href="#">Someone</a>
on December 17, 2007</span> <a href="<?php echo $this->baseUrl; ?>/index/edit/id/<?php echo $articulo->idarticulo;?>" class="permalink">Editar</a>
<a href="<?php echo $this->baseUrl; ?>/comentario/index/id/<?php echo $articulo->idarticulo;?>" class="comentarios">Commentarios</a></p>
</div>
<a href="<?php echo $this->baseUrl; ?>/index/delete/id/<?php echo $articulo->idarticulo;?>">Delete</a>
<?php endforeach; ?></div>
<!-- end #content -->
<?php echo $this->render('lateral.phtml'); ?>
<p><a href="<?php echo $this->baseUrl; ?>/index/add">Añadir nuevo artículo</a></p>
<?php echo $this->render('footer.phtml'); ?>


Como se puede observar usamos $this-&gt;render($plantilla); para incluir el código de otra plantilla.

add.phtml que es exáctamente igual que edit.phtml



<?php echo $this->render('header.phtml'); ?>
<div id="content">
<h2><?php echo $this->escape($this->title); ?></h2>
<?php echo $this->render('index/_form.phtml'); ?>
</div>
<!-- end #content -->
<?php echo $this->render('lateral.phtml'); ?>
<?php
echo $this->render('footer.phtml'); ?>

y por último _form.phtml

<form action="<?php echo $this->baseUrl ?>/index/<?php
echo $this->action; ?>" method="post">
<div>
<label for="artist">Título</label>
<input type="text" name="titulo"
value="<?php echo $this->escape(trim($this->articulo->titulo));?>"/>
</div>
<div>
<label for="title">Cuerpo</label>
<textarea name="cuerpo" cols="60" rows="30">
<?php echo $this->escape($this->articulo->cuerpo);?>
</textarea>
</div>
<div id="formbutton">
<input type="hidden" name="idarticulo" value="<?php echo $this->articulo->idarticulo; ?>" />
<input type="submit" name="add"
value="<?php echo $this->escape($this->buttonText); ?>" />
</div>
</form>



Conclusiones

Una de las cosas que más me ha llamado la atención sobre ZF es su capacidad para ofrecer alternativas para hacer una misma cosa. Por ejemplo nos encontraremos con que podremos instanciar clases llamando en su constructor a unos cuantos parámetros o sustituyendo a estos por un único parámetro en forma de array asociativo que habremos preparado previamente.

La desventaja de no estar asistidos por comandos de shell se convierte en un punto a favor de la flexibilidad de la jerarquía de archivos, es decir, tendremos total poder de decisión para estructurar los archivos de nuestra aplicación.

Al final la forma de trabajo termina siendo bastante parecida a nuestro caso anterior, nos queda todo bastante estructurado según los principios del patrón MVC.

De nuevo nos quedamos con las sensación de haber rascado superficialmente algo que podría llegar a ser muy potente en caso de tener que hacer una aplicación más compleja.