<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tutoriales de Desarrollo y Diseño Web &#124; Web.Ontuts &#187; Tutoriales</title>
	<atom:link href="http://web.ontuts.com/category/tutoriales/feed/" rel="self" type="application/rss+xml" />
	<link>http://web.ontuts.com</link>
	<description>Tutoriales y Recursos Web de Calidad en Español</description>
	<lastBuildDate>Tue, 07 Sep 2010 10:41:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>contentEditable: Contenido editable en HTML5</title>
		<link>http://web.ontuts.com/tutoriales/contenteditable-contenido-editable-en-html5/</link>
		<comments>http://web.ontuts.com/tutoriales/contenteditable-contenido-editable-en-html5/#comments</comments>
		<pubDate>Fri, 20 Aug 2010 10:14:38 +0000</pubDate>
		<dc:creator>Adrián Mato</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[contentEditable]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[html5]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=5116</guid>
		<description><![CDATA[Explicamos el uso y funcionamiento de uno de los nuevos atributos que acompañan a HTML5: contentEditable. Los navegadores más modernos soportan esta especie de "rich text box" de forma que el usuario pueda editar contenido HTML "in situ".


No related posts.]]></description>
			<content:encoded><![CDATA[<h3>¿Qué es contentEditable?</h3>
<p>Para ser exactos existen dos elementos similares envueltos: <strong>designMode</strong> y <strong>contentEditable</strong>, similares pero con distintos objetivos.</p>
<p>El atributo <strong>designMode</strong> está enfocado en la edición de todo el documento, por tanto podríamos decir que es un editor HTML al uso con el que editar todo un documento.</p>
<p>Por otra parte <strong>contentEditable</strong> sólo permite editar los elementos que tengan este atributo asignado dentro de un documento, además de los elementos &#8220;hijo&#8221; contenidos dentro del &#8220;padre&#8221;.</p>
<blockquote><p>Los atributos <strong>designMode</strong> y <strong>contentEditable</strong> se emplean para activar el modo edición en documentos enteros o elementos concretos respectivamente.</p></blockquote>
<p>Si bien actualmente existen multitud de <strong>editores &#8220;WYSIWYG&#8221;</strong> (What You See Is What You Get / lo que ves es lo que obtienes), es de esperar que con el avance y estandarización de HTML5, estos vayan quedando en un segundo plano en pro de la integración nativa con los navegadores y su modo de edición.</p>
<h3>Aprendiendo a utilizar contentEditable en HTML5</h3>
<p>Como hemos mencionado en la introducción, estamos tratando con un <strong>atributo asignable</strong> a distintas <strong>etiquetas</strong> HTML, por lo que el uso es realmente trivial como podemos ver en el siguiente ejemplo:</p>
<pre name="code" class="html">
&lt;!DOCTYPE html>
&lt;html>
&lt;head>
	&lt;meta charset="utf-8" />
	&lt;title>contentEditable: Contenido editable en HTML5&lt;/title>
	&lt;link rel="stylesheet" href="main.css" />
&lt;/head>
&lt;body>
	&lt;article>
		&lt;header>
			&lt;h1>Contenido Editable con HTML5&lt;/h1>
		&lt;/header>

		&lt;section>
			&lt;p>
				Mediante el atributo "contentEditable" podremos convertir nuestros elementos en contenido editable, al más puro estilo richtextbox.
				Haz &lt;strong>click para editar&lt;/strong>.
			&lt;/p>
		&lt;/section>

		&lt;section>
			&lt;h2>Tareas a realizar la próxima semana:&lt;/h2>
			&lt;ol contentEditable="true">
				&lt;li>Redactar nuevos tutoriales para &lt;a href="http://web.ontuts.com">Ontuts&lt;/a>.&lt;/li>
				&lt;li>Dominar el mundo con &lt;a href="http://erasmusu.com">Erasmusu.com&lt;/a>.&lt;/li>
				&lt;li>Sacar a pasear a Coki.&lt;/li>
				&lt;li>Introduce &lt;em>siguiente tarea&lt;/em> aquí...&lt;/li>
			&lt;/ol>
		&lt;/section>

	&lt;/article>
&lt;/body>
&lt;/html>
</pre>
<p>Basta con asignar el atributo <strong>editableContent=&#8221;true&#8221;</strong> a la lista ordenada para poder editar los elementos actuales e incluso <strong>introducir más elementos</strong> a la misma.</p>
<h4>El código CSS</h4>
<p>Como curiosidad he querido resaltar algún que otro selector que a más de uno puede resultarle confuso o nuevo:</p>
<pre name="code" class="css">
[contenteditable]{
	border: 1px dotted transparent;
}
[contenteditable]:hover{
	background: #fff7c3;
	border: 1px dotted #f0df6c;
}
section > *{
	margin: 1em;
}
</pre>
<p>Empleamos <strong>[contenteditable]</strong> y <strong>[contenteditable]:hover</strong> para asignarle estilo a los elementos que tengan activo el modo edición, tanto para su estado normal como para cuando el usuario pasa el cursor por encima de los mismos. No es espetacular pero ayuda a orientar al usuario:</p>
<p><center><a href="http://web.ontuts.com/wp-content/uploads/tutoriales/contenteditable/"><img src="http://web.ontuts.com/wp-content/uploads/2010/08/contenteditable_img.png" alt="Previa del tutorial" /></a></center></p>
<p>Para los más inexpertos en HTML5 simplemente comentarles que las etiquetas <strong>&lt;header></strong> y <strong>&lt;section></strong> son nuevas y simplemente se emplean para separar dentro de un artículo <strong>&lt;article></strong> con cabecera y secciones del mismo.</p>
<h3>¿Qué navegadores lo soportan?</h3>
<p>Como ocurre con la mayoría de novedades de HTML 5, los navegadores más recientes las soportan &#8220;sin problemas&#8221;:</p>
<ul>
<li><strong>Firefox 3</strong>.</li>
<li><strong>Google Chrome</strong>.</li>
<li><strong>Internet Explorer</strong> (desde la 5.5).</li>
<li><strong>Opera 9</strong>.</li>
<li><strong>Safari 3</strong>.</li>
</ul>
<p>Y como más de uno habrá podido deducir con las comillas anteriores &#8220;sin problemas&#8221;&#8230; <i><strong>el demonio está en los detalles</strong></i>, por lo que os aconsejo esta <a href="http://www.quirksmode.org/dom/execCommand/">demo online con la que probar los distintos navegadores</a>.</p>
<h3>Como curiosidad acerca de sus orígenes&#8230;</h3>
<p>Sorprendentemente estos dos atributos (designMode y contentEditable) fueron originalmente diseñados e <strong>implementados</strong> por <strong>Microsoft</strong> con la versión <strong>Internet Explorer 5.5</strong>, aunque estaban explicados con una buena documentación en la realidad carecían de una buena funcionalidad, eran bastante engorrosos.</p>
<h3>Conclusión final</h3>
<p>Nada más conocer esta nueva característica me he parado a pensar en las horas que hemos perdido muchos desarrolladores en tratar de generar una &#8220;rich text box&#8221; en condiciones y todas las trabas e incompatibilidades que nos hemos encontrado a lo largo del proceso&#8230; ojalá que por fin estos atributos nos ayuden y faciliten un poco la vida, tanto a nosotros como al propio usuario final.</p>
<p>Me viene a la cabeza <a href="http://www.erasmusu.com/es">Erasmusu</a>&#8230; en su día implementáramos nuestro propio rich text box con emoticonos, negrita, cursiva, etc, para los comentarios y foros y la verdad es que fue un dolor de cabeza contínuo. Al final optamos por el texto plano enriquecido de toda la vida <img src='http://web.ontuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>¡Nos vemos en la próxima publicación!</p>


<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/contenteditable-contenido-editable-en-html5/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Drag &amp; Drop en HTML 5</title>
		<link>http://web.ontuts.com/tutoriales/drag-drop-en-html-5/</link>
		<comments>http://web.ontuts.com/tutoriales/drag-drop-en-html-5/#comments</comments>
		<pubDate>Tue, 10 Aug 2010 08:31:26 +0000</pubDate>
		<dc:creator>Iván Guardado</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[interfaces]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=4911</guid>
		<description><![CDATA[Hola a todos, retomamos de nuevo los tutoriales de nuestro querido javascript, esta vez vamos a ver una nueva funcionalidad que nos trae HTML5. Veremos como hacer arrastables o contenedores a los cuales arrastrar cualquier tipo de elemento.


No related posts.]]></description>
			<content:encoded><![CDATA[<h3>Introducción</h3>
<p>Una característica bastante utilizada en aplicaciones con una interfaz de usuario enriquezida es la de poder arrastrar los elementos. Ejemplos típicos son el de reorganizar los elementos de una web para personalizarla o mover un elemento a una determinada categoría.</p>
<p>Hasta ahora la forma más fácil de hacer esto era usar una librería de Javascript que nos facilitase el trabajo. La jQuery UI, que es la que más me conozco, estaba muy avanzada en este tema. Sin embargo, al ser una implementación totalmente basada en Javascript, tenía bastantes inconvenientes como:</p>
<ul>
<li>Si mueves <strong>demasiado rápido</strong> los elementos, el elemento arrastrado se suele quedar atrás y se pierde en &#8220;dragging&#8221;.</li>
<li>Solo puedes <strong>mover</strong> elementos <strong>dentro de la misma web</strong>, no puedes arrastrar elementos de una ventana a otra ni aceptar elementos que no sean DOM.</li>
<li><strong>Menos eficiencia</strong> al requerir más código.</li>
</ul>
<h3>Novedades en HTML 5</h3>
<p>Los creadores del estándar HTML5, parece que han visto que esta característica es importante y que necesita ser mucho más exprimida para dar mucho más juego a las aplicaciones webs. A continuación os cito las principales novedades del estándard en este tema:</p>
<ul>
<li>Nuevos <strong>eventos</strong> en el DOM: dragstart, drag, dragenter, dragover, dragleave, drop, dragend.</li>
<li>Nuevo <strong>atributo</strong> para los elementos DOM para hacerlo <strong>arrastrable</strong>: draggable=&#8221;true&#8221;.</li>
<li>Se permite <strong>adicionar información</strong> en el elemento arrastrable para que el contenedor pueda recibirla.</li>
<li>Posibilidad de <strong>establecer</strong> la <strong>imagen &#8220;ghost&#8221;</strong> mostrada mientras se arrastra.</li>
<li>Posibilidad de añadir <strong>efectos</strong> del estilo: copiar, mover, vínculo,  etc&#8230;</li>
</ul>
<p>Además, esta característica va más allá de la propia web, dándonos la posiblidad de arrastrar elementos entre distintas webs e incluso aceptar elementos de otras aplicaciones como arrastrar un texto de un procesador de texto o un fichero del sistema.</p>
<h3>Los nuevos eventos</h3>
<p>Vamos a listar los nuevos eventos que nos trae HTML5 para drag &amp; drop y luego veremos un ejemplo de implementación para que veáis lo fácil y rápido que se implementa.</p>
<h4>dragstart</h4>
<p>Comienza el arrastrado. El &#8220;target&#8221; del evento será el elemento que está siendo arrastrado.</p>
<h4>drag</h4>
<p>El elemento ha sido movido. El &#8220;target&#8221; del evento será el elemento arrastrado.</p>
<h4>dragenter</h4>
<p>Se dispara cuando un elemento que está siendo arrastrado entra en un contenedor. El &#8220;target&#8221; del evento será el elemento contenedor.</p>
<h4>dragover</h4>
<p>El elemento ha sido movido dentro del contenedor. El &#8220;target&#8221; del evento será el elemento contenedor. Como el comportamiento por defecto es denegar el &#8220;drop&#8221;, la función debe retornar el valor &#8220;false&#8221; o llamar a &#8220;preventDefault&#8221; para que indicar que se puede el soltar elemento.</p>
<h4>dragleave</h4>
<p>El elemento arrastrado ha salido del contenedor. El &#8220;target&#8221; del evento será el elemento contenedor.</p>
<h4>drop</h4>
<p>El elemento arrastrado has sido éxitosamente soltado en el elemento contenedor. El &#8220;target&#8221; del evento será el elemento contenedor.</p>
<h4>dragend</h4>
<p>Se ha dejado de arrastrar el elemento, con éxito o no. El &#8220;target&#8221; del evento será el elemento arrastrado.<br />
Vamos a ver un ejemplo de como hacer un elemento arrastable usando jQuery.</p>
<pre name="code" class="javascript">
$('#mydrag')
    .attr('draggable', 'true')
    .bind('dragstart', function(ev) {
        var dt = ev.originalEvent.dataTransfer;
        dt.setData("Text", "Información adicional");
        return true;
    })
    .bind('dragend', function(ev) {
        return false;
    });
</pre>
<p>Y ahora la otra parte, creamos un contenedor que esté a la espera de elementos arrastrables.</p>
<pre name="code" class="javascript">
$('#mycontainer')
    .bind('dragenter', function(ev) {
        //Cambiamos color del contenedor para mostrar que acepta al elemento
        $(ev.target).css('background','#F00');
        return false;
    })
    .bind('dragleave', function(ev) {
       //Reestablecemos el color del contenedor
        $(ev.target).css('background','#FFF');
        return false;
    })
    .bind('dragover', function(ev) {
        //Importante como hemos dicho para que acepte al elemento,
        //por defecto cancela el drop
        return false;
    })
    .bind('drop', function(ev) {
        var dt = ev.originalEvent.dataTransfer;
        alert(dt.getData('Text'));
        return false;
    });
});
</pre>
<p>Creo que el código no require de mucha explicación pues simplemente establece las funciones para cada evento, aunque creo que es importante reseñar que gracias al objeto <strong>dataTransfer</strong> se puede compartir información entre el elemento arrastrado y el contenedor, lo veremos a fondo a continuación.</p>
<h3>Transfiriendo información con dataTransfer</h3>
<p>Como ya hemos dicho, una de las características más importantes de esta nueva implementación, es la posibilidad de cargar información en el elemento arrastrado para que el contenedor pueda recibirla. Pero, lo que todavía está mucho mejor, es que un contenedor pueda recibir los datos desde elementos arrastrados desde otros navegadores o aplicaciones.</p>
<p>Como ya he mostrado en el ejemplo anterior, esto se hace a través de los métodos <strong>setData</strong> y <strong>getData</strong> del objeto <strong>dateTransfer</strong> expuesto en el objecto del evento (Event Object).</p>
<p>La información se debe de almacenar usando los <a href="https://developer.mozilla.org/En/DragDrop/Recommended_Drag_Types">tipos recomendados</a> para un uso estándard y adecuado. A continuación unos ejemplos:</p>
<pre name="code" class="javascript">
var dt = ev.originalEvent.dataTransfer;
dt.setData('text/plain', 'Hola mundo');
dt.setData('text/html', '<span>Hola mundo</span>');
dt.setData('text/uri-list', 'http://web.ontuts.com');
</pre>
<p>Un ejemplo de uso de los <strong>tipos recomendados</strong> es seleccionar un texto en el navegador, arrastralo a un contenedor y esperar encontrar algo en en text/html, o arrastrar texto desde un procesador de texto y esperar encontrar algo en text/plain.</p>
<h3>Uso de imágenes fantasma</h3>
<p>Esta es una nueva característica que nos permite establecer lo que el usuario verá como imagen semitransparente mientras arrastra el elemento. Tenemos tres posibilidades:</p>
<ul>
<li>Establecer un elemento del DOM.</li>
<li>Establecer una imagen (aunque se base en el punto anterior).</li>
<li>Establecer un elemento canvas.</li>
</ul>
<p>Esto se hace a través del método <strong>setDragImage</strong>, incluído una vez más, en la propiedad <strong>dataTransfer</strong>. A continuación un ejemplo de los tres tipos:</p>
<pre name="code" class="javascript">
var dt = ev.originalEvent.dataTransfer;
 //Elemento DOM
dt.setDragImage( $('h2')[0], 0, 0);
 //Imagen (elemento DOM)
dt.setDragImage( $('#image')[0], 32, 32); 

var canvas = document.createElement("canvas");
canvas.width = canvas.height = 50;
var ctx = canvas.getContext("2d");
ctx.lineWidth = 8;
ctx.moveTo(25,0);
ctx.lineTo(50, 50);
ctx.lineTo(0, 50);
ctx.lineTo(25, 0);
ctx.stroke();
//Canvas
dt.setDragImage(canvas, 25, 25);
</pre>
<p>El método <strong>setDragImage</strong> acepta como primer parámetro el elemento que se desea mostrar como imagen. Los dos siguientes parámetros son el &#8220;offset&#8221; que se desea dar a la imagen respecto a la posición por defecto.</p>
<h3>Usando efectos de arrastrado / tirado</h3>
<p>El drag &#038; drop del nuevo estándar ha sido creado para sobre todo para soportar acciones como copiar, mover y vincular. Por ello se han añadido los efectos de tirado para mostrar la acción que se va a ejecutar. Para que te hagas una idea, son los iconcitos pequeños que aparecen cuando arrastras un icono en el S.O. (una flechita, un signo más, etc&#8230;).</p>
<p>Estes efectos pueden establecerse a nivel del elemento arrastrado o a nivel del contenedor. Si se vinculan al contenedor, el efecto solamente aparece cuando se producen los eventos <strong>dragenter</strong> y <strong>dragover</strong>.</p>
<p>Para usar esta funcionalidad se establecen las propiedades <strong>effectAllowed</strong> y <strong>dropEffect</strong> para los elementos arrastrados y contenedores respectivamente. Como es de esperar, estas propiedades están incluídas en el objeto <strong>dataTransfer</strong>. A continuación un ejemplo de como añadir el efecto al elemento arrastrado:</p>
<pre name="code" class="javascript">
var dt = ev.originalEvent.dataTransfer;
dt.effectAllowed = 'copy';
</pre>
<p>Los valores permitidos para esta propiedad son:</p>
<ul>
<li>none: ninguna operación permitida</li>
<li>copy: solo copiar</li>
<li>move: solo mover</li>
<li>link: solo vínculo</li>
<li>copyMove: copiar o mover</li>
<li>copyLink: copiar o vincular</li>
<li>linkMove: vincular o mover</li>
<li>all: copiar, mover o vincular</li>
</ul>
<p>Por otro lado, puedes especificar el efecto para el propio contenedor, para indicar la acción que se va a llevar a cabo al soltar el elemento:</p>
<pre name="code" class="javascript">
var dt = ev.originalEvent.dataTransfer;
dt.dropEffect = 'none';
</pre>
<h3>Conclusión</h3>
<p>Hasta aquí el tutorial sobre el Drag &#038; Drop en HTML 5, si has tenido la paciencia de leerlo todo, te habrás dado cuenta que tiene muchas novedades y mejoras que despilegan un nuevo abanico de posiblidades, además de mejorar el rendimiento.</p>
<p>Espero que os haya resultado útil la información y no dudéis en dejar un comentario para preguntar, criticar o valorar.</p>
<p>¡Nos vemos en el próximo!</p>


<p>No related posts.</p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/drag-drop-en-html-5/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Sesiones en PHP</title>
		<link>http://web.ontuts.com/tutoriales/sesiones-en-php/</link>
		<comments>http://web.ontuts.com/tutoriales/sesiones-en-php/#comments</comments>
		<pubDate>Fri, 30 Jul 2010 16:58:08 +0000</pubDate>
		<dc:creator>Iván Guardado</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[sesiones]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=4784</guid>
		<description><![CDATA[En este artículo analizaremos de forma profunda las sesiones web, desde su definición hasta su uso y configuración en PHP. Si todavía no tienes qué son, cómo funcionan y para qué sirven... este tutorial te será de gran ayuda. ¡No te lo pierdas!


Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/aprendiendo-a-utilizar-la-libreria-curl-en-php/' rel='bookmark' title='Permanent Link: Aprendiendo a utilizar la librería cURL en PHP'>Aprendiendo a utilizar la librería cURL en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/aplicaciones-php-multilenguaje/' rel='bookmark' title='Permanent Link: Aplicaciones Multilenguaje PHP'>Aplicaciones Multilenguaje PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/creando-una-capa-de-conexion-abstracta-a-base-de-datos-con-php/' rel='bookmark' title='Permanent Link: Creando una capa de conexión abstracta a base de datos con PHP'>Creando una capa de conexión abstracta a base de datos con PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/como-validar-un-formulario-con-php-y-javascript-jquery/' rel='bookmark' title='Permanent Link: Cómo validar un formulario utilizando PHP y Javascript (jQuery)'>Cómo validar un formulario utilizando PHP y Javascript (jQuery)</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>¿Que es una sesión?</h3>
<p>Un sesión en una web se puede definir como el recorrido de páginas que un usuario hace en nuestro sitio, desde que entra hasta que lo abandona. Gracias al uso de sesiones podemos reconocer las peticiones de cada usuario y así llevar a cabo acciones específicas, como mostrar información adaptada a él o guardar información de sus gustos o páginas que más visita.</p>
<p>Podemos entonces decir que pueden diferenciarse dos tipos de sesiones:</p>
<ul>
<li><strong>Sesiones &#8220;activas&#8221;:</strong> las que muestran información personalizada según el usuario, por ejemplo, cuando inicia sesión en una web.</li>
<li><strong>Sesiones &#8220;pasivas&#8221;:</strong> el servidor reconoce cada movimiento del usuario y lo almacena de forma que en un futuro, se le mostrará una web con la información que al usuario le pueda parecer más interesante sin que se dé cuenta.</li>
</ul>
<p>Lógicamente, una web puede incorporar los dos tipos de sesiones, véase el caso de los servicios ofrecidos por Google <img src='http://web.ontuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<h3>¿Cómo funcionan?</h3>
<p>Las sesiones se basan en un identificador generado por visitante, simulando una especie de DNI, de modo que cuando quiera acceder a cualquier página de nuestro sitio, mostrará ese &#8220;DNI&#8221; y quedará identificado. El identificador será único por cada usuario y se le conoce como <strong>Session ID</strong> (SID).</p>
<p>Ahora la pregunta es <strong>¿Como se gestiona dicho SID?</strong> pues muy fácil, con Cookies. Si no sabes muy bien que son, te recomiendo mi anterior artículo <a href="http://web.ontuts.com/tutoriales/introduccion-a-cookies-en-la-web/">Introducción a Cookies en la Web</a>. Una cookie es información que se establece desde el servidor y que el navegador del usuario envía en cada petición de forma abstracta. Entonces, si establecemos en una cookie el identificador de sesión, ya tenemos nuestra tarjeta identificadora lista.</p>
<p>Desde el lado del servidor, se debe implementar toda la lógica de la sesión, almacenando todos los SID activos y la información relativa a ellos. Esto se puede hacer de múltiples formas: mediante sistema de ficheros, base de datos, memcached o cualquier otro método de almacenamiento de información.</p>
<h3>Manejo de sesiones en PHP</h3>
<p>PHP, como es de esperar tiene todo un <a href="http://www.php.net/manual/en/ref.session.php">set de funciones</a> para el manejo de sesiones de forma que abstrae bastante la labor del desarrollador. Entre otras cosas, llevará a cabo las siguientes tareas:</p>
<ul>
<li>Identificación por cookie del SID.</li>
<li>Si no existe el SID, crea uno y lo guarda en las cookies.</li>
<li>Almacenamiento de información relativa al SID en el sistema de ficheros.</li>
<li>Gestión del expirado de sesión.</li>
</ul>
<p>Almacenar y/o recuperar información sobre la sesión actual es tan fácil como usar un array global llamado <strong>$_SESSION</strong>.</p>
<p>Con todo esto, el uso de sesiones en PHP se hace realmente fácil. Aunque los creadores de PHP no nos quieren encadenar y nos permiten crear nuestras propias funciones para gestionar las sesiones de forma manual, como veremos más adelante en un ejemplo.</p>
<h4>Usando los manejadores de PHP</h4>
<p>Vamos a mostrar un ejemplo de como crear una sesión por visitante y almacenar información persistente durante la sesión</p>
<pre name="code" class="php">&lt;?php
//Inicia o recupera la sesión
session_start();
//Ejemplo de como crear una variable de sesión
if(!isset($_SESSION['user_name'])){
    $_SESSION['user_name'] = getUserName();
}
//Actualiza o crea una variable de sesión
$_SESSION['last_access'] = time();
</pre>
<p>Es importante informar sobre la función <strong>session_start</strong>. Dicha función se encarga de crear o cargar una sesión previamente abierta basándose en el SID pasado por cookies. Algo a tener muy en cuenta, es que al estar basado en cookies, el server generará cabeceras HTTP, por lo que es importante llamar a la función antes de que se envíe cualquier salida de texto.</p>
<p>Si no recibirás un error que dice algo como <strong>&#8220;headers already sent&#8221;</strong>. De hecho os recomiendo que pongáis la llamada en la primera línea de vuestro index.php para evitar problemas.</p>
<h4>Usando manejadores propios</h4>
<p>Como hemos dicho, PHP nos permite declarar nuestras propias funciones para gestionar las sesiones de forma personalizada, a continuación os dejo una clase base que podéis rellenar para crear vuestros manejadores:</p>
<pre name="code" class="php">
&lt;?php
/**
* Manejador de sesiones
*
*/
class Session
{
/**
* Abre la sesión
* @return bool
*/
public static function open() {
}

/**
* Cierra la sesión
* @return bool
*/
public static function close() {
}

/**
* Lee la sesión
* @param int session id
* @return string contenido de la sesión
*/
public static function read($id) {
}

/**
* Guarda la sesión
* @param int session id
* @param string contenido de la sesión
*/
public static function write($id, $data) {
}

/**
* Destruye la sesión
* @param int session id
* @return bool
*/
public static function destroy($id) {
}

/**
* Colector de basura
* @param int life time (sec.)
* @return bool
*/
public static function gc($max) {
}
</pre>
<p>Una vez que tengas la clase adaptada a tus necesidades, necesitas indicarle a PHP que use eses métodos y no los que usa por defecto.</p>
<p>Eso se consigue con la función <strong>session_set_save_handler</strong>:</p>
<pre name="code" class="php">
ini_set('session.save_handler', 'user');
session_set_save_handler(array('Session', 'open'),
                         array('Session', 'close'),
                         array('Session', 'read'),
                         array('Session', 'write'),
                         array('Session', 'destroy'),
                         array('Session', 'gc')
                         );
session_start();
</pre>
<h3>Configuración de sesiones en PHP</h3>
<p>En la documentación oficial, podéis acceder a <a href="http://www.php.net/manual/en/session.configuration.php">esta página</a> en la que se muestra un listado completo de todas las variables de configuración (en el fichero php.ini), no obstante, vamos a ver algunas que tienen bastante importancia.</p>
<h4>session.save_path</h4>
<p>Especifica en que directorio será almacenda la informacion de los distintos SID generados. En este directorio tiene que poder escribir el usuario de Apache, que será el creador de los ficheros en última instancia.</p>
<h4>session.name</h4>
<p>Especifica el nombre de la sesión por defecto, que será a su vez el nombre de la cookie establecida por el servidor. Por defecto se llama PHPSESSID pero es recomendable cambiarlo.</p>
<h4>session.gc_maxlifetime</h4>
<p>Es el número de segundos tras el cual la información almacenda pasa a ser considerada basura y por tanto borrada cuando se lance el colector de basura (Garbage Collector).</p>
<h4>session.cookie_lifetime</h4>
<p>Establece los segundos durante los cuales la cookie de sesión va a estar activa. Por defecto está a 0 y por tanto la cookie de sesión durará hasta que el usuario cierre el navegador, como ya habíamos visto en <a href="http://web.ontuts.com/tutoriales/introduccion-a-cookies-en-la-web/">Introducción a Cookies en la Web</a>.</p>
<p>Es muy importante diferenciar esta variable de <strong>gc_maxlifetime</strong>. La funcionalidad de <strong>gc_maxlifetime</strong> hace la resta de la hora del último acceso menos la hora actual, si el resultado en segundos es mayor que el valor de dicha variable, la sessión se borra.</p>
<p>Mientras que <strong>cookie_lifetime</strong> establece un tiempo de duración fijo, es decir, si se establace en 60 segundos, la sesión expirará dentro de un minuto aunque el usuario esté accediendo continuamente.</p>
<h4>session.use_only_cookies</h4>
<p>Esta variable establece que solamente se deben de usar cookies para gestionar las sesiones. Por defecto está activado y <strong>es importante no modificarlo</strong>. El problema de desactivarlo, es que PHP intentará gestionar las sesiones vía parámetros GET en caso de que no estén activadas las cookies en el navegador del usuario.</p>
<p>Esto puede llevar a problemas de seguridad y comprometer la privacidad del usuario.</p>
<h3>Conclusión</h3>
<p>El uso de sesiones con PHP es, como has visto, bastante sencillo, aunque se puede complicar todo lo que quieras si lo quieres gestionar tú de forma manual.</p>
<p>Os recomiendo que si no tenéis experiencia en el tema, probéis a experimentar hasta que entendáis bien como funciona todo, ya que mucha gente usa las sesiones pero no toda sabe como funciona, y es muy importante siempre, saber lo que está haciendo el backend a la hora de desarrollar.</p>
<p>Espero que os haya gustado, ¡nos vemos en el proximo artículo!</p>


<p>Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/aprendiendo-a-utilizar-la-libreria-curl-en-php/' rel='bookmark' title='Permanent Link: Aprendiendo a utilizar la librería cURL en PHP'>Aprendiendo a utilizar la librería cURL en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/aplicaciones-php-multilenguaje/' rel='bookmark' title='Permanent Link: Aplicaciones Multilenguaje PHP'>Aplicaciones Multilenguaje PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/creando-una-capa-de-conexion-abstracta-a-base-de-datos-con-php/' rel='bookmark' title='Permanent Link: Creando una capa de conexión abstracta a base de datos con PHP'>Creando una capa de conexión abstracta a base de datos con PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/como-validar-un-formulario-con-php-y-javascript-jquery/' rel='bookmark' title='Permanent Link: Cómo validar un formulario utilizando PHP y Javascript (jQuery)'>Cómo validar un formulario utilizando PHP y Javascript (jQuery)</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/sesiones-en-php/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Introducción a Cookies en la Web</title>
		<link>http://web.ontuts.com/tutoriales/introduccion-a-cookies-en-la-web/</link>
		<comments>http://web.ontuts.com/tutoriales/introduccion-a-cookies-en-la-web/#comments</comments>
		<pubDate>Sat, 24 Jul 2010 11:01:14 +0000</pubDate>
		<dc:creator>Iván Guardado</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[cookies]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=4666</guid>
		<description><![CDATA[Si te estás iniciando en el mundo del desarrollo web, seguramente hayas oído hablar sobre ellas, incluso puede que las hayas usado, pero quizás no entiendas muy bien lo qué son y qué posibilidades nos dan. En este artículo intentaré que os quede todo claro.


Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/sesiones-en-php/' rel='bookmark' title='Permanent Link: Sesiones en PHP'>Sesiones en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/aprendiendo-a-utilizar-la-libreria-curl-en-php/' rel='bookmark' title='Permanent Link: Aprendiendo a utilizar la librería cURL en PHP'>Aprendiendo a utilizar la librería cURL en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-y-primeros-pasos-con-prototype-libreria-javascript/' rel='bookmark' title='Permanent Link: Introducción y Primeros pasos con Prototype, librería javascript'>Introducción y Primeros pasos con Prototype, librería javascript</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-a-object-relational-mapping-orm/' rel='bookmark' title='Permanent Link: Introducción a Object-Relational Mapping (ORM)'>Introducción a Object-Relational Mapping (ORM)</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>¿Que es una Cookie?</h3>
<p>Una cookie es <strong>información</strong> enviada desde un servidor de páginas web y <strong>almacenada</strong> en el <strong>disco duro</strong> del <strong>visitante</strong> a través del navegador. Esta información será reenviada de nuevo al servidor en cada petición, de forma que el servidor puede identificar o recuperar información sobre el usuario que está accediendo.</p>
<h3>¿Por qué se han creado las cookies?</h3>
<p>Las cookies fueron implementadas por primera vez por <a id="jv-:" title="Netscape Communications" href="http://es.wikipedia.org/wiki/Netscape_Communications">Netscape Communications</a> para la creación del típico cesto de comprar en una tienda online. El problema hasta entonces era que el protocolo HTTP carecía de la posibilidad de mantener información pos sí mismo. Los métodos usados antes eran:</p>
<ul>
<li><strong>Identificación por IP</strong>: un método muy poco fiable, pues bajo una misma IP podían estar accediendo distintos usuarios (por ejemplo desde un cíber), además que la dirección IP de un usuario puede cambiar.</li>
<li><strong>Por URL</strong>: Consiste en añadir la información en la URL, despues del interrogante <strong>?</strong>. Esta es una técnica más precisa en lo que se refiere a identificación, pero tiene problemas de seguridad.</li>
</ul>
<p>Gracias a las cookies, un servidor web puede identificar un conjunto pc-navegador-usuario y mostrar la información adecuada a ese conjunto, por ejemplo un carrito de compra que haya creado.</p>
<h3>¿Son inseguras las Cookies?</h3>
<p>Existe una tendencia entre la gente a pensar que las cookies son una especie de virus o gusano. Esto es debido a que las cookies están en muchas ocasiones vinculadas a temas de spyware.</p>
<blockquote><p>La fama, la tendencia a pensar que las cookies son inseguras o malas se ha debido a que en numerosas ocasiones han sido vinculadas a temas de Spyware y otro tipo de situaciones comprometidas para el usuario.</p></blockquote>
<p>Muchas empresas de publicidad las utilizan para hacer un seguimiento de los hábitos de navegación del usuario. Esto, a mucha gente le parece que es un asalto a la privacidad y por ello las cookies tienen sus detractores.</p>
<p>Sin embargo, el uso de cookies, a día de hoy, y con un navegador actualizado, no suponen ningún tipo de riesgo para el PC ni los datos almacenados en él.</p>
<h3>¿Cómo se crea una Cookie?</h3>
<p>Vamos a entrar en algo más técnico y ver como se establecen / reciben cookies a través del protocolo HTTP. Cuando entramos en una web, por ejemplo http://www.example.com/index.html, el navegador enviará una petición al servidor parecida a esta:</p>
<pre name="code" class="php">GET /index.html HTTP/1.1</pre>
<p>Entonces el servidor hará lo que tenga que hacer para generar el contenido y devolverá algo así:</p>
<pre name="code" class="php">
HTTP/1.1 200 OK
Content-type: text/html
(contenido de la página)
</pre>
<p>Si desde el servidor se quiere guardar una cookie en el ordenador del usuario, añadirá lo siguiente a su respuesta:</p>
<pre name="code" class="php">
Set-Cookie: Name=el valor de la cookie; expires=Fri, 23-Jul-2010 23:59:59 GMT; path=/; domain=.example.com
</pre>
<p>Vamos a ver por partes qué significa esa línea:</p>
<ul>
<li><strong>Set-Cookie</strong>: Es el identificador que reconocerá el navegador para saber que se va a crear una cookie.</li>
<li><strong>Name=valor de la cookie</strong>: Es el conjunto clave valor de la cookie. Name es el nombre identificado, siendo su valor lo que va inmediatamente después del signo igual (=). El valor pueden contener textos y números.</li>
<li><strong>expires</strong>: Establece cuando la cookie dejará de existir. Si no se establece, lo cookie ser borrará al cerrar el navegador. Si se establece un tiempo inferior al actual, tambien se borrará.</li>
<li>domain: Establece el dominio al que la cookie será enviada. Si el valor es por ejemplo, &#8216;.example.com&#8217;, la cookie será enviada a cualquier subdominio existente en example.com. Si se le diese el valor img.example.com, la cookie solo sería enviada a ese subdominio, descartando también al propio example.com .</li>
<li><strong>path</strong>: Establece el directorio al que afecta la cookie. Si por ejemplo se establece a &#8216;/user&#8217;, la cookie solo existirá en example.com/user. Es importante destacar que path y domain trabajan de forma conjunta.</li>
</ul>
<h3>Crear y leer cookies en PHP</h3>
<p>Vamos a llevar todo esto tan teórico a la práctica usando PHP como lenguaje de programación. En php existe la función <strong>setcookie</strong> para crear o eliminar cookies y la variable global <strong>$_COOKIE</strong> para acceder a su contenido.</p>
<p>La funcion setcookie tiene la siguiente definición:</p>
<pre name="code" class="php">
bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )
</pre>
<p>Supongo que puedes vincular fácilmente cada parámetro con los valores que hemos listado en la sección anterior excepto <strong>$secure</strong> y <strong>$httponly</strong>.</p>
<ul>
<li><strong>$secure</strong> establece si la cookie debe solamente funcionar bajo conexiones protegidas por SSL.</li>
<li><strong>$httponly</strong> es para indicar si la cookie es solo accesible a través del protocolo HTTP, evitando así el acceso desde lenguajes de scripting como Javascript. Esto puede ayudar a reducir bastante los ataques XSS.</li>
</ul>
<p>Veamos algunos ejemplos prácticos.</p>
<h4>Ejemplo de creación de cookies</h4>
<pre name="code" class="php">
&lt;?php
$value = 'valor que se quiere guardar';
setcookie("TestCookie", $value, time()+3600);  /* expire en 1 hora */
setcookie("TestCookieDomain", $value, time()+3600,"/","tests.example.com");
</pre>
<h4>Accediendo a las cookies</h4>
<pre name="code" class="php">
&lt;?php
/*Antes de acceder a una cookie, se comprueba si se ha enviado*/
if(isset($_COOKE['TestCookie'])){
     var_dump($_COOKIE['TestCookie']);
}
</pre>
<h4>Borrando cookies</h4>
<pre name="code" class="php">
&lt;?php
/*Tiempo de expiración pasado, borra la cookie*/
setcookie ("TestCookie", "", time() - 3600);
</pre>
<h3><strong>Apuntes sobre seguridad</strong></h3>
<p>Hay que tener mucho cuidado cuando se crean o leen cookies ya que existen muchos ataques que se aprovechan de excesos de confianza / desconocimiento del programador.</p>
<ul>
<li><strong>Nunca almacenes información privada en una cookie</strong>: Un ejemplo muy común de uso de cookies es la creación de sesiones con la opción &#8220;Recordarme&#8221;. En esta clase de webs, el usuario es recordado y no tiene que volver a escribir sus credenciales para iniciar sesión. Si estás pensando en almacenar el usuario y la contraseña en las cookies, vas por mal camino, pues es fácil acceder a la información de las cookies a través de un <a id="f6fs" title="sniffer" href="http://es.wikipedia.org/wiki/Sniffer">sniffer</a> u otros ataques. Para que no os quedéis con la duda, lo que se suele hacer es generar un código aleatorio que identifique la sesión del usuario.</li>
<li><strong>Nunca te fíes del contenido de una cookie</strong>: Si bien las cookies funcionan de forma totalmente transparente al usuario y no tiene, a simple vista, formas de modificarlas, no te puedes fiar del contenido que envíen. Existen múltiples herramientas que permiten modificar / crear las cookies de forma que un usuario avanzado podría hacer grandes destrozos en el servidor. Valida siempre el valor de las cookies para comprobar que tienen lo esperado y no, por ejemplo, inyección de código.</li>
</ul>
<h3>Conclusión</h3>
<p>Las cookies han sido diseñadas para mejorar la experiencia del usuario en la web. Sin ellas no se habrían podido crear las primeras páginas con inicios de sesión y por lo tanto no llegarían a existir webs como Facebook o GMail.</p>
<p>Las cookies por tanto, aunque tengan mala fama entre las personas no-técnicas, son imprescindibles <img src='http://web.ontuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<p>¡Nos vemos en el próximo tutorial!</p>


<p>Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/sesiones-en-php/' rel='bookmark' title='Permanent Link: Sesiones en PHP'>Sesiones en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/aprendiendo-a-utilizar-la-libreria-curl-en-php/' rel='bookmark' title='Permanent Link: Aprendiendo a utilizar la librería cURL en PHP'>Aprendiendo a utilizar la librería cURL en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-y-primeros-pasos-con-prototype-libreria-javascript/' rel='bookmark' title='Permanent Link: Introducción y Primeros pasos con Prototype, librería javascript'>Introducción y Primeros pasos con Prototype, librería javascript</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-a-object-relational-mapping-orm/' rel='bookmark' title='Permanent Link: Introducción a Object-Relational Mapping (ORM)'>Introducción a Object-Relational Mapping (ORM)</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/introduccion-a-cookies-en-la-web/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Utilizando Doctrine como ORM en PHP</title>
		<link>http://web.ontuts.com/tutoriales/utilizando-doctrine-como-orm-en-php/</link>
		<comments>http://web.ontuts.com/tutoriales/utilizando-doctrine-como-orm-en-php/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 11:11:51 +0000</pubDate>
		<dc:creator>Iván Guardado</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[orm]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=4430</guid>
		<description><![CDATA[Continuamos con otra publicación perteneciente a la serie de artículos dedicados a Object-Relational Mapping (ORM). Esta vez nos centraremos en algo más práctico, creando un aplicación de ejemplo usando Doctrine como motor de ORM.


Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/como-validar-un-formulario-con-php-y-javascript-jquery/' rel='bookmark' title='Permanent Link: Cómo validar un formulario utilizando PHP y Javascript (jQuery)'>Cómo validar un formulario utilizando PHP y Javascript (jQuery)</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-a-object-relational-mapping-orm/' rel='bookmark' title='Permanent Link: Introducción a Object-Relational Mapping (ORM)'>Introducción a Object-Relational Mapping (ORM)</a></li>
<li><a href='http://web.ontuts.com/tutoriales/creando-una-capa-de-conexion-abstracta-a-base-de-datos-con-php/' rel='bookmark' title='Permanent Link: Creando una capa de conexión abstracta a base de datos con PHP'>Creando una capa de conexión abstracta a base de datos con PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/sesiones-en-php/' rel='bookmark' title='Permanent Link: Sesiones en PHP'>Sesiones en PHP</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>¿Que es Doctrine?</h3>
<p>Doctrine es una  librería para PHP que nos permite trabajar con un esquema de base de  datos como si fuese un conjunto de objetos, y no de tablas y registros.  Si no sabes todavía que significa ORM y que ventajas / desventajas  tiene, te recomiendo que leas el tutorial anterior: <a href="../tutoriales/introduccion-a-object-relational-mapping-orm/">Introducción a  Object-Relational Mapping (ORM)</a>.</p>
<p>Doctrine está  inspirado en <a href="http://www.hibernate.org/">Hibernate</a>, que es uno de los  ORM más populares y grandes que existen y nos brinda una capa de  abstracción de la base de datos muy completa. La característica más  importante es que te da la posibilidad de escribir consultas de base de  datos en un lenguaje propio llamado Doctrine Query Language (DQL).</p>
<h3>Características  principales</h3>
<p>Doctrine es una librería muy completa y muy configurable, por  lo que casi me resulta complicado seleccionar que detalles destacar. Os  pongo las características más globales, ya que este tutorial pretende  ser una introducción, por lo que dejamos de lado las cosas más  “complejas”.</p>
<h4>Generación automática del modelo</h4>
<p>Cuando se trabaja con  ORM, necesitas crear el conjunto de clases que representa el modelo de  la aplicación, luego estas clases serán vinculadas al esquema de la base  de datos de forma automática con un motor ORM. </p>
<p>Aunque son cosas  diferentes, cuando diseñas un modelo relacional y un modelo de clases,  suelen ser muy parecidos. Doctrine  se aprovecha de esta similitud y nos  permite generar de forma automática el modelo de clases basándose en el  modelo relacional de tablas.</p>
<p>Es decir, si tenemos una tabla llamada  usuarios, se autogenerará una clase llamada Usuarios cuyas propiedades  son las columnas de dicha tabla.</p>
<h4>Posibilidad de trabajar con YAML</h4>
<p>Como se comenta en el  apartado anterior, Doctrine puede generar de forma automática el modelo,  pero también deja la posibilidad (como es lógico) que puedas definir tu  mismo el mapeo de tablas y sus relaciones. </p>
<p>Esto se puede hacer con  código PHP o con <a href="http://es.wikipedia.org/wiki/YAML">YAML</a>, que es un formato de  serialización de datos legible por humanos muy usado para este fin. En  el ejemplo siguiente se muestra como definir dos tablas relacionadas:</p>
<pre name="code" class="xml">
---
User:
  columns:
    username:
      type: string(255)
    password:
      type: string(255)
    contact_id:
      type: integer
  relations:
    Contact:
      class: Contact
      local: contact_id
      foreign: id
      foreignAlias: User
      foreignType: one
      type: one

Contact:
  columns:
    first_name:
      type: string(255)
    last_name:
      type: string(255)
    phone:
      type: string(255)
    email:
      type: string(255)
    address:
      type: string(255)
  relations:
    User:
      class: User
      local: id
      foreign: contact_id
      foreignAlias: Contact
      foreignType: one
      type: one
</pre>
<p>En el ejemplo, se muestra una relación entre dos tablas a través de los campos User.contact_id y Contact.id.</p>
<h4>Doctrine_Record y  Doctrine_Table</h4>
<p>Practicamente todo nuestro modelo heredará de estas dos  clases. Doctrine_Record representa una entidad con sus propiedades  (columnas) y nos facilita métodos para insertar, actualizar o eliminar  registros entre otros. Por ejemplo:</p>
<pre name="code" class="php">
&lt;php
$user = new User();
$user-&gt;name =  “Iván”;
$user-&gt;save();
</pre>
<p>La clase  Doctrine_Table representa el esquema de una tabla. A través de esta  clase podemos, por ejemplo, obtener información sobre las columnas o  buscar registros específicos:</p>
<pre name="code" class="php">
&lt;?php
$userTable =  Doctrine_Core::getTable('Users');
$user = $userTable-&gt;find(1);
</pre>
<h4>Buscadores mágicos  (Magic finders)</h4>
<p>En Doctrine, puedes buscar registros basándote en cualquier  campo de una tabla. En el partado anterior podíamos ver que existe el  método find() para buscar un determinado id en una tabla&#8230;pues bien, se  podría hacer findByX() en donde X es el nombre de la columna.</p>
<p>Si  existen los campos llamados name y email, podemos hacer findByName() y  findByEmail(). Tambien es importante decir que existe el método  findAll(), que obtiene todos los registros de la tabla.</p>
<h4>Relaciones entre  entidades</h4>
<p>En  Doctrine, una vez que hemos definido nuestro modelo (o se ha creado de  forma automática) con las tablas y sus relaciones, resulta fácil acceder  y moverse por entidades relacionadas.</p>
<p>Vamos a ver un ejemplo de como  acceder a valores que en nuestro sistema relacional se encuentran en  tablas separadas. Teniendo en cuenta que tenemos dos tablas, User y  Comments, con relación 1-N:</p>
<pre name="code" class="php">
&lt;?php
$commentsTable =  Doctrine_Core::getTable('Comments');
$comments =  $commentsTable-&gt;findAll();
foreach($comments as $comment){
	echo  $comment-&gt;User-&gt;name;
}
</pre>
<p>A través de $comment-&gt;User estamos  accediendo a un nuevo objeto del tipo User que se carga de forma  dinámica, de esta forma podemos acceder a las propiedades y métodos de  dicha clase olvidándonos de tener que lanzar código SQL.</p>
<h3>Lenguaje DQL</h3>
<p>DQL es un lenguaje  creado para ayudar al programador a extraer objetos de la base de datos.  Entre las ventajas de usar este lenguaje se encuentran:</p>
<ul>
<li>Está diseñado para  extraer objetos, no filas, que es lo que nos interesa.</li>
<li>Entiende las  relaciones, por lo que no es necesario escribir los joins a mano.</li>
<li>Portable con  diferentes bases de datos.</li>
</ul>
<p>Es importante considerar el uso de DQL  para obtener la información a cargar en lugar de usar la “forma  automática” de Doctrine para mejorar el rendimiento. En el ejemplo  anterior, cuando se accede a $comment-&gt;User, hemos dicho que se está  cargando un nuevo objeto de forma dinámica, pues bien, esto no es óptimo  porque realiza consultas SQL de más.</p>
<p>Si tenemos 100 comentarios,  ejecutará 100 consultas SQL para cargar la información del objeto User,  lo cual es una pérdida de rendimiento enorme. En lugar de eso, es  necesario usar DQL para que traiga toda la información de un solo paso:</p>
<pre name="code" class="php">
&lt;?php
$q =  Doctrine_Query::create()
	-&gt;select('c.*,u.name')
	-&gt;from('Comments c')
	-&gt;innerJoin('c.User u');
$comments =  $q-&gt;execute();
</pre>
<p>El  código anterior obtendrá la información de los comentarios cruzados con  los datos de usuario, por lo que obtendrá toda la información en un  solo paso.</p>
<p>El  lenguaje DQL es algo tan extenso que necesitaría varios artículos para  explicarlo en profundidad, por eso te recomiendo que mires <a href="http://www.doctrine-project.org/projects/orm/1.2/docs/manual/dql-doctrine-query-language/en">la documentación  oficial</a> que está bien explicado y completo <img src='http://web.ontuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Construyendo un  ejemplo real</h3>
<p>Para darle algo de vida al tutorial, vamos a crear una  implementación muy sencilla de Doctrine en la que veremos todo lo  comentado hasta ahora. La aplicación consiste en un listado de  comentarios escritos por usuarios.</p>
<p>Cada vez que se desee insertar un  nuevo comentario, se deberá rellenar un formulario con: nombre, e-mail y  texto, siendo los dos últimos obligatorios. Si el usuario no deja su  nombre, se mostrará como “Desconocido”.</p>
<h4>Paso 1: crear el  esquema de base de datos</h4>
<p>El ejemplo es tan sencillo que solamente  tendrá dos tablas relacionadas entre sí: users y users_comments.</p>
<pre name="code" class="sql">
CREATE SCHEMA  ontuts_doctrine CHARSET UTF8 COLLATE utf8_general_ci;
use ontuts_doctrine;
CREATE TABLE users(
	id INT(11) PRIMARY  KEY auto_increment,
	name VARCHAR(30),
	email VARCHAR(60)
)ENGINE=INNODB;
CREATE TABLE  users_comments(
	id INT(11) PRIMARY KEY auto_increment,
	id_user INT(11)  NOT NULL,
	text VARCHAR(255),
CONSTRAINT fk_users_comments_users  FOREIGN KEY (id_user) REFERENCES users(id)
)ENGINE=INNODB;
</pre>
<h4>Paso 2: crear el  modelo</h4>
<p>Esto,  como ya habrás aprendido, se puede hacer de forma manual (con PHP o  YAML) y de forma automática. En este caso lo haremos de forma automática  para explicar como funciona. Vamos a crear el script que se encargará  de generar los ficheros, <strong>create_orm_model.php</strong>:</p>
<pre name="code" class="php">
&lt;?php
require_once(dirname(__FILE__)  . '/lib/Doctrine.php');
spl_autoload_register(array('Doctrine',  'autoload'));
$conn  =  Doctrine_Manager::connection('mysql://root:password@localhost/ontuts_doctrine',  'doctrine');
Doctrine_Core::generateModelsFromDb('models',  array('doctrine'), array('generateTableClasses' =&gt; true));
</pre>
<p>Nos aseguramos de que  existe la carpeta models en la raíz de nuestro proyecto:</p>
<pre name="code" class="php">
mkdir  /path/myproject/models
</pre>
<p>Ahora llamamos al script, bien vía Apache o  vía CLI:</p>
<pre name="code" class="php">
php5  -f create_orm_model.php
</pre>
<p>Una vez se ha ejecutado con éxito, vamos a la  carpeta models y vemos que se han generado varios ficheros:</p>
<ul>
<li>Users.php</li>
<li>UsersTable.php</li>
<li>UserComments.php</li>
<li>UserCommentsTable.php</li>
<li>generated/BaseUsers.php</li>
<li>generated/BaseUsersComments.php</li>
</ul>
<p>Si te fijas, se han  creado las clases que se extienden de Doctrine_Record y Doctrine_Table  que ya hemos comentado antes.</p>
<p>BaseUsers.php y BaseUsersComments.php  representan clases base y que no deberías modificar. Cualquier método o  propiedad que desees añadir, debes hacerlo sobre Users.php o  UsersComments.php.</p>
<h4>Paso 3: extender el modelo</h4>
<p>Analizando la  aplicación llegamos a la conclusión de que la clase Users necesita dos  nuevos métodos:</p>
<ul>
<li>getName: que devuelva el nombre del usuario o  “Desconocido” si no ha rellenado esa información.</li>
<li>addComment: que  inserte un nuevo comentario asignado a dicho usuario.</li>
</ul>
<p>Editamos entonces el  fichero Users.php y lo dejamos así:</p>
<pre name="code" class="php">
&lt;?php
class Users extends  BaseUsers
{
	public function addComment($text){
		$comment = new UsersComments();
		$comment-&gt;id_user = $this-&gt;id;
		$comment-&gt;text = $text;
		$comment-&gt;save();
	}
	public function  getName(){
		if(empty($this-&gt;name) || is_null($this-&gt;name)){
			return  "Desconocido";
		}else{
			return $this-&gt;name;
		}
	}
}
</pre>
<p>Tambien necesitamos  extender la clase UsersCommentsTable para sobreescribir el método  findAll(), de forma que obtenga la información cruzada con la tabla  users y así mejorar el rendimiento. Editamos el fichero  UsersCommentsTable.php y lo dejamos así:</p>
<pre name="code" class="php">
&lt;?php
class  UsersCommentsTable extends Doctrine_Table
{
	/**
	* Returns an  instance of this class.
	*
	* @return object  UsersCommentsTable
	*/
	public static function getInstance()
	{
		return  Doctrine_Core::getTable('UsersComments');
	}
	public function  findAll($hydrationMode = null){
		$q = Doctrine_Query::create()
			-&gt;select('c.*,u.name')
			-&gt;from('UsersComments c')
			-&gt;innerJoin('c.Users u');
		return $q-&gt;execute();
	}
}
</pre>
<p>Listo, ya tenemos  nuestro modelo ampliado <img src='http://web.ontuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<h4>Paso 4: crear la lógica</h4>
<p>A continuación creamos  el fichero index.php que contendrá la lógica principal de la  aplicación. Nos queda algo así: <strong>index.php</strong></p>
<pre name="code" class="php">
&lt;?php
//Carga Doctrine
require_once(dirname(__FILE__)  . '/lib/Doctrine.php');
spl_autoload_register(array('Doctrine',  'autoload'));
$conn =  Doctrine_Manager::connection('mysql://root@localhost/ontuts_doctrine',  'doctrine');
$conn-&gt;setCharset('utf8');
Doctrine_Core::loadModels('models');

//Variable en la  plantilla html
$tpl = array("comments"=&gt; array(), "error"=&gt;false);

//Comprueba si se ha  enviado el formulario
if(!empty($_POST) &amp;&amp;  isset($_POST['create_comment'])){
	$email = filter_input(INPUT_POST,  "email", FILTER_SANITIZE_STRING);
	$name = filter_input(INPUT_POST,  "name", FILTER_SANITIZE_STRING);
	$text = filter_input(INPUT_POST,  "text", FILTER_SANITIZE_STRING);
	//Comprueba que se hayan rellenado  los campos obligatorios
	if(!empty($email) &amp;&amp;  !is_null($email) &amp;&amp;
	!empty($text) &amp;&amp;  !is_null($text)){
		$userTable = Doctrine_Core::getTable('Users');
		$users =  $userTable-&gt;findByEmail($email);
		$user = null;
		//Si el  usuario no existe, lo crea
		if($users-&gt;count()==0){
			$user =  new Users();
			$user-&gt;name = $name;
			$user-&gt;email = $email;
			$user-&gt;save();
		}else{
			$user =  $users[0];
		}
		//Inserta el comentario
		$user-&gt;addComment($text);
	}else{
		//Si no se se  han rellenado todos los valores obligatorios
		//mostrará un  error
		$tpl['error'] = true;
	}
}
//Carga los comentarios
$commentsTable =  Doctrine_Core::getTable('UsersComments');
$tpl['comments'] =  $commentsTable-&gt;findAll();
//Envia la información
require_once('template.phtml');
</pre>
<h4>Paso 5: crear la  plantilla</h4>
<p>Por  último ya solo nos queda dar formato visual al contenido de <strong>template.phtml</strong>:</p>
<pre name="code" class="html">
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML  1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
&lt;html  xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"&gt;
&lt;head&gt;
&lt;meta  http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h2&gt;Añadir  comentario&lt;/h2&gt;
&lt;?if($tpl['error']):?&gt;&lt;p&gt;Por  favor, rellena las cajas marcadas con asterisco  (*)&lt;/p&gt;&lt;?endif?&gt;
&lt;form action="index.php"  method="post"&gt;
&lt;label&gt;Dirección e-mail  *&lt;/label&gt;&lt;br/&gt;
&lt;input type="text"  name="email"/&gt;&lt;br/&gt;
&lt;label&gt;Nombre&lt;/label&gt;&lt;br/&gt;
&lt;input  type="text" name="name"/&gt;&lt;br/&gt;
&lt;label&gt;Comentario *&lt;/label&gt;&lt;br/&gt;
&lt;textarea  name="text" cols="80" rows="5"&gt;&lt;/textarea&gt;&lt;br/&gt;
&lt;input  type="submit" name="create_comment" value="Enviar"/&gt;
&lt;/form&gt;
&lt;h2&gt;Comentarios de los usuarios&lt;/h2&gt;
&lt;ul&gt;
&lt;?foreach($tpl['comments'] as $comment):?&gt;
&lt;li&gt;&lt;a  href="mailto:&lt;?=$comment-&gt;Users-&gt;email?&gt;"&gt;&lt;?=$comment-&gt;Users-&gt;getName()?&gt;&lt;/a&gt;  - &lt;?=$comment-&gt;text?&gt;&lt;/li&gt;
&lt;?endforeach?&gt;
&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<h3>Reflexión final</h3>
<p>Como has visto, en cuestión de minutos  tenemos una mini aplicación en PHP que nos permite añadir y listar  registros de una base de datos de una forma muy ordenada y sin tocar ni  una letra de código SQL.</p>
<p>No llevaría mucho tiempo añadir un sistema de  login de usuarios y que pudiesen editar y borrar los comentarios,  gracias a los métodos save() y delete() de la clase Doctrine_Record.<br />
﻿<br />
¡Nos vemos en la próxima publicación!</p>


<p>Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/como-validar-un-formulario-con-php-y-javascript-jquery/' rel='bookmark' title='Permanent Link: Cómo validar un formulario utilizando PHP y Javascript (jQuery)'>Cómo validar un formulario utilizando PHP y Javascript (jQuery)</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-a-object-relational-mapping-orm/' rel='bookmark' title='Permanent Link: Introducción a Object-Relational Mapping (ORM)'>Introducción a Object-Relational Mapping (ORM)</a></li>
<li><a href='http://web.ontuts.com/tutoriales/creando-una-capa-de-conexion-abstracta-a-base-de-datos-con-php/' rel='bookmark' title='Permanent Link: Creando una capa de conexión abstracta a base de datos con PHP'>Creando una capa de conexión abstracta a base de datos con PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/sesiones-en-php/' rel='bookmark' title='Permanent Link: Sesiones en PHP'>Sesiones en PHP</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/utilizando-doctrine-como-orm-en-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introducción a Microsoft Silverlight: Parte II</title>
		<link>http://web.ontuts.com/tutoriales/introduccion-a-microsoft-silverlight-parte-ii/</link>
		<comments>http://web.ontuts.com/tutoriales/introduccion-a-microsoft-silverlight-parte-ii/#comments</comments>
		<pubDate>Mon, 28 Jun 2010 12:09:07 +0000</pubDate>
		<dc:creator>Eugenio Estrada</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[microsoft]]></category>
		<category><![CDATA[silverlight]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=4375</guid>
		<description><![CDATA[Más vale tarde que nunca... Espero que la espera haya valido la pena. Tras la primera parte, quedó pendiente cómo trabajar con Silverlight y ver un poco de código. Para la desesperación de unos pocos, en esta segunda parte he continuado la introducción a la plataforma con más gráficos y diagramas. Pero finalmente, hemos hecho una pequeña demo para ver su potencial.


Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/introduccion-a-microsoft-silverlight-parte-i/' rel='bookmark' title='Permanent Link: Introducción a Microsoft Silverlight: Parte I'>Introducción a Microsoft Silverlight: Parte I</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-comprension-y-utilizacion-de-los-css-sprites-parte-i/' rel='bookmark' title='Permanent Link: Introducción, Comprensión y Utilización de los CSS Sprites: Parte I'>Introducción, Comprensión y Utilización de los CSS Sprites: Parte I</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-comprension-y-utilizacion-de-los-css-sprites-parte-ii/' rel='bookmark' title='Permanent Link: Introducción, Comprensión y Utilización de los CSS Sprites: Parte II'>Introducción, Comprensión y Utilización de los CSS Sprites: Parte II</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>Este tutorial forma parte de una serie</h3>
<ul>
<li><a href="http://web.ontuts.com/tutoriales/introduccion-a-microsoft-silverlight-parte-i/">Ir a la 1ª Parte &#8211; Introducción a Microsoft Silverlight: Parte I</a></li>
<li><a href="http://web.ontuts.com/tutoriales/introduccion-a-microsoft-silverlight-parte-ii/">Ir a la 2ª Parte &#8211; Introducción a Microsoft Silverlight: Parte II</a></li>
</ul>
<h3>Multiplataforma</h3>
<p>En <a href="http://web.ontuts.com/tutoriales/introduccion-a-microsoft-silverlight-parte-i/">la primera parte</a> nos centramos en qué y para qué es Silverlight. Una vez que ya lo sabemos, lo más importante es saber cómo lo hace. Veremos cómo está construido y solo así podremos entender, entre otras cosas, sus limitaciones. Como dijimos en la primera parte, Silverlight es un plugin que se instala en nuestro navegador. La cantidad de navegadores y plataformas soportadas era muy amplía y esto supone una gran dificultad.</p>
<p>Esto estriba en que cada navegador y cada plataforma tiene sus propias limitaciones. Esto va a provocar que el plugin del navegador tenga esas mismas limitaciones. Por si eso no fuera poco, las aplicaciones Silverlight fuera del navegador tendrán limitaciones y pequeños matices distintos por su propia naturaleza. Por lo tanto, en resumen, Silverlight tendrá tantas limitaciones como el total de sus plataformas soportadas tengan.</p>
<p>Si eso no se hiciese de esa forma, tendríamos un problema, ya que una aplicación que funciona en un sistema, podría no funcionar en otro. Por poner algún ejemplo, desde una aplicación Silverlight no se puede tener acceso a los directorios del sistema o desde una aplicación fuera del navegador, el programador no puede abrir un enlace en el navegador, si no es el usuario al que se pide confirmación explícitamente.</p>
<blockquote><p>La <strong>Platform Adaptation Layer (PAL)</strong>, es la encargada de abstraer a Silverlight de la plataforma de donde se ejecuta. Lo que permite que poco a poco se vayan pudiendo ir incorporando características del sistema a la plataforma de Silverlight, por ejemplo las más deseadas son: acceso a la WebCam y servicios de impresión de documentos.</p></blockquote>
<p>Partiendo de esta base, podremos analizar que tiene consigo Silverlight.</p>
<h3>Recursos</h3>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen1.png" alt="" /></center></p>
<p>Silverlight trae consigo una serie de clases, divididas en distintas tecnologías o agrupaciones:</p>
<ul>
<li><strong>Base Class Library (BCL)</strong>: Contiene todo lo básico para trabajar con cualquier lenguaje .NET: colecciones, genéricos, criptografía, tipos básicos, hilos, etc. Aquí estará todo aquello que sea de carácter general.</li>
<li><strong>Windows Communication Foundation (WCF)</strong>: Contiene todos los metodos necesarios para las comunicaciones, como servicios web, serialización XML y JSON, RSS y todo tipo de mecanismos para las comunicaciones.</li>
<li><strong>Acceso a datos (ADO.NET)</strong>: Tiene todos los métodos necesarios para el manejo de datos, tanto jerárquicos como el XML, secuenciales como los Streams, consultas declarativas integradas en el lenguaje (LinQ), enlace a datos en la interfaz y demás.</li>
<li><strong>Dynamic Language Runtime (DLR)</strong>: Nos permite hacer uso de los lenguajes de programación dinámicos como JavaScript Managed, IronPython, IronRuby o lenguajes funcionales como F#.
<li><strong>Windows Presentation Foundation (WPF)</strong>: Este quizás es el más importante de Silverlight, ya que es donde engloba el Presentation Core y el XAML.</li>
<ul>
<li><strong>Presentation Core</strong>: Contiene todos los controles, el acceso a derechos de contenido multimedia (DRM), el DeepZoom, los métodos de entrada (teclado, ratón, pantalla), todo el sistema de animaciones, la forma de integrar los enlaces a datos en la propia interfaz, etc.</li>
<li><strong>XAML</strong>: Es la gran estrella en todo esta tecnología, ya que con muy pocas líneas de código de marcado, nos permite instanciar objetos que de forma imperativa estábamos perdiendo mucho tiempo. Llegando al punto de la integración de equipos de diseño gráfico donde no se tendrían que mezclar con herramientas de programadores.</li>
</ul>
</li>
</ul>
<p>Si bien ahora, todo esto te queda un poco grande, a medida que avancemos en este tutorial verás viendo como cada una de las partes que aquí simplemente describimos, van cobrando sentido en nuestras aplicaciones Silverlight.</p>
<h3>¿Cómo funciona?</h3>
<p>Cuando compilamos con Silverlight / .NET, lo que estamos haciendo es, desde un punto de vista de la plataforma (sin ningún lenguaje de programación especifico), coger código someterlo a un compilador que lo transforma en lo que Microsoft llama ensamblado. Dicho ensamblado escrito en un lenguaje similar a ensamblador, en tiempo de ejecución se compila a código nativo (normalmente en el primer uso nada más) y este es el que se ejecuta. Queda bien ilustrado con la siguiente figura:</p>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen2.png" alt="" /></center></p>
<p>En el caso de Silverlight, contamos con un proceso intermedio:<br />
<center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen3.png" alt="" /></center></p>
<p>Esto se debe a que la generación a código nativo la hace el propio plugin de Silverlight en el navegador. Además una aplicación Silverlight puede contener X ensamblados y X recursos (imágenes, videos, etc.), eso no se debería descargar elemento a elemento, sino que es mejor empaquetarlo en un único fichero comprimido (con el algoritmo gzip). Y para eso se crear el fichero .xap, ya que es el que se mandará el navegador.</p>
<blockquote><p>El empaquetado de recursos tienes sus matices, es bueno incluirlo en el XAP siempre y cuando sean recursos de poco peso para la aplicación. En caso de que sea un tamaño considerable, este se debería descargar asíncronamente bajo petición de la aplicación (y cacheado posteriormente a poder ser).</p></blockquote>
<h3>El diseñador tiene su sitio</h3>
<p>En la primera parte apenas citamos el <strong>XAML (Xml Application Markup Language)</strong>, un nuevo lenguaje creado para Silverlight, a lo largo de esta segunda parte hemos hablado de lo que nos proporciona la plataforma para manejarlo. Pero solo hablamos muy por encima de lo que era.</p>
<p>XAML ha nacido con varios objetivos muy claros, el más claro es la división entre diseñador y programador, de tal forma que el diseñador solamente tenga que trabajar con el XAML usando los recursos que el programador con XAML y el lenguaje que escoja le provea, pero de esto hablaremos más adelante.</p>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen4.png" alt="" /></center></p>
<p>Pero además intenta llevar a su máxima expresión el patrón <a href="http://es.wikipedia.org/wiki/Modelo_Vista_Controlador">Módelo-Vista-Controlador (MVC)</a>. Pero para su adaptación a Silverlight, Microsoft ha creado un derivado de este patrón llamado <a href="http://en.wikipedia.org/wiki/Model_View_ViewModel">Modelo-Vista-VistaModelo</a>, con las modificaciones que se hizo sobre el MVC original, se omite el Controlador y en cambio se crea un una adaptación del Modelo para cada Vista y a mayores la Vista puede acceder directamente al Modelo.</p>
<h3>Manos a la obra</h3>
<p>Hemos hablado de que el programador y el diseñador pueden convivir en un proyecto en paralelo, para ello hay que definir bien las funciones de cada uno y ver cómo pueden colaborar entre ellos. Sobre el ciclo de vida completo hablaremos en próximas entregas, ahora haremos un vista rápida de cómo pueden colaborar.</p>
<p>Aun que, de momento, no hemos visto nada de código. Podemos ir hablando de cómo pueden colaborar los dos roles principales en el desarrollo de una aplicación Silverlight.</p>
<p>Normalmente a la hora de diseñar una interfaz contamos con 3 partes fundamentales: <strong>esbozar, prototipar y diseñar/desarrollar</strong>. Normalmente el esbozo lo hace el analista en colaboración con el cliente, el prototipo lo hace solo el diseñador y diseño y desarrollo final lo hacen entre programador y diseñador, cada uno su rol.</p>
<p>En este caso el proceso sería el siguiente:</p>
<ul>
<li>El analista hace el esbozo (en blanco y negro) ya usando los controles de Silverlight (con un estilo especial para remarcar que es un esbozo)</li>
<li>El diseñador partiendo de ese esbozo hace un prototipo funcional (con datos de ejemplo y sin procesos de negocio)</li>
<li>El programador otorga al prototipo los procesos de negocio para ser completamente funcional</li>
</ul>
<p>Exceptuando el primer paso los dos siguientes pueden convivir juntos, el programador puede darle funcionalidad sin molestar al diseñador.</p>
<p>Pero el papel del programador no se limita a eso. Este le puede preparar recursos al diseñador para que use:</p>
<ul>
<li><strong>Controles de usuario</strong>: A pesar de que Silverlight tiene una buena cantidad de controles, que además son ampliados por la comunidad en el <strong>Silverlight Toolkit</strong>, el programador puede desarrollar sus propios controles para que el diseñador pueda usar directamente en la interfaz.</li>
<li><strong>Comportamientos</strong>: Los comportamientos (conocidos como Behaviors) se usan para añadir funcionalidad a controles ya sea de forma genérica a cualquiera existente o alguno específico. Algún ejemplo de ellos podría ser una máquina de físicas para otorgar a los controles propiedades físicas como gravedad.</li>
<li><strong>Efectos</strong>: Los efectos se pueden desarrollar usando el lenguaje <strong>HLSL </strong>y las librerías de <em>DirectX</em>. Con ello creamos los llamados <em>Pixel Shaders</em>. Por defecto son dos los que tienen el <strong>Blur </strong>(Difuminado) y <strong>DropShadow </strong>(Sombra).</li>
</ul>
<p>En la primera demo de este tutorial vamos a ver como empezar a hacer algo con Silverlight. Recuerdo que en la primera entrega cité el software necesario para programar con Silverlight. A día de hoy tenemos otra alternativa, de la cual hablaremos en la siguiente entrega, que es MonoDevelop con soporte a <a href="http://www.go-mono.com/moonlight/">Moonlight </a>(Compatible con Silverlight 2.0 y 3.0 de forma experimental).</p>
<p>Esta primera demo la haremos con Visual Studio 2010 y Silverlight 4 SDK. Para eso veremos el proceso para crear un nuevo proyecto:</p>
<p>Abrimos <strong>Visual Studio 2010 </strong>y creamos un nuevo proyecto: Seleccionamos Visual C#&gt;Silverlight&gt;Silverlight Application</p>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen5.png" alt="" /></center></p>
<p>A continuación nos preguntará si queremos crear también un proyecto Web que aloje la aplicación, para esta primera demo le diremos que no.</p>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen6.png" alt="" /></center></p>
<p>Como podéis ver el proyecto generado tiene 2 ficheros, acabamos con extensión <strong>XAML</strong>:</p>
<ul>
<li><strong>App.xaml</strong>: Todas las aplicaciones Silverlight lo tienen, es el encargado de gestionar al carga del contenido, el que gestiona los errores como último nivel, cuando la aplicación se inicia, etc. En general se encarga de todo el ciclo de vida de la aplicación Silverlight.</li>
<li><strong>MainPage.xaml</strong>: Será el contenido principal que se pintará al ejecutar nuestra aplicación.</li>
</ul>
<p>Para nuestra primera <em>demo</em> tan solo vamos a dibujar los últimos artículos publicados en Ontuts, para ello debemos editar el fichero MainPage.xaml y añadir un control <strong>ListBox</strong>.</p>
<pre name="code" class="xml">
&lt;UserControl x:Class="SilverlightApplication1.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
      &lt;Grid x:Name="LayoutRoot" Background="White"&gt;
            <strong>&lt;ListBox x:Name="TitularesListBox" FontSize="18" /&gt;</strong>
      &lt;/Grid&gt;
&lt;/UserControl&gt;
</pre>
<p>Para continuar, debemos añadir la referencia System.Xml.Linq.</p>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen7.png" alt="" /></center></p>
<p>Una vez lo tengamos añadido debemos editar el <strong>código C#</strong> de la página principal.</p>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen8.png" alt="" /></center></p>
<p>Y en el cargar los últimos titulares de Ontuts, usando su RSS, como se muestra a continuación:</p>
<pre name="code" class="c-sharp">
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Net;
using System.Xml.Linq;

namespace SilverlightApplication1
{
    public partial class MainPage : UserControl
    {
        Uri feed = new Uri("http://feeds.feedburner.com/web-ontuts", UriKind.Absolute);

        public MainPage()
        {
            InitializeComponent();
            Loaded += MainPage_Loaded;
        }

        void MainPage_Loaded(object sender, RoutedEventArgs e)
        {

            // El objeto WebClient nos permite descargar
            // conteindo de otras páginas
            var webClient = new WebClient();
            // Establecemos el método que gestionará la descarga
            webClient.DownloadStringCompleted += webClient_DownloadStringCompleted;
            // Solitamos la descarga del feed
            webClient.DownloadStringAsync(feed);
        }

        void webClient_DownloadStringCompleted(object sender, object args)
        {
            var e = (DownloadStringCompletedEventArgs)args;
            // Comprobamos que no hay error
            if (e.Error != null) return;
            // Parseamos mediante LinqToXml el resultado
            var doc = XDocument.Parse(e.Result);
            // Obtemos con una consulta Linq los titulos de las entradas
            var query = from n in XDocument.Parse(e.Result).Descendants("item")
                                           select n.Element("title").Value;
            // Establecemos los titulos al ListBox
            TitularesListBox.ItemsSource = query;
        }
    }
}
</pre>
<p>Una vez esté hecho, con refrescar la página (F5 en Windows) ya se nos cargará en el navegador nuestra aplicación y podremos depurarla.</p>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/imagen9.png" alt="" /></center></p>
<p>Y eso es todo, espero que haya quedado lo suficientemente claro <img src='http://web.ontuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<h3>Referencias</h3>
<p>Os dejo con algunas referencias y fuentes que me han ayudado e inspirado a completar esta segunda parte del tutorial.</p>
<h4>Enlaces</h4>
<ul>
<li><strong>Silverlight</strong>: <a href="http://silverlight.net">http://silverlight.net</a></li>
<li><strong>Visual Studio</strong>: <a href="http://msdn.microsoft.com/vstudio">http://msdn.microsoft.com/vstudio</a></li>
<li><strong>Expression</strong>: <a href="http://expression.microsoft.com">http://expression.microsoft.com</a></li>
<li><strong>Moonlight</strong>: <a href="http://www.go-mono.com/moonlight">http://www.go-mono.com/moonlight</a></li>
<li><strong>Eclipese Tools For Silverlight</strong>: <a href="http://www.eclipse4sl.org">http://www.eclipse4sl.org</a></li>
</ul>
<h4>Libros</h4>
<ul>
<li><a href="http://www.amazon.com/gp/product/073563887X?ie=UTF8&amp;redirect=true&amp;ref_=s9_simh_gw_p14_i7&amp;linkCode=shr&amp;camp=213733&amp;creative=393185&amp;tag=unapizcdejava-20">Microsoft Silverlight 4 Step by Step</a></li>
<li><a href="http://www.amazon.com/gp/product/1430272074?ie=UTF8&amp;ref_=pd_sim_b_6&amp;linkCode=shr&amp;camp=213733&amp;creative=393189&amp;tag=unapizcdejava-20">Pro Business Applications with Silverlight 4</a></li>
<li><a href="http://www.amazon.com/gp/product/143022407X?ie=UTF8&amp;ref_=sr_1_2&amp;s=books&amp;qid=1277715583&amp;sr=1-2&amp;linkCode=shr&amp;camp=213733&amp;creative=393181&amp;tag=unapizcdejava-20">Foundation Silverlight 3 Animation</a></li>
</ul>
<h3>Reflexión final: ¿Vamos a por un tercero?</h3>
<p>Otra vez me debo disculpar, por haber tardado tanto en escribir la segunda parte. Pero ahora, que <em>he cogido carrerilla</em>, no me quiero quedar aquí.</p>
<p>Comentad qué os parece el enfoque, qué os gustaría leer sobre el tema, etc. Estamos abiertos a propuestas para continuar siendo de interés para vosotros.</p>
<p>¡Nos vemos en la próxima publicación!</p>


<p>Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/introduccion-a-microsoft-silverlight-parte-i/' rel='bookmark' title='Permanent Link: Introducción a Microsoft Silverlight: Parte I'>Introducción a Microsoft Silverlight: Parte I</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-comprension-y-utilizacion-de-los-css-sprites-parte-i/' rel='bookmark' title='Permanent Link: Introducción, Comprensión y Utilización de los CSS Sprites: Parte I'>Introducción, Comprensión y Utilización de los CSS Sprites: Parte I</a></li>
<li><a href='http://web.ontuts.com/tutoriales/introduccion-comprension-y-utilizacion-de-los-css-sprites-parte-ii/' rel='bookmark' title='Permanent Link: Introducción, Comprensión y Utilización de los CSS Sprites: Parte II'>Introducción, Comprensión y Utilización de los CSS Sprites: Parte II</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/introduccion-a-microsoft-silverlight-parte-ii/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Aplicaciones Multilenguaje PHP</title>
		<link>http://web.ontuts.com/tutoriales/aplicaciones-php-multilenguaje/</link>
		<comments>http://web.ontuts.com/tutoriales/aplicaciones-php-multilenguaje/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 10:36:35 +0000</pubDate>
		<dc:creator>Iván Guardado</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[multilenguaje]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=4243</guid>
		<description><![CDATA[Damos un repaso a los distintos distintos métodos de implementación de páginas web multilenguaje, mostrando cómo implementar algunos de ellos. Si estás pensando en crear una web con contenido en varios idiomas... ¡tienes que leer esto!


Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/sesiones-en-php/' rel='bookmark' title='Permanent Link: Sesiones en PHP'>Sesiones en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/aprendiendo-a-utilizar-la-libreria-curl-en-php/' rel='bookmark' title='Permanent Link: Aprendiendo a utilizar la librería cURL en PHP'>Aprendiendo a utilizar la librería cURL en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/mini-aplicaciones-web-con-python-y-juno-parte-i/' rel='bookmark' title='Permanent Link: Mini Aplicaciones Web con Python y Juno: Parte I'>Mini Aplicaciones Web con Python y Juno: Parte I</a></li>
<li><a href='http://web.ontuts.com/tutoriales/mini-aplicaciones-web-con-python-y-juno-parte-ii/' rel='bookmark' title='Permanent Link: Mini Aplicaciones Web con Python y Juno: Parte II'>Mini Aplicaciones Web con Python y Juno: Parte II</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>Introducción</h3>
<p>Hoy en día, gracias a  Internet, tenemos la capacidad de llegar a cualquier persona del mundo,  desde al vecino del quinto hasta alguien perdido en algún lugar remoto  de las antípodas. Esto es una enorme ventaja que no se puede  desaprovechar cuando tienes un nicho de mercado global.</p>
<p>Si tu web ofrece  un servicio o información que puede ser consumido por cualquier persona  del mundo, tienes que cuidar muchos detalles para que el usuario final  se sienta satisfecho y conforme. Se tiene que sentir como en casa.</p>
<blockquote><p>
Es importante presentar al usuario una web adaptada a su localización, tanto en idioma como en formatos (fecha, moneda, etc&#8230;) de forma que su experiencia en el sitio sea buena y así, mejorar la calidad de las visitas.
</p></blockquote>
<p>Sin embargo, tener una  web en varios idiomas es algo complejo, tanto desde punto de vista  técnico (que es la parte que ocuparemos en este artículo) como de  mantenimiento. A continuación veremos los distintos métodos que puedes  usar para convertir tu aplicación PHP en multilenguaje.</p>
<h3>Formatos para las  traducciones</h3>
<p>La primera pregunta que te viene a la cabeza cuando comienzas  con el diseño de una aplicación multilenguaje es: ¿cómo hago para  abstraer los textos de las plantillas? ¿uso un array? ¿uso variables  globales? ¿un fichero csv?.</p>
<p>Bien, a continuación os muestro un listado  de los métodos más usados con sus ventajas e inconvenientes:</p>
<h4>Array</h4>
<p>Este es el formato más <strong>sencillo</strong> para los programadores, pero es ineficaz cuando tienes muchas  cadenas de texto o muchos idiomas a traducir. Sin embargo puede ser una  buena elección cuando se trata de un sitio pequeño con poco texto y en  el cual es el programador quien se encarga de hacer las traducciones (si  le pasas un array a alguna persona no técnica para traducir,  seguramente se cargue algo).</p>
<h4>Ficheros CSV</h4>
<p>Este formato es <strong>cómodo</strong> cuando es el cliente (persona no técnica) el que se encarga de la  traducción, pues es legible por cualquier editor de texto estándard. Sin  embargo, se debe de tener cuidado con la codificación UTF8.</p>
<h4>Gettext</h4>
<p>Este es uno de los  métodos <strong>más utilizados</strong>. <a href="http://es.wikipedia.org/wiki/Gettext">gettext</a> es la biblioteca GNU  de internacionalización usada para crear programas de interfaz en  múltiples lenguajes, y que en los últimos años ha dado el salto al mundo  web.</p>
<p>Es  un formato de traducción profesional, ya que no es legible como texto  plano, sino que necesitas un programa de edición como <a href="http://sourceforge.net/projects/poedit/">POEdit</a>, que evitará  problemas de codificación y hará del proceso de traducción una  experiencia más agradable. PHP <a href="http://www.php.net/manual/en/book.gettext.php">incorpora  funciones</a> para trabajar con esta librería de forma nativa.</p>
<h4>Ficheros Ini</h4>
<p>Tiene las <strong>mismas características</strong> que los ficheros <strong>CSV</strong>, lo único que cambia es la forma de  organizar los textos. Es un formato legible por el ser humano y  adecuado para que el cliente edite los ficheros sin complicaciones. Se  debe tener especial cuidado con la codificación UTF8.</p>
<h4>TMX</h4>
<p>Este formato será  usado por clientes que tengan varios sistemas que usan <strong>una única fuente de traducción</strong>, o cuando la fuente de traducción necesita ser  independiente del sistema (system-independent). TMX es un fichero basado  en XML que parece encaminado a ser el próximo estándard en la industria.</p>
<p>Este tipo de  ficheros son legibles por el ser humano, pero su procesamiento es más  lento que el de ficheros gettext.</p>
<h3>Usando gettext en PHP</h3>
<p>De todos los formatos  que he citado anteriormente, he elegido este para mostrar una  implementación porque para mí es el más cómodo y profesional tanto para los  traductores como para los programadores.</p>
<h4>Primer paso: Instalar un editor y crear un catálogo</h4>
<p>Como ya os he  comentado antes, para este formato se necesita un editor capaz de  manejar este tipo de ficheros. Yo os recomiendo <a href="http://sourceforge.net/projects/poedit/">POEdit</a>, que es fácil de  instalar y de aprender a manejar.</p>
<p>Antes de nada, tenéis que saber como funciona el programa, ya que, seguramente si no os lo dicen, no conseguiréis hacer nada con él. El concepto general de su funcionamiento sería el siguiente:</p>
<ul>
<li>Le especificamos en que directorio(s) se encuentran los ficheros que contendrán los textos traducibles.</li>
<li>Le especificamos cual será lar marca que identificará que textos son traducibles. Normalmente el guión bajo (_) .</li>
<li>El programa se encarga de buscar en todos los ficheros, los textos traducibles y los añade a una lista para su traducción.</li>
</ul>
<p>Vamos entonces a crear nuestro primer catálogo. Teniendo en cuenta que tenemos un fichero  como el siguiente en <i>/path/templates/template.php</i> en donde <strong>path</strong> es el directorio del proyecto:</p>
<pre name="code" class="html">
&lt;html&gt;
&lt;body&gt;
&lt;h1&gt;&lt;?=_("Hola  Mundo!")?&gt;&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<p>Aquí hay que destacar el uso de la funcion <strong>_</strong> (guión bajo). Dicha función nos ofrece dos cosas: por un lado, es un alias de la función de <strong>PHP gettext</strong>, por lo tanto se encargará automáticamente de traducir ese texto una vez configurado. Y por otro lado ayudará al <strong>POEdit</strong> a encontrar las cadenas de texto traducibles.</p>
<p>Abrimos <strong>POEdit</strong> y vamos  a <i>Archivo &gt; Nuevo catálogo</i> y rellenamos información relativa al  proyecto:</p>
<p><center><a href="http://web.ontuts.com/wp-content/uploads/2010/06/1.png"><img src="http://web.ontuts.com/wp-content/uploads/2010/06/1.png" alt="" title="gettext_1" class="alignnone size-full wp-image-4282" /></a></center></p>
<p>En la pestaña <strong>Carpetas</strong> debes indicar en que  directorio se encuentran los ficheros con textos traducibles:</p>
<p><center><a href="http://web.ontuts.com/wp-content/uploads/2010/06/2.png"><img src="http://web.ontuts.com/wp-content/uploads/2010/06/2.png" alt="" title="gettext_2" class="alignnone size-full wp-image-4283" /></a></center></p>
<p>Y en la pestaña <strong>Palabras Clave</strong> seleccionamos cual va  a ser el identificador de los textos traducibles. Como hemos dicho  antes, lo más común es el guion bajo (_):</p>
<p><center><a href="http://web.ontuts.com/wp-content/uploads/2010/06/3.png"><img src="http://web.ontuts.com/wp-content/uploads/2010/06/3.png" alt="" title="gettext_3" class="alignnone size-full wp-image-4284" /></a></center></p>
<p>Una vez acaba la configuración, os pedirá en  donde queréis guardar el fichero. Lo guardamos en <i>/path/locale/en_US/LC_MESSAGES/app.po</i> . En ese directorio,  se creará tambien un fichero <strong>app.mo</strong>, que es el que nos interesa.</p>
<h4>Segundo paso: Hacer la  traducción</h4>
<p>Una vez creado y guardado el catálogo, podemos comenzar la  traducción. Como ves, el programa ha detectado los textos traducibles y  te los sirve en bandeja para que los traduzcas. Más fácil imposible <img src='http://web.ontuts.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p><center><a href="http://web.ontuts.com/wp-content/uploads/2010/06/4.png"><img src="http://web.ontuts.com/wp-content/uploads/2010/06/4.png" alt="" title="gettext_4" width="540" class="alignnone size-medium wp-image-4285" /></a></center></p>
<h4>Tercer paso: Traducir desde PHP</h4>
<p>Como ya hemos dicho,  en PHP exite la función <a href="http://es2.php.net/manual/en/book.gettext.php">gettext</a> que nos permitirá  trabajar con el fichero que acabamos de generar. Creamos el fichero <i>/path/index.php</i></p>
<pre name="code" class="php">
&lt;?php
//Establecemos el  idioma a inglés
putenv('LC_ALL=en_US');
setlocale(LC_ALL, 'en_US.UTF8');

//Establecemos en que  directorio se encuentran las traducciones
bindtextdomain("app",  "./locale");

//Elegimos  el dominio (aka tabla o fichero)
textdomain("app");

// Incluyo el fichero  plantilla
require  'templates/template.php';
</pre>
<p>¡Así de sencillo! Ahora PHP ya se encarga de cargar los textos según el locale establecido. Pero&#8230;¿cómo lo encuentra si tenemos un esqueleto de carpetas bastante complejo? La respuesta es: tenemos ese esqueleto complejo porque es el que PHP espera tener. Analicemos directorio por directorio la ruta del fichero <strong>app.mo</strong>:</p>
<ul>
<li><i>/path/locale</i>: Con la función <strong>bindtextdomain</strong> le decimos a PHP que debe de buscar en ese directorio las traducciones.</li>
<li><i>/path/locale/en_US</i>: Al establecer la configuración local a en_US con las funciones <strong>putenv</strong> y <strong>setlocale</strong>, PHP buscará este directorio.</li>
<li><i>/path/locale/en_US/LC_MESSAGES</i>: este es un directorio en el que PHP busca por defecto.</li>
<li><i>/path/locale/en_US/LC_MESSAGES/app.mo</i>: Cargará este fichero porque así se le ha especificado en la función <strong>textdomain</strong>.</li>
</ul>
<h4>Cuarto paso: Agregando  nuevos textos</h4>
<p>Si vuelves a agregar algun texto a la plantilla, no pasa  nada, por defecto se mostrara sin traducir (que ya es algo mejor que  nada) y luego simplemente tienes que abrir el catálogo con el editor y  pinchar en el botón <i>Actualizar catálogo</i></p>
<p><center><a href="http://web.ontuts.com/wp-content/uploads/2010/06/5.png"><img src="http://web.ontuts.com/wp-content/uploads/2010/06/5.png" width="540" alt="" title="gettext_5" class="alignnone size-full wp-image-4286" /></a></center></p>
<p>Y el programa ya te saca un listado de los  nuevos textos que se han añadido:</p>
<p><center><a href="http://web.ontuts.com/wp-content/uploads/2010/06/6.png"><img src="http://web.ontuts.com/wp-content/uploads/2010/06/6.png" alt="" title="gettext_6" class="alignnone size-full wp-image-4287" /></a></center></p>
<h3>Reflexion final</h3>
<p>Como has visto,  existen múltiples metodos para incorporar sistema multilenguaje en tu  aplicación web. De ti, del usuario final, y del número de cadenas de  texto a traducir depende la elección entre uno u otro, ya que cualquiera  puede ser efectivo bajos determinadas condiciones. </p>
<p>Si te decantas por  usar gettext, te darás cuenta que lo único que tienes que hacer es  definir bien los catálogos y luego todo son ventajas, tanto para los  desarrolladores como para los traductores.</p>
<p>Espero que os haya parecido interesante el  artículo y que dejéis vuestra opinión sobre el tema&#8230;</p>
<p>¡Nos vemos en el  próximo artículo!</p>


<p>Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/sesiones-en-php/' rel='bookmark' title='Permanent Link: Sesiones en PHP'>Sesiones en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/aprendiendo-a-utilizar-la-libreria-curl-en-php/' rel='bookmark' title='Permanent Link: Aprendiendo a utilizar la librería cURL en PHP'>Aprendiendo a utilizar la librería cURL en PHP</a></li>
<li><a href='http://web.ontuts.com/tutoriales/mini-aplicaciones-web-con-python-y-juno-parte-i/' rel='bookmark' title='Permanent Link: Mini Aplicaciones Web con Python y Juno: Parte I'>Mini Aplicaciones Web con Python y Juno: Parte I</a></li>
<li><a href='http://web.ontuts.com/tutoriales/mini-aplicaciones-web-con-python-y-juno-parte-ii/' rel='bookmark' title='Permanent Link: Mini Aplicaciones Web con Python y Juno: Parte II'>Mini Aplicaciones Web con Python y Juno: Parte II</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/aplicaciones-php-multilenguaje/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Creando un Menú Desplegable y Elegante en jQuery</title>
		<link>http://web.ontuts.com/tutoriales/creando-un-menu-desplegable-en-jquery/</link>
		<comments>http://web.ontuts.com/tutoriales/creando-un-menu-desplegable-en-jquery/#comments</comments>
		<pubDate>Thu, 03 Jun 2010 10:22:49 +0000</pubDate>
		<dc:creator>Adrián Mato</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=3979</guid>
		<description><![CDATA[En este tutorial aprenderemos a crear nuestro propio menú desplegable basado en jQuery. Lo haremos partiendo de cero y explicándolo como siempre paso a paso, de forma que todos podamos comprender cómo plantear el problema y buscar una solución sencilla y efectiva.


Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/como-crear-un-menu-de-pestanas-elegante-en-jquery/' rel='bookmark' title='Permanent Link: Cómo crear un menú de pestañas elegante en jQuery'>Cómo crear un menú de pestañas elegante en jQuery</a></li>
<li><a href='http://web.ontuts.com/tutoriales/como-crear-un-menu-contextual-en-javascript-mediante-jquery/' rel='bookmark' title='Permanent Link: Cómo crear un Menú Contextual en Javascript mediante jQuery'>Cómo crear un Menú Contextual en Javascript mediante jQuery</a></li>
<li><a href='http://web.ontuts.com/tutoriales/mejorando-las-cajas-de-busqueda-mediante-jquery/' rel='bookmark' title='Permanent Link: Mejorando las cajas de búsqueda mediante jQuery'>Mejorando las cajas de búsqueda mediante jQuery</a></li>
<li><a href='http://web.ontuts.com/tutoriales/como-crear-un-plugin-para-jquery/' rel='bookmark' title='Permanent Link: Cómo crear un Plugin para jQuery'>Cómo crear un Plugin para jQuery</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>Introducción: ¿Qué vamos a hacer?</h3>
<p>Retomamos los tutoriales relacionados con <a href="/tag/javascript">javascript</a> y <a href="/tag/jquery">jQuery</a> de la mano de este control en forma de <strong>menú desplegable</strong>.</p>
<p>Al contrario que otros controles de este tipo, para desplegar este control, el usuario sólo tendrá que pasar el ratón por encima, en lugar de hacer click. Este será el aspecto final del control a crear:<br />
<center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/dropdown_final.png" alt="Vista previa" /></center></p>
<p>La idea es que las distintas opciones sean mostradas en formato lista (&lt;ul&gt;), de forma que cada elemento de la lista sea un enlace que te traslade a una nueva sección en la que aparezca marcada la nueva opción seleccionada.</p>
<p>El control no necesitará hacer el cambio entre sección actualmente activa y la seleccionada, ya que eso se controlaría a la hora de generar la salida html en la lógica de programación (PHP, Java, Python&#8230; o lo que quiera que uséis).</p>
<p><strong>Actualización:</strong> Por cierto&#8230; veo que algunas personas no lo han entendido bien: estamos imprimiendo una salida HTML limpia, por tanto los robots y rastreadores de Google y derivados, podrán indexar y seguir perfectamente todas esas opciones de nuestro menú.</p>
<p>¡Vamos a por el primer paso del tutorial!</p>
<h3>Paso 1: La estructura HTML</h3>
<p>Como acostumbramos a realizar en nuestros tutoriales, mantendremos una estructura sencilla, intentando que sea vistosa pero sobretodo clara a la hora de comprender qué estamos haciendo:</p>
<pre name="code" class="html">
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
&lt;html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="es-ES">
&lt;head>
    &lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    &lt;title>Cómo crear un menú de pestañas elegante en jQuery&lt;/title>
    &lt;link rel="stylesheet" href="css/main.css" type="text/css" media="screen" />
&lt;/head>
&lt;body>
    &lt;div class="wrapper">
        &lt;h1>Creando un menú desplegable en jQuery&lt;/h1>
        &lt;ul class="dropdown">
			&lt;li class="active">Visualizando: &lt;span>Tutoriales&lt;/span>&lt;/li>
			&lt;li class="first">&lt;a href="#">Recursos&lt;/a>&lt;/li>
			&lt;li>&lt;a href="#">Inspiración&lt;/a>&lt;/li>
			&lt;li>&lt;a href="#">Contacto&lt;/a>&lt;/li>
			&lt;li class="last">&lt;a href="#">Ver más...&lt;/a>&lt;/li>
		&lt;/ul>
    &lt;/div>
    &lt;script type="text/javascript" src="jquery.js">&lt;/script>
    &lt;script type="text/javascript" src="main.js">&lt;/script>
&lt;/body>
&lt;/html>
</pre>
<p>Esto es lo que deberíamos tener actualmente:<br />
<center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/dropdown_step1.png" alt="Vista previa paso 1" /></center></p>
<p>Básicamente lo único relevante de este paso es comprender que estamos aplicando la clase de CSS &#8220;<strong>dropdown</strong>&#8221; a nuestra lista, con la que estaremos advirtiendo a nuestro futuro código javascript que será un control del tipo desplegable.</p>
<p>Vamos pues a darle un poco de estilo al ejemplo antes de ponernos con la lógica en javascript&#8230;</p>
<h3>Paso 2: Aplicando estilo con CSS a nuestro menú desplegable</h3>
<p>En este caso, el código CSS es muy básico y se centra principalmente en el aspecto visual del control de menú desplegable que estamos creando:</p>
<pre name="code" class="css">
@CHARSET "UTF-8";
/*
Author: Adrian Mato
Author URI: http://web.ontuts.com
*/

/******* GENERAL RESET *******/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em,
font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody,
tfoot, thead, tr, th, td {
    border:0pt none;
    font-family:inherit;
    font-size:100%;
    font-style:inherit;
    font-weight:inherit;
    margin:0pt;
    padding:0pt;
    vertical-align:baseline;
}
body{
    line-height: 1em;
    font-size: 14px;
    font-family: Arial, Helvetica, sans-serif;
    margin: 0pt;
    cursor: default;
    color: #fff;
    background: #262626 url("bg.png") repeat scroll 0 0;
}
table{
    border-collapse: separate;
    border-spacing: 0pt;
}
strong{
    font-weight: 700;
}
caption, th, td{
    font-weight:normal;
    text-align:left;
}
blockquote:before, blockquote:after, q:before, q:after{
    content:"";
}
blockquote, q{
    quotes:"" "";
}
pre{
    font-family: Arial, Helvetica, sans-serif;
}
input{
    border: 0;
    margin: 0;
    font-family: Arial, Helvetica, sans-serif;
}
textarea{
    font-family: Arial, Helvetica, sans-serif;
    color: #888888;
    padding: 7px 3px 0 4px;
    font-size: 11px;
}
select{
    font-size: 11px;
    color: #888888;
    background: #fff;
    font-family: Arial, Helvetica, sans-serif;
    border: 1px solid #CAD2CE;
}
ul{
    list-style: none;
    list-style-type: none;
    list-style-position: outside;
}
a{
    cursor: pointer;
    color: #ece6bd;
    text-decoration: underline;
    outline: none !Important;
}
html,body{
    height:100%;
}
.clear{
    clear: both;
    height: 0;
    visibility: hidden;
    display: block;
    line-height: 0;
}
.clearfix{
    overflow: hidden;
}
.italic{
    font-style: italic;
}
/******* /GENERAL RESET *******/

/******* GENERAL *******/
h1{
    color: #fff;
    font-size: 26px;
    line-height: 3em;
}
h2{
    line-height: 2em;
    margin-top: 30px;
    margin-bottom: 20px;
    color: #e4e1cd;
}
.wrapper{
    width: 982px;
    margin: 0pt auto;
    padding-top: 10px;
}
/******* /GENERAL *******/

/******* CONTENT *******/
h3{
    line-height:1em;
    vertical-align:middle;
    height:48px;
    padding:10px 10px 10px 52px;
    font-size:32px;
    color:#E4E1CD;
}
/******* /CONTENT *******/

/******* MENU *******/
ul.dropdown{
	width: 200px;
	border: 1px solid #000;
	border-radius: 5px;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	background: #1a1a1a;
	margin-top: 2em;
}
ul.dropdown li{
	display: none;
	font-size: 12px;
}
ul.dropdown li.active{
	display: block;
	color: #8c8c8c;
	font-size: 14px;
	padding: 12px;
	color: #555;
	border-top: 1px solid #313131;
	border-radius: 4px;
	-moz-border-radius: 4px;
	-webkit-border-radius: 4px;
}
ul.dropdown li.active span{
	background: transparent url("dropdown.png") no-repeat scroll right center;
	padding-right: 24px;
	color: #8c8c8c;
}
ul.dropdown li a{
	display: block;
	text-decoration: none;
	padding: 8px 8px 8px 10px;
	background: #1e1e1e;
	border-bottom: 1px solid #171717;
}
ul.dropdown li.last a{
	border:0;
}
ul.dropdown li.first a{
	border-top: 3px solid #131313;
}
ul.dropdown li a:hover{
	background: #232323;
	color: #fff;
	padding-left: 11px;
}
/******* /MENU *******/
</pre>
<p>La clase <strong>dropdown</strong> será con la que identificaremos el control y sobre la cual aplicaremos el resto de propiedades a los distintos elementos.</p>
<p>Así pues todos los <strong>elementos</strong> de la lista <strong>compartirán estilo</strong> excepto la <strong>clase active</strong> la cual aparecerá siempre en el primer puesto de la lista, y será la que identifique el elemento actualmente seleccionado / activo.</p>
<p>Este es el aspecto que presentará ahora nuestro control:</p>
<p><center><img src="http://web.ontuts.com/wp-content/uploads/2010/06/dropdown_step2.png" alt="Vista previa paso 2" /></center></p>
<p>Como habréis podido comprobar, ahora <strong>no se están mostrando</strong> los elementos de la lista, salvo el elemento activo. Sólo necesitamos crear la lógica para que se muestren / oculten a nuestro antojo via javascript&#8230;</p>
<p>¡Vamos a ello!</p>
<h3>Paso 3: Añadiendo interacción con javascript mediante jQuery</h3>
<p>Repasemos qué es lo que queremos de nuestro control en forma de menú despegable antes de aplicar la interacción:</p>
<ul>
<li><strong>Mostrar</strong> opciones al pasar el ratón por encima.</li>
<li><strong>Ocultar</strong> opciones al mover el ratón de encima.</li>
<li><strong>Reutilizable</strong> para tener múltiples controles en una misma página.</li>
</ul>
<p>Con esto en mente, necesitaremos identificar (mediante selectores de jQuery) los elementos de clase &#8220;<strong>dropdown</strong>&#8221; para <strong>reconocerlos</strong> como controles de <strong>menú desplegable</strong>.</p>
<p>Antes de explicar la parte de los eventos, echemos un vistazo al código final:</p>
<pre name="code" class="javascript">
//variable global para controles dropdown
var menu = $("ul.dropdown");

//control de eventos
$(this.document).ready(function(){
	menu.mouseover(function(){
		displayOptions($(this).find("li"));
	});
	menu.mouseout(function(){
		hideOptions($(this));
	});
})

//funcion que MUESTRA todos los elementos del menu
function displayOptions(e){
	e.show();
}
//funcion que OCULTA los elementos del menu
function hideOptions(e){
	e.find("li").hide();
	e.find("li.active").show();
}
</pre>
<p>El código como podéis comprobar es relativamente sencillo, y en el podemos ver la asignación de los eventos &#8220;al pasar por encima&#8221; y &#8220;al quitar el ratón de encima&#8221;, los cuales llaman a las funciones:</p>
<ul>
<li><strong>displayOptions()</strong>: Muestra todos los elementos de la lista inicialmente ocultos al pasar el ratón por encima.</li>
<li><strong>hideOptions()</strong>: Oculta todos los elementos de la lista, y luego vuelve a mostrar el elemento activo.</li>
</ul>
<p>La razón por la que mostramos nuevamente el elemento activo es porque el selector de jQuery que estamos empleando está <strong>ocultando todos</strong> los elementos de la lista, entre los que se incluye el activo, pero deseamos que se <strong>mantenga visible</strong>.</p>
<p>Antes de dar por finalizado el tutorial, me gustaría resaltar otro detalle del código anterior: la <strong>función find()</strong>, mediante la cual localizamos los elementos del <strong>control</strong> que está <strong>recibiendo los eventos</strong>.</p>
<blockquote><p>Siempre que programes controles reutilizables, trata de trabajar con <strong>referencias al objeto</strong> activo, de esta forma podrás <strong>reutilizarlos</strong> o crear <strong>plugins</strong> de forma realmente sencilla.</p></blockquote>
<p>Podríamos estar utilizando selectores de jQuery genéricos pero recordad: <strong>queremos</strong> que nuestro <strong>control</strong> pueda <strong>aparecer en más de una ocasión</strong> en una misma página, por tanto trabajamos con <strong>referencias al objeto activo</strong>, en lugar de a todos los objetos que contengan la clase de CSS dropdown.</p>
<p>Con todo ello, <strong>generar un plugin</strong> apartir de este planteamiento sería realmente trivial. Si bien no lo hemos hecho en este tutorial, sí que hemos explicado anteriormente <a href="http://web.ontuts.com/tutoriales/como-crear-un-plugin-para-jquery/">cómo crear plugins en jQuery</a>, echadle un vistazo que es realmente útil.</p>
<h3>Reflexión final</h3>
<p>Una vez más vuelvo a insistir en lo de siempre: con un poco de <strong>curiosidad</strong> y <strong>ganas</strong> por profundizar en lo que nos interesa podemos -en este caso- crear controles dinámicos en javascript de forma realmente sencilla.</p>
<p>Lo más importante es <strong>tener claro</strong> qué es lo que <strong>queremos que haga nuestro control</strong> (sus eventos, etc&#8230;) y luego buscar la mejor solución posible a cada uno de esos requisitos ya sea con jQuery u otras librerías javascript que manejemos.</p>
<p>Espero que os haya resultado útil y&#8230; ¡Nos vemos en el próximo artículo!</p>


<p>Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/como-crear-un-menu-de-pestanas-elegante-en-jquery/' rel='bookmark' title='Permanent Link: Cómo crear un menú de pestañas elegante en jQuery'>Cómo crear un menú de pestañas elegante en jQuery</a></li>
<li><a href='http://web.ontuts.com/tutoriales/como-crear-un-menu-contextual-en-javascript-mediante-jquery/' rel='bookmark' title='Permanent Link: Cómo crear un Menú Contextual en Javascript mediante jQuery'>Cómo crear un Menú Contextual en Javascript mediante jQuery</a></li>
<li><a href='http://web.ontuts.com/tutoriales/mejorando-las-cajas-de-busqueda-mediante-jquery/' rel='bookmark' title='Permanent Link: Mejorando las cajas de búsqueda mediante jQuery'>Mejorando las cajas de búsqueda mediante jQuery</a></li>
<li><a href='http://web.ontuts.com/tutoriales/como-crear-un-plugin-para-jquery/' rel='bookmark' title='Permanent Link: Cómo crear un Plugin para jQuery'>Cómo crear un Plugin para jQuery</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/creando-un-menu-desplegable-en-jquery/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Presentando y Utilizando Google Font API y Google Font Directory</title>
		<link>http://web.ontuts.com/tutoriales/presentando-y-utilizando-google-font-api-y-google-font-directory/</link>
		<comments>http://web.ontuts.com/tutoriales/presentando-y-utilizando-google-font-api-y-google-font-directory/#comments</comments>
		<pubDate>Thu, 20 May 2010 10:58:53 +0000</pubDate>
		<dc:creator>Adrián Mato</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[font]]></category>
		<category><![CDATA[fuentes]]></category>
		<category><![CDATA[google]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=3815</guid>
		<description><![CDATA[Google lo ha vuelto a hacer. Ayer mismo nos ha sorprendido anunciando Google Font API y Google Font Directory. Analizamos estos dos nuevos proyectos con los que pretenden facilitarnos el uso de fuentes de letra personalizadas en nuestras páginas de manera relamente sencilla. ¡Tutorial incluído!


Related posts:<ol><li><a href='http://web.ontuts.com/noticias/google-estrena-la-nueva-version-de-google-images/' rel='bookmark' title='Permanent Link: Google estrena la nueva versión de Google Images'>Google estrena la nueva versión de Google Images</a></li>
<li><a href='http://web.ontuts.com/recursos/fuentes-de-letra-gratis-y-de-calidad-volumen-6/' rel='bookmark' title='Permanent Link: Fuentes de Letra Gratis y de Calidad Volumen #6'>Fuentes de Letra Gratis y de Calidad Volumen #6</a></li>
<li><a href='http://web.ontuts.com/recursos/fuentes-de-letra-gratis-y-de-calidad-volumen-8/' rel='bookmark' title='Permanent Link: Fuentes de Letra Gratis y de Calidad Volumen #8'>Fuentes de Letra Gratis y de Calidad Volumen #8</a></li>
<li><a href='http://web.ontuts.com/general/sorteamos-23-invitaciones-para-google-wave/' rel='bookmark' title='Permanent Link: Sorteamos 23 Invitaciones para Google Wave'>Sorteamos 23 Invitaciones para Google Wave</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>Introduciendo Google Font Api y Google Font Directory</h3>
<p>Ayer 19 de Mayo de 2010 los chicos de Google <a href="http://googlecode.blogspot.com/2010/05/introducing-google-font-api-google-font.html">presentaban</a> dos grandes y buenas noticias para los desarrolladores / diseñadores web: <strong>Google Font API</strong> y <strong>Google Font Directory</strong>.</p>
<p>Su idea es compartir con todos nosotros una biblioteca de fuentes de letra amplia, de gran calidad y open source, conocida como <a href="http://code.google.com/webfonts">Google Font Directory</a>.</p>
<blockquote><p>Google comparte una gran biblioteca de fuentes de letra open source listas para utilizar de forma realmente sencilla mediante su Google Font API.</p></blockquote>
<p>Estas fuentes de letra podrán ser utilizadas mediante la <a href="http://code.google.com/intl/es-ES/apis/webfonts/docs/getting_started.html">Google Font API</a> con el fin de mejorar el aspecto visual de todos nuestros diseños de manera realmente rápida y sencilla.</p>
<p>Han pasado muchos años y las fuentes de letra &#8220;no estándar&#8221; siguen sin tener un uso claro y sencillo, teniendo que recurrir a hacks, librerías javascript, objetos flash&#8230; todo esto pretende cambiar con CSS3 y su propiedad @font-face, y Google tratará de servir todas estas fuentes através de la nube según los requerimientos de cada navegador.</p>
<h3>Ventajas y compatibilidad con múltiples navegadores</h3>
<p>Todos aquellos que decidan usar la Google Font API tendrán asegurada la compatibilidad con múltiples navegadores para cualquiera de las fuentes de letra de su biblioteca. Todas estas fuentes tendrán las ventajas de un texto normal y corriente:</p>
<ul>
<li>Se comportan como <strong>texto normal</strong>, no hay canvas ni otros objetos.</li>
<li><strong>Indexables</strong> y legibles para los buscadores.</li>
<li><strong>Visualmente atractivas</strong> y sin pérdida de calidad al hacer zoom.</li>
<li><strong>Accesibles</strong> a todo tipo de lectores de pantalla.</li>
<li><strong>Rápido</strong> a la hora de cargar las fuentes.</li>
</ul>
<p>Por si fuera poco, navegadores &#8220;obsoletos&#8221; como <strong>Internet Explorer 6</strong> serán perfectamente soportados por la Google Font API sin aparantemente ningún tipo de hack, canvas ni derivados.</p>
<h3>Cómo utilizar la Google Font API</h3>
<p>Como nos comentan en su blog, el método de uso es realmente sencillo. Tan sólo necesitamos crear un enlace a la Google Font API especificando las fuentes de letra que deseamos usar y aplicarla mediante selectores CSS en donde deseemos:</p>
<pre name="code" class="html">
&lt;!DOCTYPE html>
&lt;html>
	&lt;head>
		&lt;meta charset="utf-8">
		&lt;title>Ontuts - Ejemplo de Google Fonts API&lt;/title>
		&lt;link href='http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz' rel='stylesheet' type='text/css'>
		&lt;style>
			body{
				font-family: 'Yanone Kaffeesatz', serif;
				font-size: 72px;
				text-shadow: 4px 4px 4px #bbb;
				font-weight: 700;
				text-align: center;
			}
		&lt;/style>
&lt;/head>
&lt;body>
	¡Ontuts arrasa por donde pasa!
&lt;/body>
&lt;/html>
</pre>
<p>Este sería el resultado final:<br />
<center><a href="http://web.ontuts.com/wp-content/uploads/tutoriales/google_fonts_api/index.html"><img src="http://web.ontuts.com/wp-content/uploads/2010/05/sample_fontsapi.png" alt="Ejemplo Google Fonts API" /></a></center></p>
<p>Es realmente potente y sencillo, ¿verdad? No dejéis de mirar el html final que nos genera, es transparente, texto plano de toda la vida&#8230; pero con otro aspecto. Nada de canvas, objetos flash ni similares.</p>
<h3>Reflexión final</h3>
<p>Si bien todavía está en beta, como diseñador web me resulta realmente interesante ver que por fin podremos empezar a plantearnos el uso de fuentes de letra distintas a las habituales, que aporten mayor solidez a nuestros diseños sin necesidad de hacks, trucos baratos o imágenes en lugar de texto.</p>
<p>Me ha parecido una grandísima idea el hecho de que estén abiertos a que los <strong>diseñadores contribuyan</strong> a la biblioteca y seguramente en próximas semanas no parará de crecer.</p>
<p>Si bien puede parecer un problema, el hecho de que se aloje todo en los servidores de Google nos permitirá visualizar las fuentes de letra mucho más <strong>rápidamente</strong> ya sea por la velocidad con la que las sirven o por la cache que se irá acumulando a medida que visitemos más sitios que usen este método.</p>
<p>¿Qué opinión os merece a vosotros todo esto?</p>


<p>Related posts:<ol><li><a href='http://web.ontuts.com/noticias/google-estrena-la-nueva-version-de-google-images/' rel='bookmark' title='Permanent Link: Google estrena la nueva versión de Google Images'>Google estrena la nueva versión de Google Images</a></li>
<li><a href='http://web.ontuts.com/recursos/fuentes-de-letra-gratis-y-de-calidad-volumen-6/' rel='bookmark' title='Permanent Link: Fuentes de Letra Gratis y de Calidad Volumen #6'>Fuentes de Letra Gratis y de Calidad Volumen #6</a></li>
<li><a href='http://web.ontuts.com/recursos/fuentes-de-letra-gratis-y-de-calidad-volumen-8/' rel='bookmark' title='Permanent Link: Fuentes de Letra Gratis y de Calidad Volumen #8'>Fuentes de Letra Gratis y de Calidad Volumen #8</a></li>
<li><a href='http://web.ontuts.com/general/sorteamos-23-invitaciones-para-google-wave/' rel='bookmark' title='Permanent Link: Sorteamos 23 Invitaciones para Google Wave'>Sorteamos 23 Invitaciones para Google Wave</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/presentando-y-utilizando-google-font-api-y-google-font-directory/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Reducir el tiempo de carga Web: Optimizando imágenes PNG</title>
		<link>http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-png/</link>
		<comments>http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-png/#comments</comments>
		<pubDate>Tue, 11 May 2010 08:58:19 +0000</pubDate>
		<dc:creator>Adrián Mato</dc:creator>
				<category><![CDATA[Tutoriales]]></category>
		<category><![CDATA[imágenes]]></category>
		<category><![CDATA[optimización]]></category>
		<category><![CDATA[png]]></category>

		<guid isPermaLink="false">http://web.ontuts.com/?p=3684</guid>
		<description><![CDATA[Quinto capítulo de la serie "Reducir el tiempo de carga Web" con el que pretendemos mostrar el camino de cara a mejorar la velocidad de carga de nuestras web, reduciendo así el tiempo de espera del usuario que tanto valor tiene a día de hoy. En esta ocasión revisaremos la forma en la que podremos optimizar nuestras imágenes en formato .PNG... ¡No os lo perdáis!


Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-jpeg/' rel='bookmark' title='Permanent Link: Reducir el tiempo de carga Web: Optimizando imágenes JPEG'>Reducir el tiempo de carga Web: Optimizando imágenes JPEG</a></li>
<li><a href='http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-gif/' rel='bookmark' title='Permanent Link: Reducir el tiempo de carga Web: Optimizando imágenes GIF'>Reducir el tiempo de carga Web: Optimizando imágenes GIF</a></li>
<li><a href='http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-seleccionando-el-formato-de-imagen/' rel='bookmark' title='Permanent Link: Reducir el tiempo de carga Web: Seleccionando el Formato de Imagen'>Reducir el tiempo de carga Web: Seleccionando el Formato de Imagen</a></li>
<li><a href='http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-minimiza-las-peticiones-http/' rel='bookmark' title='Permanent Link: Reducir el tiempo de carga Web: Minimiza las Peticiones HTTP'>Reducir el tiempo de carga Web: Minimiza las Peticiones HTTP</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<h3>Este tutorial forma parte de una serie</h3>
<ul>
<li><a href="http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-minimiza-las-peticiones-http">Ir a la 1ª Parte &#8211; Minimiza las Peticiones HTTP</a></li>
<li><a href="http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-seleccionando-el-formato-de-imagen">Ir a la 2ª Parte &#8211; Seleccionando el Formato de Imagen</a></li>
<li><a href="http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-gif">Ir a la 3ª Parte &#8211; Optimizando imágenes GIF</a></li>
<li><a href="http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-jpeg">Ir a la 4ª Parte &#8211; Optimizando imágenes JPEG</a></li>
<li><a href="http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-png">Ir a la 5ª Parte &#8211; Optimizando imágenes PNG</a></li>
</ul>
<h3>¿Por qué optimizar las imágenes?</h3>
<p>Como habíamos visto en el primer capítulo sobre <a href="http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-minimiza-las-peticiones-http">minimizar las peticiones HTTP</a>, estábamos intentando reducir el número de peticiones HTTP combinando todas nuestras imágenes en una única imagen. Reduciendo el número de imágenes estamos reduciendo también el número de peticiones HTTP.</p>
<blockquote><p>Una vez tenemos claro el formato de imagen que nos conviene es importante optimizar cada una de nuestras imágenes para reducir su tiempo de carga lo máximo posible.</p></blockquote>
<p>Ahora que tenemos un menor número de imágenes a cargar, necesitamos <strong>reducir el tamaño</strong> de estos archivos en disco, reduciendo así el tiempo de carga de nuestra web.</p>
<p>En este artículo trataremos de conocer la forma en la que podemos <strong>reducir el peso</strong> de las imágenes <strong>.PNG</strong>.</p>
<h3>¿Cómo reducir el tamaño de la imagen sin perder calidad?</h3>
<p>Las imágenes PNG contienen una serie de datos que pueden llegar a ser útiles en numerosas ocasiones, pero esta información carecerá en su mayoría de utilidad a la hora de crear y diseñar un sitio web. A esta información se le conoce como <strong>metadata</strong> y puede ser:</p>
<ul>
<li>Información sobre la <strong>cámara</strong>.</li>
<li><strong>Fecha y hora</strong> de la foto.</li>
<li>Miniaturas de <strong>vista previa</strong> e incluso <strong>audio</strong>.</li>
<li>Información específica de la <strong>aplicación</strong> (por ejemplo Photoshop).</li>
<li><strong>Comentarios</strong> acerca de la imagen.</li>
<li>Y mucho más&#8230;</li>
</ul>
<p>Como habrás podido suponer los fotógrafos pueden encontrar útil esta información acerca de la cámara, comentarios de las fotos, fechas, configuraciones&#8230; pero para nuestros diseños web no necesitaremos este tipo de información, sólo queremos mostrar la imagen.</p>
<blockquote><p><strong>Eliminando</strong> la <strong>información</strong> oculta <strong>reduciremos el tamaño</strong> de nuestras imágenes, favoreciendo a una mayor rapidez en la carga de las mismas.</p></blockquote>
<p>Con toda esta información &#8220;inútil&#8221; por detrás de la foto nuestras imágenes están ocupando más espacio del que deberían, vamos a ver pues cómo eliminarla y favorecer así a una mayor rapidez en la carga de las mismas. ¡Allá vamos!</p>
<h3>Optimizando con la herramienta pngcrush</h3>
<p>Como hemos estado comentando a lo largo de esta serie de artículos, <strong>PNG</strong> es un <strong>formato sin pérdida</strong>, por lo que podremos realizar una gran cantidad de operaciones y guardado sin pérdida de calidad respecto a la imagen original (no hablamos de reducir la paleta de colores por supuesto, aunque hay excepciones).</p>
<p>Partiendo de la introducción a esta entrada, eliminaremos la <strong>información innecesaria</strong> mediante la herramienta <a href="http://pmt.sourceforge.net/pngcrush/">pngcrush</a>.</p>
<h4>Uso de comandos pngcrush para optimizar las imágenes PNG</h4>
<p>Si todavía no habéis descargado <a href="http://pmt.sourceforge.net/pngcrush/">pngcrush</a> hacedlo, y a continuación probamos el siguiente comando:</p>
<pre name="code" class="php">
pngcrush -rem all -brute -reduce imagen_original.png imagen_final.png
</pre>
<p>Analicemos algunas de las opciones que hemos usado:</p>
<ul>
<li><strong>-rem</strong> elimina todos los <strong>bytes innecesarios</strong>, pero mantiene el de <strong>transparencia</strong>.</li>
<li><strong>-reduce</strong> tratará de <strong>eliminar</strong> aquellos <strong>colores</strong> que no están siendo utilizados en la paleta de colores.</li>
<li><strong>-brute</strong> probará una gran cantidad de métodos para <strong>optimizar al máximo</strong> la imagen. En muchos casos no consigue optimizar, pero si estamos en una situación &#8220;offline&#8221; no perdemos nada por intentarlo. En escenarios de máximo rendimiento / online no es aconsejable ya que es un <strong>proceso realmente lento</strong>.</li>
</ul>
<blockquote><p>Eliminar la información (metadata) de la imagen hará que también se <strong>elimine el copyright</strong> de la misma, así que tened <strong>cuidado</strong>, quizás sí os interese / debáis mantener dicha información.</p></blockquote>
<p>Existen muchas otras alternativas&#8230; ¡echemos un vistazo!</p>
<h3>Alternativas a pngcrush</h3>
<p>Como siempre, nosotros hemos escogido pngcrush, pero existen multitud de alternativas en la web, tan sólo hay que buscarlas y probarlas:</p>
<ul>
<li><a href="http://advsys.net/ken/utils.htm">PNGOut</a></li>
<li><a href="http://www.pobox.com/%7Ejason1/pngrewrite/">pngrewrite</a></li>
<li><a href="http://www.cs.toronto.edu/%7Ecosmin/pngtech/optipng/">OptiPNG</a></li>
</ul>
<p>Para terminar, simplemente comentar que es importante probar las distintas opciones disponibles para lograr la mejor configuración para nuestro proyecto y caso de uso. No es lo mismo trabajar en entornos online con recursos limitados que en entornos offline donde podemos tener mayor capacidad y recursos.</p>
<h3>Reflexión final</h3>
<p>Al igual que en el artículo anterior, con un poco de <strong>investigación</strong>, uso de las <strong>herramientas</strong> apropiadas y sentido común podemos conseguir optimizar nuestros sitios web de manera relativamente sencilla.</p>
<p>Ahora que controlamos de forma básica la optimización de los tres tipos de imágenes más utilizados en la web (GIF, JPEG y PNG), sólo nos queda seguir mejorando e investigando estos métodos&#8230; no dudéis en compartirlos con todos en los comentarios.</p>
<p>¡Nos vemos en la próxima publicación!</p>


<p>Related posts:<ol><li><a href='http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-jpeg/' rel='bookmark' title='Permanent Link: Reducir el tiempo de carga Web: Optimizando imágenes JPEG'>Reducir el tiempo de carga Web: Optimizando imágenes JPEG</a></li>
<li><a href='http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-gif/' rel='bookmark' title='Permanent Link: Reducir el tiempo de carga Web: Optimizando imágenes GIF'>Reducir el tiempo de carga Web: Optimizando imágenes GIF</a></li>
<li><a href='http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-seleccionando-el-formato-de-imagen/' rel='bookmark' title='Permanent Link: Reducir el tiempo de carga Web: Seleccionando el Formato de Imagen'>Reducir el tiempo de carga Web: Seleccionando el Formato de Imagen</a></li>
<li><a href='http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-minimiza-las-peticiones-http/' rel='bookmark' title='Permanent Link: Reducir el tiempo de carga Web: Minimiza las Peticiones HTTP'>Reducir el tiempo de carga Web: Minimiza las Peticiones HTTP</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://web.ontuts.com/tutoriales/reducir-el-tiempo-de-carga-web-optimizando-imagenes-png/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
