<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0">
  <channel>
    <title>aRamirez - blog</title>
    <link>http://www.aramirez.es</link>
    <description />
    <pubDate>Wed, 20 Oct 2010 22:10:00 +0200</pubDate>
    <managingEditor>alberto@aramirez.es (Alberto Ramirez)</managingEditor>
    <language>en</language>
    <image>
      <url />
      <title />
      <width />
      <height />
    </image>
    <feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="aramirez-blog" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="aramirez-blog" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="aramirez-blog" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Aramirez-Blog" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="aramirez-blog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2FAramirez-Blog" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><item>
      <title><![CDATA[PHPConference Barcelona 2010]]></title>
      <link>http://www.aramirez.es/blog/phpconference-barcelona-2010/</link>
      <description><![CDATA[<p>Los días 29 y 30 de octube se celebra en Barcelona la 3&ordf; edición de la <a href="http://phpconference.es/" target="_blank">PHP Barcelona Conference</a>.  El año pasado no estuve muy atento de la fecha de celebración y  finalmente, cuando me enteré, era demasiado tarde para organizar el  viaje. Este año sin embargo me enteré con bastante tiempo de antelación y  no me lo pensé..</p>
<p>Con todo reservado solo quedaba elegir las ponencias a las que  asistir, ya que hay ponencias en tres salas diferentes y obviamente solo  se puede asister a una por horario. La decisión no es fácil, hay una  gran variedad de temáticas y ponentes de enorme nivel (Ilia Alshanetsky,  Fabien Potencier, Stefan Priebsch, Lorenzo Alberton, Enrico Zimuel,  etc.). Tras un difícil decisión, la lista elegida en principio, es la  siguiente:</p>
<p><strong>Viernes:</strong></p>
<ol>
<li>Hidden features of PHP</li>
<li>A new approach to object persistence in PHP</li>
<li>Design patterns&hellip; revisited for PHP 5.3</li>
<li>PHP in the Enterprise: Develop and Deploy Mission Critical Applications</li>
<li>Desarrollo de aplicaciones para Facebook en PHP</li>
<li>Architecture and testability</li>
<li>Comet: By Pushing Server Data, We Push the Web Forward</li>
</ol>
<p><strong>Sábado:</strong></p>
<ol>
<li>SCRUM al detalle: Revisión práctica y perversiones humanas</li>
<li>APC & Memcached the High Performance Duo</li>
<li>Mondongo, un ODM para PHP y MongoDB</li>
<li>Continuous Improvement in PHP projects</li>
<li>Service Oriented Architecture: a good thing to do</li>
</ol>
<p>Aquí está el <a href="http://phpconference.es/php-barcelona-conference-2010/talks/" target="_blank">listado completo de ponencias</a> y los <a href="http://phpconference.es/php-barcelona-conference-2010/schedule/" target="_blank">horarios</a> de éstas.</p>]]></description>
      <guid isPermaLink="false">phpconference-barcelona-2010</guid>
      <pubDate>Wed, 20 Oct 2010 22:10:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[Comenzando con TDD]]></title>
      <link>http://www.aramirez.es/blog/comenzando-con-tdd/</link>
      <description><![CDATA[<p>Tras la lectura de dos maravillosos libros acerca de métodologías ágiles de desarrollo:</p>
<ul>
<li>Scrum y XP desde las trincheras.</li>
<li>Kanban y Scrum - Obteniendo lo mejor de ambos.</li>
</ul>
<p>Me dispongo a iniciarme en el mundo de <strong>TDD</strong>. Para los que no sepáis qué es <a href="http://es.wikipedia.org/wiki/Desarrollo_guiado_por_pruebas" target="_blank">TDD</a>, se trata de una metodología de desarrollo basada en pruebas: "Test Driven Development" (Desarrollo guiado por pruebas).</p>
<p>Hace varios días que comencé a escribir pruebas unitarias y de  integración en mis aplicaciones Symfony y una vez que me noto con  soltura y empiezo a cogerle el ritmo (y el gustillo) he decidido que es  un buen momento para iniciarme en TDD. Para ello he elegido un libro  escrito por <a href="http://www.carlosble.com/" target="_blank">Carlos Ble</a>: <strong>Diseño Ágil con TDD</strong>.</p>
<p>Aclarando conceptos iniciales:</p>
<ul>
<li><strong>Prueba unitaria:</strong> se encarga de testear  un caso concreto (atómico). Por ejemplo para testear la operación suma  en una calculadora. Este tipo de pruebas no pueden modificar el estado  del sistema, es decir, no pueden ejecutar operaciones sobre base de  datos, enviar emails, etc.</li>
<li><strong>Pruebas de integración:</strong> al igual que las pruebas unitarias,  testean un caso atómico de una clase pero con la diferencia de que si  pueden ejecutar operaciones sobre el sistema. Para ello se usa un  entorno de test (por ejemplo se crear una BBDD con sufijo "_test" y se  prueba sobre ella). En cada prueba de integración se ejecutan unos  métodos especiales que preparan el sistema, normalmente starUp y  tearDown, el primero prepara el sistema (ejemplo: introduce datos en la  BBDD de tests) y el segundo devuelve el sistema al estado inicial  (ejemplo: borra los datos).</li>
<li><strong>Pruebas funcionales:</strong> son las pruebas que se encargan de  reproducir el comportamiento de la aplicación a nivel usuario (Ejemplo:  ejecutar un login en la web).</li>
</ul>
<p>Esto es todo por el momento. Prometo postear con lo que vaya aprendiendo sobre TDD.</p>
<p>Algunos enlaces de interés:</p>
<p><a href="http://es.wikipedia.org/wiki/Desarrollo_guiado_por_pruebas" target="_blank">TDD en la Wikipedia.</a></p>
<p><a href="http://www.carlosble.com/" target="_blank">El blog de Carlos Ble.</a></p>
<p><a href="http://www.dirigidoportests.com/el-libro" target="_blank">Diseño ágil con TDD.</a></p>
<p>&nbsp;</p>]]></description>
      <guid isPermaLink="false">comenzando-con-tdd</guid>
      <pubDate>Wed, 22 Sep 2010 20:00:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[Doctrine Migrations: ejemplo de uso básico]]></title>
      <link>http://www.aramirez.es/blog/doctrine-migrations-ejemplo-de-uso-basico/</link>
      <description><![CDATA[<p>Inspirado por un buen post de <strong>Asier Marqués</strong>: <a href="http://blackslot.com/blog/2010/01/desarrollo-agil-con-symfony-doctrine-y-mysql-workbench/#comment-99" target="_blank">Desarrolo ágil con symfony, doctrine y mysql workbench</a> en el que nos habla de como usar Mysql workbench y un plugin que nos  permite exportar nuestra base de datos a formato YAML, he decidido  contar mi corta experiencia con Doctrine migrations.</p>
<p>Hace poco comencé a usar esta opción que nos brinda a todos el ORM <strong>Doctrine</strong>. Tras dos o tres task lanzados, ya me enteraba de como funciona, es más, empecé a cogerle el gusto...</p>
<p>La idea parte teniendo un schema.yml inicial, una BBDD en el servidor  y la necesidad de incorporar cambios en ambos. El uso es mucho más  extenso, pero me voy a centrar en los siguiente:</p>
<p>Teniendo el schema.yml</p>
<pre><code># config/doctrine/schema.yml<br /><br />Category:<br />    columns:<br />        id:<br />            type: integer(11)<br />            notnull: true<br />            autoincrement: true<br />            primary: true<br />            unsigned: true<br />        name:<br />            type: string(255)</code></pre>
<p>Y la BBDD en el servidor exactamente tal y como se declara en el schema.</p>
<p>Se necesita aplicar un cambio en la BBDD (añadir/eliminar columna, modificar una relación, etc.).</p>
<pre><code># config/doctrine/schema.yml<br /><br />Category:<br />    columns:<br />        id:<br />            type: integer(11)<br />            notnull: true<br />            autoincrement: true<br />            primary: true<br />            unsigned: true<br />        name:<br />            type: string(255)<br />Item:<br />    columns:<br />        id:<br />            type: integer(11)<br />            notnull: true<br />            autoincrement: true<br />            primary: true<br />            unsigned: true<br />        category_id:<br />            type: integer(11)<br />            notnull: true<br />            unsigned: true<br />        name:<br />            type: string(255)<br />    relations:<br />        Category:<br />            local: category_id<br />            foreign: id<br />            type: one<br />            foreignAlias: Items</code></pre>
<p>Cómo hacemos esto?</p>
<p><strong>1.</strong> Modificar el schema.yml para aplicar el cambio.</p>
<p><strong>2.</strong> Lanzar el task <strong>$ php symfony doctrine:generate-migrations-diff</strong>. Esto crea una estructura de ficheros en el directorio lib/ del proyecto:</p>
<ul>
<li>lib/migration/doctrine (Directorio que contiene los cambios a aplicar)<br /></li>
<li>x_versionN.php (Archivo que contiene los cambios para pasar a la versión N)</li>
<li>Tabla migration_version en el servidor, que almacena la versión actual.</li>
</ul>
<pre><code>// lib/migration/doctrine/1283537548_version1.php<br /><br />class Version1 extends Doctrine_Migration_Base<br />{<br />    public function up()<br />    {<br />        $this-&gt;createTable('item', array(<br />             'id' =&gt; <br />             array(<br />              'type' =&gt; 'integer',<br />              'autoincrement' =&gt; '1',<br />              'primary' =&gt; '1',<br />              'unsigned' =&gt; '1',<br />              'length' =&gt; '11',<br />             ),<br />             'category_id' =&gt; <br />             array(<br />              'type' =&gt; 'integer',<br />              'notnull' =&gt; '1',<br />              'unsigned' =&gt; '1',<br />              'length' =&gt; '11',<br />             ),<br />             'name' =&gt; <br />             array(<br />              'type' =&gt; 'string',<br />              'length' =&gt; '255',<br />             ),<br />             ), array(<br />             'primary' =&gt; <br />             array(<br />              0 =&gt; 'id',<br />             ),<br />             ));<br />    }<br /><br />    public function down()<br />    {<br />        $this-&gt;dropTable('item');<br />    }<br />}</code></pre>
<pre><code>// lib/migration/doctrine/1283537549_version2.php</code><br /><br /><code>class Version2 extends Doctrine_Migration_Base<br />{<br />    public function up()<br />    {<br />        $this-&gt;createForeignKey('item', 'item_category_id_category_id', array(<br />             'name' =&gt; 'item_category_id_category_id',<br />             'local' =&gt; 'category_id',<br />             'foreign' =&gt; 'id',<br />             'foreignTable' =&gt; 'category',<br />             ));<br />        $this-&gt;addIndex('item', 'item_category_id', array(<br />             'fields' =&gt; <br />             array(<br />              0 =&gt; 'category_id',<br />             ),<br />             ));<br />    }<br /><br />    public function down()<br />    {<br />        $this-&gt;dropForeignKey('item', 'item_category_id_category_id');<br />        $this-&gt;removeIndex('item', 'item_category_id', array(<br />             'fields' =&gt; <br />             array(<br />              0 =&gt; 'category_id',<br />             ),<br />             ));<br />    }<br />}</code></pre>
<p><strong>3.</strong> Lanzar el task <strong>$ php symfony doctrine:migrate</strong>. Aplica en la BBDD los cambios que se crearon en el paso anterior. Se le pueden pasar parámetros (entorno, up / down, etc.)</p>
<p><strong>4.</strong> Por último se reconstruyen las clases del modelo: <strong>$ php symfony doctrine:build --all-classes</strong></p>
<p><strong>5.</strong> No nos olvidemos de borrar la cache..</p>
<p>Con esto, ya tenemos los cambios que requerimos, aplicados tanto en  nuestro proyecto Symfony como en nuestra BBDD. Fácil y rápido.</p>
<p><strong>Enlaces:</strong></p>
<p><a href="http://www.aramirez.es/uploads/downloads/probando.zip" target="_blank">Proyecto de prueba creado para el post</a></p>
<p><a href="http://www.doctrine-project.org/projects/orm/1.2/docs/manual/migrations/en" target="_blank">Documentación doctrine migrations</a></p>
<p><a href="http://www.slideshare.net/denderello/symfony-live-2010-using-doctrine-migrations" target="_blank">Transparencia presentada en Symfony Live 2010 por Dennis</a></p>]]></description>
      <guid isPermaLink="false">doctrine-migrations-ejemplo-de-uso-basico</guid>
      <pubDate>Thu, 02 Sep 2010 23:35:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[sfImageTransformExtraPlugin: transforma y cachea imágenes al vuelo]]></title>
      <link>http://www.aramirez.es/blog/sfimagetransformextraplugin-transforma-y-cachea-imagenes-al-vuelo/</link>
      <description><![CDATA[<p>Necesitaba crear un módulo que transformase imágenes al vuelo según  parámetros pasados por la URL y almacenara una cache de éstas (módulo  PIC para imágenes).<br /><br />Cuando me disponía a empezarlo, justo vi <strong>sfImageTransformExtraPlugin</strong> para Symfony. Que hace todo lo que necesitaba y más.<br /><br />Después  de instalarlo y pelearme inicialmente con el routing para conseguir  llamar al módulo de forma correcta el resultado es espléndido: rápido,  gestión máginifica de la cache, fácil de usar una vez conocido, fexible,  etc.<a href="http://www.symfony-project.org/plugins/sfImageTransformExtraPlugin" target="_blank"><br /> <br /> Plugin sfImageTransformExtraPlugin.</a><br /><br /><a href="http://test.ical.ly/2010/08/18/workshop-integrating-sfimagetransformextraplugin-into-your-symfony-backend-application-part-1/" target="_blank">Tutorial parte 1.</a><br /><br /><a href="http://test.ical.ly/2010/08/20/workshop-integrating-sfimagetransformextraplugin-into-your-symfony-backend-application-%E2%80%93-part-2/" target="_blank">Tutorial parte 2.</a></p>]]></description>
      <guid isPermaLink="false">sfimagetransformextraplugin-transforma-y-cachea-imagenes-al-vuelo</guid>
      <pubDate>Tue, 31 Aug 2010 20:30:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[Acciones en Admin Generator con GeneratorHelper]]></title>
      <link>http://www.aramirez.es/blog/acciones-en-admin-generator-con-generatorhelper/</link>
      <description><![CDATA[<p>Cuando tenemos un módulo creado a través del "Admin Generator" se crean varias acciones automáticamente como son: edit, new, delete, list... A veces, se necesitan crear acciones diferentes a éstas y que son más propias del modelo de la aplicación como por ejemplo: "mostrar en portada", "Artículo fuera de Stock".</p>
<p>Para ello normalmente existen dos vías diferentes: desde el generator.yml o sobreescribiendo el partial correspondiente a las acciones del form o list.</p>
<p>Este sería un ejemplo para una acción que pondrá un artículo en la portada de la web:</p>
<p><strong>Desde el generator.yml:&nbsp;</strong></p>
<pre><code>generator:<br />&nbsp; class: sfDoctrineGenerator<br />&nbsp; param:<br />&nbsp;&nbsp;&nbsp; model_class:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Articulos<br />&nbsp;&nbsp;&nbsp; theme:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; admin<br />&nbsp;&nbsp;&nbsp; non_verbose_templates: true<br />&nbsp;&nbsp;&nbsp; with_show:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; false<br />&nbsp;&nbsp;&nbsp; singular:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~<br />&nbsp;&nbsp;&nbsp; plural:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~<br />&nbsp;&nbsp;&nbsp; route_prefix:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; articulos<br />&nbsp;&nbsp;&nbsp; with_doctrine_route:&nbsp;&nbsp; true<br />&nbsp;&nbsp;&nbsp; actions_base_class:&nbsp;&nbsp;&nbsp; sfActions<br /><br />&nbsp;&nbsp;&nbsp; config:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; actions: ~<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list:&nbsp;&nbsp;&nbsp; ~<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; filter:&nbsp; ~<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; actions:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; portada:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; action:&nbsp; 'inHome' <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; label:&nbsp;&nbsp; 'En portada'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; confirm: 'Este artículo pasará a la portada de la web. Continuar?'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _delete: ~<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _list:&nbsp;&nbsp; ~<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _save_:&nbsp; ~<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; edit:&nbsp; ~<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new:&nbsp; ~<br /></code></pre>
<p><strong>Sobreescribiendo _form_actions.php</strong></p>
<pre><code>&lt;ul class="sf_admin_actions"&gt;<br />&lt;?php if ($form-&gt;isNew()): ?&gt;<br />  &lt;?php // Si es un objeto nuevo no se muestra el botón ?&gt;<br />&nbsp; &lt;?php // Aquí irían las acciones propias del Form: edit, save... ?&gt;<br />&lt;?php else: ?&gt;<br />  &lt;?php if (!$articulos-&gt;getInHome()): ?&gt;<br />&nbsp; &lt;li class="sf_admin_action_portada"&gt;<br />&nbsp; &lt;?php echo link_to(__('En portada', array(), 'messages'), 'articulos/inHome?id='.$articulos-&gt;getId(), array(&nbsp; 'conditional' =&gt; 'portada',&nbsp; 'conditional_value' =&gt; false,)) ?&gt;<br />&nbsp; &lt;/li&gt;<br />  &lt;?php endif; ?&gt;<br />&nbsp; &lt;?php // Aquí irían las acciones propias del Form: edit, save... ?&gt;<br />&lt;?php endif; ?&gt;<br />&lt;/ul&gt;<br /></code></pre>
<p>Esta última opción da más control al programador y se pueden tener en cuenta condicionales como por ejemplo que el artículo tenga el campo home=true, en cuyo caso ya no se necesita que aparezca el botón.</p>
<p>Pero las opciones no se acaban aquí, existe otro método de crear nuevas acciones un poco más complejas sin necesidad de sobreescribir el elemento Partial. Cómo? Haciendo uso del Helper propio del módulo creado por el Admin Generator (Articulos en este ejemplo). Este archivo se sitúa en el directorio lib del módulo: <strong>apps/backend/modules/##moduleName/lib/##moduleNameGeneratorHelper.class.php</strong> y extiende de Base##moduleNameGeneratorHelper que ha su vez extiende de la clase <strong>sfModelGeneratorHelper</strong>. Si miramos esta clase vemos que contiene los helpers linkToNew, linkToEdit, linkToList, linkToSave y linkToSaveAndAdd que se corresponden con las acciones base y que devuelven el tag li con una llamada al helper link_to configurado con los parámetros de la acción.</p>
<p>Si se mira el Partial de las acciones, primero se comprueba que exista el método ##actionName dentro del helper, si existe se muestra su valor, si no existe se crea la acción parametrizada según el fichero generator.yml</p>
<p><strong>cache/backend/#env/modules/autoArticulos/templates/_form_actions.php</strong></p>
<pre><code>&lt;?php <br /> // ... <br /> if (method_exists($helper, 'linkToPortada')): ?&gt;<br />&nbsp; &lt;?php echo $helper-&gt;linkToPortada($form-&gt;getObject(), array(&nbsp; 'action' =&gt; 'inHome',&nbsp; 'label' =&gt; 'En portada',&nbsp; 'confirm' =&gt; 'Este artículo pasará a la portada de la web. Continuar?',&nbsp; 'params' =&gt;&nbsp;&nbsp; array(&nbsp;&nbsp;&nbsp; 'conditional' =&gt; 'portada',&nbsp;&nbsp;&nbsp; 'conditional_value' =&gt; false,&nbsp; ),&nbsp; 'class_suffix' =&gt; 'portada',)) ?&gt;<br />&lt;?php else: ?&gt;<br />&nbsp; &lt;?php echo link_to(__('En portada', array(), 'messages'), 'articulos/inHome?id='.$articulos-&gt;getId(), array(&nbsp; 'conditional' =&gt; 'portada',&nbsp; 'conditional_value' =&gt; false,)) ?&gt;<br />&lt;?php endif; <br />  // ...<br />?&gt;</code></pre>
<p>Entonces, una forma de personalizar acciones es usando el generator.yml y el helper del módulo para pasárle parámetros, condicionales, etc. Siguiendo con el ejemplo:</p>
<p><strong>generator.yml</strong></p>
<pre><code>generator:<br />&nbsp; class: sfDoctrineGenerator<br />&nbsp; param:<br />&nbsp;&nbsp;&nbsp; config:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; actions:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; novedad: <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; action:&nbsp; 'inNew' <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; label:&nbsp;&nbsp; 'Novedad' <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; confirm: 'Este artículo será marcado como novedad. Continuar?'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; params:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conditional: 'novedad'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conditional_value: false</code></pre>
<p>El parámetro 'novedad' se usará para saber qué propiedad del objeto hay que chequear y el parámetro conditional_value para saber qué valor ha de tomar el objeto para que se muestre esta acción.</p>
<p><strong>articulosGeneratorHelper.class.php</strong></p>
<pre><code>&lt;?php <br /><br />class articulosGeneratorHelper extends BaseArticulosGeneratorHelper<br />{<br />&nbsp; public function linkToPortada($object, $params)<br />&nbsp; {<br />&nbsp;&nbsp;&nbsp; $conditional = (isset($params['params']['conditional'])) ? 'get'.ucfirst($params['params']['conditional']) : null;<br />&nbsp;&nbsp;&nbsp; $conditional_value = (isset($params['params']['conditional_value'])) ? $params['params']['conditional_value'] : true;<br />&nbsp;&nbsp;&nbsp; if ( ($conditional == null) || ($conditional != null && $object-&gt;$conditional() == $conditional_value) ) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return link_to(__($params['label'], array(), 'sf_admin'), 'articulos/' . $params['action'] . '?id=' . $object, array('confirm' =&gt; !empty($params['confirm']) ? __($params['confirm'], array(), 'sf_admin') : $params['confirm']));<br />&nbsp;&nbsp;&nbsp; } else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return '';<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />}<br /></code></pre>
<p>Tengo que decir que no he visto nada en la documentación oficial de Symfony al respecto, por lo que no se hasta qué punto es una forma correcta de hacer uso de ésto, sobre todo con el paso de parámetros no estándares (conditional y conditional_value) desde el generator.yml al helper del módulo.</p>]]></description>
      <guid isPermaLink="false">acciones-en-admin-generator-con-generatorhelper</guid>
      <pubDate>Mon, 19 Jul 2010 19:00:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[Carro de la compra sencillo con sfDoctrineShoppingCart]]></title>
      <link>http://www.aramirez.es/blog/carro-de-la-compra-sencillo-con-sfdoctrineshoppingcart/</link>
      <description><![CDATA[<p>Navegando por la web de Symfony en busca de un plugin que implementase las operaciones de un <strong>"Carrito de la compra"</strong> y que fuese compatible con la versión 1.4 y el ORM Doctrine, no encontré nada interesante. Sin embargo, vi el plugin <strong>sfShoppingCartPlugin</strong> creado por Fabien Potencier para la versión 1.0 de Symfony y Propel.</p>
<p>Tras echarle un ojo, vi que implementa basicamente lo que necesito para mi carro de la compra y que solo requería de algunas modificaciones para hacerlo compatible con Doctrine, por lo que decidí hacer uso de él.</p>
<p>El plugin en si consta solamente con dos clases:</p>
<ul>
<li><strong>sfShoppingCart</strong>: que se encarga de modelar la cesta como tal y sus operaciones básicas (añadido, borrado, etc.)</li>
<li><strong>sfShoppingCartItem</strong>: que modela un item que se añadirá a sfShoppingCart.</li>
</ul>
<p>Básicamente, el uso es el siguiente:</p>
<pre><code>&lt;?php<br /><br />// Se crea el objeto de la cesta<br />$carrito = new sfShoppingCart(sfConfig::get('app_tax'));<br />// Se añade un nuevo Articulo al carrito<br />$article = ArticlesTable::getInstance()-&gt;find($request-&gt;getParameter('id'));<br />$articleItem = new sfShoppingCartItem(get_class($article), $article-&gt;getId());<br />$articleItem-&gt;setPrice($article-&gt;getPrice());<br />$articleItem-&gt;setQuantity($request-&gt;getParameter('quantity'));<br />$carrito-&gt;addItem($articleItem);<br />// Recorrer el carrito y ver el precio total<br />foreach ($carrito-&gt;getItems() as $item) {<br />&nbsp;&nbsp;&nbsp; echo sprintf(<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; '%d: %f x %d = %f', <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item-&gt;getId(), <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item-&gt;getPrice(), <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item-&gt;getQuantity(), <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ($item-&gt;getPrice() * $item-&gt;getQuantity())<br />&nbsp;&nbsp;&nbsp; );<br />}<br />echo 'Total: ' . $carrito-&gt;getTotal();</code></pre>
<p>Además de estas operaciones la clase sfShoppingCart incluye dos operaciones que devuelven el objeto Item del modelo:</p>
<pre><code>sfShoppingCart::getObjects()<br />sfShoppingCart::getObject($class, $id)</code></pre>
<p>Estos métodos hacen uso de Propel para obtener el objeto de la clase $class y con id $id.</p>
<p>Como lo que necesito es hacer uso de Doctrine, nada mejor que extender sfShoppingCart a sfDoctrineShoppingCart y sobreescribir ambos métodos:</p>
<pre><code>&nbsp; public function getObject($class, $id)<br /> &nbsp; {<br /> &nbsp;&nbsp;&nbsp; // We must first make sure that the requested object does exist in the shopping cart<br /> &nbsp;&nbsp;&nbsp; $ind = $this-&gt;getItemIndice($class, $id);<br /> &nbsp;&nbsp;&nbsp; <br /> &nbsp;&nbsp;&nbsp; return (($ind !== null) ? call_user_func(array($class.'Table', 'find'), $id) : null);<br /> &nbsp; }<br /> <br /> &nbsp; public function getObjects()<br /> &nbsp; {<br /> &nbsp;&nbsp;&nbsp; $object_ids = array();<br /> &nbsp;&nbsp;&nbsp; foreach ($this-&gt;getItems() as $item)<br /> &nbsp;&nbsp;&nbsp; {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!array_key_exists($item-&gt;getClass(), $object_ids))<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $object_ids[$item-&gt;getClass()] = array();<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /> <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $object_ids[$item-&gt;getClass()][] = $item-&gt;getId();<br /> &nbsp;&nbsp;&nbsp; }<br /> <br /> &nbsp;&nbsp;&nbsp; $objects = array();<br /> &nbsp;&nbsp;&nbsp; foreach ($object_ids as $class =&gt; $ids)<br /> &nbsp;&nbsp;&nbsp; {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $objects = array_merge(<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $objects, <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Doctrine::getTable($class)-&gt;createQuery('c')-&gt;whereIn('c.id', $ids)-&gt;execute()-&gt;getData()<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br /> &nbsp;&nbsp;&nbsp; }<br /> <br /> &nbsp;&nbsp;&nbsp; return $objects;<br /> &nbsp; }</code></pre>
<p>Con ésto ya se puede hacer uso de la clase sfDoctrineShoppingCart con todas las funcionalidades que ofrece sfShoppingCart pero adaptado a Doctrine.</p>
<p>Además he implementado algunos métodos extra para hacerlo más cómodo al uso. Por ejemplo sfDoctrineShoppingCart::getInstance() o sfDoctrineShoppingCart::save() para obtener y almacenar el carrito</p>
<p><a href="http://www.symfony-project.org/plugins/sfShoppingCartPlugin" target="_blank">Plugin sfShoppingCart de Fabien Potencier.</a></p>
<p><a href="http://www.symfony-project.org/cookbook/1_0/en/shopping_cart" target="_blank">Ejemplo de uso para 1.0</a></p>
<p><a href="/uploads/downloads/sfDoctrineShoppingCart.php.zip" target="_blank">Clase sfDoctrineShoppingCart</a></p>]]></description>
      <guid isPermaLink="false">carro-de-la-compra-sencillo-con-sfdoctrineshoppingcart</guid>
      <pubDate>Wed, 14 Jul 2010 10:00:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[Mi nuevo Twitter]]></title>
      <link>http://www.aramirez.es/blog/mi-nuevo-twitter/</link>
      <description><![CDATA[<p>Me acabo de crear una cuenta twitter con fin de seguir las ponencias de Symfony organizadas por decharlas en Castellón. No me manejo muy bien aun en esto, pero parece que me adapto rápidamente.</p>
<p>Voy a experimentar durante un tiempo a ver qué ocurre...</p>
<p>Pueden seguirme en <a href="http://twitter.com/aramirez_" target="_blank">Alberto Ramirez on Twitter</a></p>]]></description>
      <guid isPermaLink="false">mi-nuevo-twitter</guid>
      <pubDate>Tue, 06 Jul 2010 10:32:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[Mi paso de Symfony 1.0 a 1.4]]></title>
      <link>http://www.aramirez.es/blog/mi-paso-de-symfony-1-0-a-1-4/</link>
      <description><![CDATA[<p>Bueno, llevo unos 3 meses trabajando con la versión 1.4 de Symfony y el ORM Doctrine. Anteriormente estuve un par de años trabajando con la version 1.0 y Propel.</p>
<p>El cambio a la hora de codificar es significativo, no puedo comentar nada nuevo que no se haya comentado ya al respecto de las novedades de estas versiones sobre la 1.0.</p>
<p>En mi caso, la integración con el <strong>subframework de formularios</strong> aun continua su proceso, me manejo mucho mejor que al principio, pero aun hay muchos aspectos que desconozco.</p>
<p>El sistema de <strong>enrutamiento</strong> lo veo muy cómodo, ya que permite establecer en el routing.yml los parámetros del objeto que vas a pasar por URL de modo que a la hora de llamar al helper link_to, solo con pasarle el objeto completo, éste ya se encarga de formatear la URL. Además con los comportamientos (<strong>behaviors</strong>) de <strong>Doctrine</strong>, también se pueden definir Slug de forma muy sencilla.</p>
<p>Ejemplo:</p>
<p>routing.yml</p>
<pre><code>watch_show:<br />&nbsp; url:&nbsp;&nbsp; /catalogo/:brand_url/:slug<br />&nbsp; class: sfDoctrineRoute<br />&nbsp; options: { model: Watch, type: object }<br />&nbsp; param: { module: watch, action: show }<br />&nbsp; requirements:<br />&nbsp;&nbsp;&nbsp; id: \d+<br />&nbsp;&nbsp;&nbsp; sf_method: [get]</code></pre>
<p>showSuccess.php</p>
<pre><code>url_for('watch_show', $principalWatch)</code></pre>
<p>lib/model/doctrine/Watch.class.php</p>
<pre><code>public function getBrandUrl()<br />{<br />&nbsp;&nbsp;&nbsp; return $this-&gt;getBrand()-&gt;getSlug();<br />}</code></pre>
<p>schema.yml</p>
<pre><code>Watch:<br />&nbsp; tableName: watch<br />&nbsp; actAs:<br />&nbsp;&nbsp;&nbsp; Timestampable: ~<br />&nbsp;&nbsp;&nbsp; Sluggable:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unique: true<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fields:&nbsp; [model_name]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; canUpdate: true</code></pre>
<p>apps/frontend/modules/watch/actions/actions.class.php</p>
<pre><code>public function executeShow(sfWebRequest $request)<br />{<br />&nbsp;&nbsp;&nbsp; // 'watch_show' <br />&nbsp;&nbsp;&nbsp; $this-&gt;watch = $this-&gt;getRoute()-&gt;getObject();<br />}</code></pre>
<p>Otro de los cambios importantes es el paso a Doctrine como ORM en defecto de Propel. Lo noto muy flexible, fácil de aprender pero por completo diferente a Propel. Los retrieves se hacen en base a la clase Doctrine_Query y vas formando query de una forma mucho más gráfica y no tan complejas como con Criteria de Propel.</p>
<p>Ejemplo de Query:</p>
<pre><code>$q = Doctrine::getTable('Articulos')-&gt;createQuery('a')<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -&gt;leftJoin('a.Familias f')<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -&gt;leftJoin('a.Subfamilias sf')<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -&gt;innerJoin('a.fotos ft')<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -&gt;where('a.pagarias = ?',false)<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -&gt;andWhere('a.agotado = ?', false)<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -&gt;andWhere('a.familia = f.id')<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -&gt;orderBy('a.portada DESC')<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; -&gt;limit(23)<br />;</code></pre>
<p>Doctrine cuenta con una tabla que modela el objeto en si "Articulos" (en este ejemplo) y con una clase destinada a las operaciones de consulta a la tabla "ArticulosTable" (siguiendo con el ejemplo).</p>
<p>Existen muchas más cosas a tener en cuenta. Por lo que es recomendable leer las novedades de cada versión con el fin de tener en mente las nuevas funcionalidades que aporta cada versión de Symfony.</p>
<p>Yo acabo de pedir el libro <a href="http://www.symfony.es/2009/12/01/mas-con-symfony-el-libro-definitivo/" target="_blank">Más con Symfony</a> que está destinado a los programadores que conocen bien el entorno y quieren ser aun más productivos con Symfony.</p>]]></description>
      <guid isPermaLink="false">mi-paso-de-symfony-1-0-a-1-4</guid>
      <pubDate>Tue, 06 Jul 2010 10:32:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[FirePHP como debugger para AJAX]]></title>
      <link>http://www.aramirez.es/blog/firephp-como-debugger-para-ajax/</link>
      <description><![CDATA[<p>Un complemento para Firefox que he usado recientemente y me ha facilitado la vida para debugear peticiones <strong>AJAX</strong> es <strong>FirePHP</strong>. FirePHP es un complemento para Firebug y permite hacer <strong>debug</strong> PHP en la consola de Firebug.</p>
<p><strong>&iquest;Cómo usarlo?</strong></p>
<p>Hay que usar Firefox y tener instalado el complemento <a href="https://addons.mozilla.org/es-ES/firefox/addon/1843" target="_blank">Firebug</a> (que seguro que lo tienes ya), luego hay que instalar <a href="https://addons.mozilla.org/en-US/firefox/addon/6149" target="_blank">FirePHP</a> y descargar e instalar la <a href="http://www.firephp.org/HQ/FinalRelease.htm" target="_blank">librería</a> PHP.</p>
<p>Una vez obtenido el complemento e instalada la librería se puede usar de esta forma:</p>
<pre><code>require_once('FirePHPCore/FirePHP.class.php');<br />$firephp = FirePHP::getInstance(true);<br />$firephp-&gt;log('Hello World');</code></pre>
<p>Se pueden loguear variables, costantes, arrays, objetos, etc.</p>
<p><strong>Para Symfony</strong></p>
<p>Si se está usando Symfony como framework para el desarrollo se puede instalar el plugin sfFirePHP e integrarlo en su funcionamiento (sistema de log, barra de depuración web, etc).</p>
<p>Enlaces:</p>
<p><a href="http://www.firephp.org/HQ/Install.htm" target="_blank">Instalación.</a></p>
<p><a href="http://www.firephp.org/HQ/Use.htm" target="_blank">Ejemplos de uso.</a></p>
<p><a href="http://www.symfony-project.org/plugins/sfFirePHPPlugin" target="_blank">sfFirePHP.</a></p>]]></description>
      <guid isPermaLink="false">firephp-como-debugger-para-ajax</guid>
      <pubDate>Sat, 19 Dec 2009 16:34:00 +0100</pubDate>
    </item>
    <item>
      <title><![CDATA[Operar con el buffer de PHP]]></title>
      <link>http://www.aramirez.es/blog/operar-con-el-buffer-de-php/</link>
      <description><![CDATA[<p>PHP nos brinda la posibilidad de almacenar el contenido de la salida estándar en un <strong>buffer</strong> y poder operar con él. Una de las posibilidades que tenemos con este buffer es la de simular un efecto de carga e ir mostrando contenido de la web secuencialmente (Simulando un efecto AJAX).</p>
<p>Este sería un pequeño script PHP de ejemplo.</p>
<pre><code>ob_start();<br />for($x=0; $x&lt;10; $x++){<br />&nbsp; echo printf('- Línea %d.'."\n", $x);<br />&nbsp; ob_flush();<br />&nbsp; flush();<br />&nbsp; sleep(1);<br />}<br />ob_end_flush();</code></pre>
<p>Aunque es muy sencillo, lo que realiza es lo siguiente:</p>
<ol>
<li>Inicia el Buffer de PHP.</li>
<li>Comienza un bucle que imprime una línea de código cualquiera.</li>
<li>Limpia el contenido del buffer y lo saca por la salida estándar.</li>
<li>Duerme el script PHP durante 1 segundo (para simular la escritura mediante una carga asíncrona).</li>
<li>Termina el buffer.</li>
</ol>]]></description>
      <guid isPermaLink="false">operar-con-el-buffer-de-php</guid>
      <pubDate>Fri, 04 Dec 2009 22:00:00 +0100</pubDate>
    </item>
    <item>
      <title><![CDATA[En papel antes que en código]]></title>
      <link>http://www.aramirez.es/blog/en-papel-antes-que-en-codigo/</link>
      <description><![CDATA[<p>Cuando se comienza con un proyecto nuevo hay programadores que se ponen directamente a picar código como locos. Esto es una práctica totalmente desaconsejable para crea software y más aun para crear sistemas complejos. Las razones de ello son múltiples:</p>
<ul>
<li>Aun no se conoce (o no de forma correcta) el dominio sobre el cual va a actuar el software.</li>
<li>Tampoco se tiene de forma detallada y formal la problemática que ha de resolverse.</li>
<li>No se conocen las necesidades de los usuarios finales del SW de una forma completa.</li>
<li>Estos puntos llevan a crear métodos y funciones con funcionalidades repetitivas o inversas, por lo que no se hace uso de reutilización de software de manera eficiente.</li>
<li>Tampoco se tiene en cuenta la refactorización.</li>
<li>El software será menos escalable y tendrá problemas de mantenimiento posterior.</li>
</ul>
<p>Se podrían seguir enumerando desventajas, pero no voy a seguir aquí eternamente. &iquest;Qué se debe hacer pues, antes de ponerse a programar una nueva aplicación?</p>
<p>Para solucionar estos problemas y crear una aplicación sólida, escalable y de fácil mantenimiento hay que empezar por unos cimientos sólidos, y para este propósito nada mejor que guiarnos por algún ciclo de vida del software conocido.</p>
<p>Las etapas varían dentro de cada ciclo de vida, pero yo me centraré en comentar las siguientes:</p>
<ol>
<li>Documentación y recogida de requisitos.</li>
<li>Análisis.</li>
<li>Diseño.</li>
<li>Implementación.</li>
</ol>
<p><strong>Documentación y recogida de requisitos.</strong></p>
<p>Esta etapa comprende las sucesivas entrevistas con el cliente y con los usuarios que usarán la aplicación. Ellos han de transmitir qué necesitan y qué problemática debe de solucionar el nuevo software.</p>
<p>En esta etapa podemos empezar a hacer uso de ciertos diagramas que permitan ponernos de acuerdo con el cliente, pero por ello mismo la notación no puede ser completamente formal ya que, de tal modo, no sería legible para el cliente.</p>
<p>Los diagramas que se pueden usar en esta etapa son:</p>
<ul>
<li><strong>Casos de uso:</strong> este diagrama ayudará a conocer los actores que intervendrán en la aplicación. Los actores pueden ser personas (o mejor dicho, papeles que tomarán éstos) u otros sistemas de software externo que hagan uso directo de la aplicación. Cada actor tendrá asociación directa con uno o más casos de uso. Un caso de uso es una funcionalidad comprendida en el dominio del SW (crear factura, buscar factura, etc.).</li>
<li><strong>Diagrama estático de clases:</strong> se notarán las clases más importantes dentro del dominio, los atributos de cada una y las relaciones entre estas clases. Por ejemplo clase Factura, Cliente, Compra, etc.</li>
</ul>
<p><strong>Análisis.</strong></p>
<p>Una vez redactado el informe de especificación de requisitos, documentado en el punto anterior, se pasa a la etapa de análisis, esta etapa hace de puente entre la documentación y el diseño y su cometido es el de revisar, formalizar y ver funcionalidades que no se tuvieran en cuenta en la etapa anterior. En la etapa del análisis se puede usar UML estándar ya que la documentación creada en esta etapa será visible para analistas y programadores.</p>
<p>Los pasos de esta etapa:</p>
<ul>
<li><strong>Revisión de los casos de uso.</strong> Este es el momento de ampliar el diagrama de los casos de uso con la finalidad de documentar todos y cada uno de los casos de uso y actores del nuevo sistema.</li>
<li><strong>Revisión del diagrama de clases:</strong> para tener en cuenta casos especiales de atributos, herencias, interfaces, agregaciones, etc.</li>
</ul>
<p><strong>Diseño.</strong></p>
<p>Es el paso previo a la escritura de código (implementación), aquí es donde se tienen en cuenta la reutilización de código (uso de librerías, frameworks, clases, patrones de diseño, componentes, etc.), la persistencia de los datos (sistema de BBDD y diseño de ésta), normalización del diagrama de clases según el lenguaje de programación elegido.</p>
<p><strong>Implementación.</strong></p>
<p>Una vez documentados todos los puntos anteriores y con los diagramas de casos de uso, de clases, entidad-relación de BBDD y alguno opcional como: secuencias, estados, colaboración, etc. Es la hora de empezar a escribir código. &iquest;Contento por ésto? bien, pues puedes estar contento por dos veces ya que además de empezar la codificación del proyecto a tu lenguaje de programación favorito (o en su defecto, el elegido), también ahorrarás una gran cantidad de tiempo gracias a que todas (o la gran mayoría) de funcionalidades están documentadas de una manera formal, se tienen en cuenta grandes masas de reutilización de código, refactorización, etc. Que te ahorrarán grandes dolores de cabeza, errores, multitud de pruebas, etc.</p>
<p>Para ampliar conocimientos acerca de este tema, hay multitud de libros de <strong>Ingeniería del Software</strong>, UML, patrones de diseño (design pattern) y ciclo de vida del software.</p>]]></description>
      <guid isPermaLink="false">en-papel-antes-que-en-codigo</guid>
      <pubDate>Sun, 29 Nov 2009 22:05:00 +0100</pubDate>
    </item>
    <item>
      <title><![CDATA[Patrón Factory. Creación de objetos complejos de forma fácil.]]></title>
      <link>http://www.aramirez.es/blog/patron-factory-creacion-de-objetos-complejos-de-forma-facil/</link>
      <description><![CDATA[<p><a href="/blog/patrones-de-diseno-en-php/" target="_blank">Este es un post perteneciente a la serie "Patrones de Diseño".</a><br /><br />Normalmente para crear un objeto de una clase se utiliza el operador "new" que nos brinda el propio lenguaje de programación, pero a veces, la creación de un nuevo objeto requiere de una serie de pasos, configuraciones e incluso puede ser necesario crear objetos de alguna subclase. Este problema se ve incrementado si el número de objetos (de la misma clase) a crear, es elevado. El problema entonces: &iquest;Cómo crear objetos complejos de forma fácil, rápida y sin posibilidad de errores?<br /><br />La solución a este problema puede ser la siguiente: crear un método de clase (o una función) que se encargue de crear la instancia, configurar los nuevos objetos e incluso de crear objetos dependientes de otras clases. De este modo la llamada será simple, independientemente de la complejidad que tenga esa clase en si para ser instanciada.<br /><br />Esta solución es la que nos brinda el patrón "Factory", y se puede ver más fácilmente con los siguientes ejemplos:</p>
<p>- Ejemplo 1:</p>
<pre><code>&nbsp;&nbsp;&nbsp; $connection = new DbConnection($host, $user, $pass);<br />&nbsp;&nbsp;&nbsp; // Esta forma no sería la adecuada<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // Conexion creada mediante un método estático regido por el patron "Factory",<br />&nbsp;&nbsp;&nbsp; // que aisla el código de la conexion a la base de datos<br />&nbsp;&nbsp;&nbsp; $connection = DbConnection::createConnection(); <br />&nbsp;&nbsp;&nbsp; // Esta forma seria la correcta.<br />?&gt;</code></pre>
<p>- Ejemplo 2:</p>
<pre><code>&nbsp;&nbsp;&nbsp; // La clase Application configura la aplicacion, en este caso se instancia<br />&nbsp;&nbsp;&nbsp; // mediante su constructor y el paso de ciertos parametros.<br />&nbsp;&nbsp;&nbsp; $application = new Application(array(<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'app_name' =&gt; 'appTest',<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'version'&nbsp; =&gt; '1.0',<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'path'&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; =&gt; '/'<br />&nbsp;&nbsp;&nbsp; ));<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // De esta forma la configuracion se hace a traves del metodo<br />&nbsp;&nbsp;&nbsp; // configure de una forma sencilla. configure() se rige por el patron<br />&nbsp;&nbsp;&nbsp; // Factory<br />&nbsp;&nbsp;&nbsp; $application = Application::configure();<br />?&gt;</code></pre>
<p>&nbsp;</p>
<p>&iquest; Qué pasaría en caso de no utilizar el patrón Factory si es necesario cambiar la estructura de los objetos ? habría que ir objeto por objeto de la clase reformada cambiando su estructura y adaptándolo a su nueva forma, la pérdida de tiempo que esto ocasionaría se incrementa por el número de objetos que se crean en la aplicación y la complejidad de estos.<br /><br />Sin embargo, si se utiliza un método regido por "Factory" para crear los objetos, la adaptación de estos a la nueva estructura vendría por la modificación única en el método que crea la instancia, con lo cual la pérdida de tiempo es mínima, sin hablar de la posibilidad de errores que se pueden cometer si el número de objetos a modificar es elevado.<br /><br />El patrón "Factory" no es solo aplicable a la clase, es decir, se puede crear un método interno de una clase para que el resto de métodos de la clase puedan llamar a un código necesario en cada uno de ellos sin necesidad de repetir éste:</p>
<pre><code>&lt;?php<br />&nbsp;&nbsp;&nbsp; class DbActions{<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function list(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $connection = $this-&gt;_getConnection();<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // código a ejecutar para sacar el listado<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function getById($id){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $connection = $this-&gt;_getConnection();<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // código a ejecutar para obtener el registro<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // mediante la id pasada<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // _getConnection aisla y devuelve la conexion<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; private function _getConnection(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return new DbConnection(<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; DB_HOST,<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; DB_USER,<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; DB_PASS<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />?&gt;</code></pre>
<p>Una vez explicada la funcionalidad del patrón "Factory" se puede aplicar a un ejemplo más complejo:<br /><br />Archivo index.php</p>
<pre><code>&lt;?php&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // Comienza la ejecucion del programa: <br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // se instancia Application, con los datos <br />&nbsp;&nbsp;&nbsp; // necesarios para la ejecucion.<br />&nbsp;&nbsp;&nbsp; // Se hace uso de Singleton para la instancia.<br />&nbsp;&nbsp;&nbsp; require_once ('application.php');<br />&nbsp;&nbsp;&nbsp; $application = Application::getInstance();<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // Ejecucion de un query.<br />&nbsp;&nbsp;&nbsp; $results = $application-&gt;db-&gt;executeQuery("SELECT * FROM `porfolio`");<br />&nbsp;&nbsp;&nbsp; <br />?&gt;</code></pre>
<p>Archivo application.php</p>
<pre><code>&lt;?php<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; class Application{<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * $instance = Object<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; private static $instance = null;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Contiene un objeto de la clase que conecta a la BBDD.<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * $db = DbConex Class<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public $db = null;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * private __construct<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; private function __construct(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Conexion a la BBDD<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; require_once ('dbconex.php');<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // create_connection es un metodo regido por "Factory"<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // que abstrae la logica de configuracion, conexion<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // y acceso a la BBDD.<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Solo requiere el motor de BBDD como parámetro<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $this-&gt;db = DbConex::create_connection('MySQL');<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Metodo que devuelve la instancia (si no existe la crea) de<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * la clase Application. Se rige por "Singlenton".<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; *<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * @return Application<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function getInstance(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (is_null(Application::$instance)) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Application::$instance = new Application();<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return Application::$instance;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />?&gt;</code></pre>
<p>Archivo dbconex.php</p>
<pre><code>&lt;?php<br />&nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp; Clase padre regida por "Factory" que inicializa<br />&nbsp;&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp; la conexion a la base de datos<br />&nbsp;&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp; @return DbConex<br />&nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; abstract class DbConex<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * datos de conexion<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; protected $data = array(<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'db_host'&nbsp;&nbsp;&nbsp; =&gt; &nbsp;&nbsp;&nbsp; 'localhost',<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'db_user'&nbsp;&nbsp;&nbsp; =&gt; &nbsp;&nbsp;&nbsp; 'user_test',<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'db_pass'&nbsp;&nbsp;&nbsp; =&gt; &nbsp;&nbsp;&nbsp; 'user_test'<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Identificador de la conexion<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public $resource = null;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Es el metodo que instancia la subclase que va<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * a conectar a la base de datos.<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * @param: $type<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; *<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * @return Object<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public static function create_connection($type){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; switch ($type) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; case 'MySQL':<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $db = new MySQLDbConex();<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; case 'Oracle':<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $db = new OracleDbConex();<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; case 'PostgreSQL':<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $db = new PostgreSQLDbConex();<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return $db;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * metodo que ha de ser sobreescrito por las clases<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * hijas que se va a encargar de realizar la consulta<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * SQL.<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * @param $sql = String<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; *<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * @return mixed<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; abstract public function executeQuery($sql);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; class MySQLDbConex extends DbConex{<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function __construct(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $this-&gt;resource = mysql_connect(<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $this-&gt;data['db_host'],<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $this-&gt;data['db_user'],<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $this-&gt;data['db_pass']<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; mysql_select_db('aramirez', $this-&gt;resource);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function executeQuery($sql){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Codigo de ejecucion del Query SQL<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; class OracleDbConex extends DbConex{<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function __construct(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Codigo de conexxion a Oracle<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function executeQuery($sql){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Codigo de ejecucion del Query SQL<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; class PostgreSQLDbConex extends DbConex{<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function __construct(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Codigo de conexxion a PostgreSQL<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br /><br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function executeQuery($sql){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Codigo de ejecucion del Query SQL<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />?&gt;</code>
</pre>]]></description>
      <guid isPermaLink="false">patron-factory-creacion-de-objetos-complejos-de-forma-facil</guid>
      <pubDate>Thu, 17 Sep 2009 00:00:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[Patrón Singleton. Restringir a una clase para una única instancia posible]]></title>
      <link>http://www.aramirez.es/blog/patron-singleton-restringir-a-una-clase-para-una-unica-instancia-posible/</link>
      <description><![CDATA[<p><a href="/blog/patrones-de-diseno-en-php/" target="_blank">Este es un post perteneciente a la serie "Patrones de Diseño".</a></p>
<p>En ciertas ocasiones se necesita que una clase en concreto mantenga la misma y <strong>única instancia</strong> a lo largo de toda la ejecución del programa, sin permitir que se cree un objeto diferente de esa clase.</p>
<p>Podemos pensar en una cesta de la compra como ejemplo, cuando un cliente entra en la tienda se instancia un objeto de la clase que gestiona la cesta, si en otro punto del programa se necesita recurrir a la cesta, el objeto devuelto ha de ser el mismo que el anterior y no uno nuevo. Otro ejemplo puede ser el de la conexión a una base de datos: cuando el programa comienza se crea la conexión, y lo óptimo es que todas las operaciones SQL se hagan a través de la misma conexión abierta, así se ahorra en tiempo de ejecución ya que no hay que abrir, ejecutar y cerrar cada vez que se requiera de una operación SQL, además en el caso de una aplicación de comercio electrónico se puede requerir que el identificador de conexión sea siempre el mismo para ejecutar transacciones.</p>
<p>De este razonamiento sacamos un problema en común:</p>
<p><strong>&iquest;Cómo se puede restringir que una clase solo devuelva una única instancia de ella lo largo de toda la ejecución del programa?</strong></p>
<p>Para resolver este problema se usa el patrón de diseño <strong>Singleton</strong>: una clase basada en este patrón instancia e inicializa un objeto y provee de acceso para extraer el mismo objeto cada vez. Todo ello a través de un método comúnmente llamado <strong>getInstance()</strong>.<strong></strong></p>
<p><strong>&iquest;Cómo se implementa? </strong></p>
<ul>
<li>Se declara una <strong>propiedad</strong> de la clase con visibilidad <strong>privada</strong> que va a contener en todo momento la instancia de la clase.</li>
<li>Se establece como <strong>privado</strong> el <strong>constructor</strong> de la clase.</li>
<li>Se crea un <strong>método</strong> de acceso <strong>público getInstance</strong> que se encargará de <strong>devolver la instancia</strong> de la clase si existe, y <strong>si no existe la crea</strong> previamente.</li>
</ul>
<p><strong>Ejemplo:</strong> Para el ejemplo vamos a usar una clase que contiene toda la lógica de base de datos (conexión, ejecución de SQL, etc.) y que se rige por el patron Singleon.</p>
<p>Clase dbConex:</p>
<pre><code>&lt;?php<br />&nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp; Clase que devuelve la instancia de la conexion<br />&nbsp;&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp; a la base de datos, si no existe la abre.<br />&nbsp;&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp; Sigue el patron de diseño Singleton<br />&nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; class DbConex<br />&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Propiedad privada que almacena la instancia<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; private static $instance = null;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Propiedad publica que almacena el ultimo query ejecutado con exito<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public $lastQuery = "";<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Constructor de la clase, con acceso privado<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; private function __construct(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Metodo que devuelve la instancia de la clase si existe,<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * si no la crea previamente<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; *<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * @return Object<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public static function getInstance(){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (is_null(DbConex::$instance)) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; DbConex::$instance = new DbConex();<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return DbConex::$instance;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /**<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * Metodo que ejecuta un query SQL<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * y lo almacena en una variable como ultimo query ejecutado<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; *<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; * @param $query = String<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public function executeQuery($query){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // .. Codigo que ejecuta el Query ..<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $this-&gt;lastQuery = $query;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br /></code></pre>
<p>Como se ve en este código el método getInstance comprueba si existe la instancia de DbConex accediendo a la propiedad estática $instance, si no existe la crea y la almacena en dicha propiedad, por último devuelve $instance.</p>
<p>Adicionalmente he creado una propiedad $lastQuery que almacena el último query ejecutado con éxito. La finalidad de esto es poder comprobar la ejecución del Singleton.</p>
<p>El método executeQuery se encarga de ejecutar el SQL pasado como parámetro y almacenar el query en la propiedad $lastQuery. Este método va a ayudar a comprobar la funcionalidad del singleton.</p>
<p>Ahora se muestra el script PHP que llama y ejecuta la clase DbConex para realizar la prueba del Singleton:</p>
<pre><code><br />&lt;?php<br />&nbsp;&nbsp;&nbsp; // Se insertan todos los archivos necesarios<br />&nbsp;&nbsp;&nbsp; require_once ('dbconex.php');<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // Comienza la aplicacion<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // Se instancia la clase DbConex, se ejecuta un Query y se elimina la variable de memoria<br />&nbsp;&nbsp;&nbsp; $dbConex = DbConex::getInstance();<br />&nbsp;&nbsp;&nbsp; $dbConex-&gt;executeQuery("SELECT table1.id, table1.name FROM table1;");<br />&nbsp;&nbsp;&nbsp; unset($dbConex);<br />&nbsp;&nbsp;&nbsp; // Hemos terminado con la conexion en este punto<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // Simulamos otro punto de la aplicacion:<br />&nbsp;&nbsp;&nbsp; // Volvemos a crear una instancia de dbConex<br />&nbsp;&nbsp;&nbsp; $dbConex2 = DbConex::getInstance();<br />&nbsp;&nbsp;&nbsp; // Para comprobar que el objeto devuelto es el mismo <br />&nbsp;&nbsp;&nbsp; // mostramos el ultimo query ejecutado<br />&nbsp;&nbsp;&nbsp; echo sprintf("Last SQL: %s \r\n", $dbConex2-&gt;lastQuery);<br /></code></pre>
<p>Como se puede ver primero se crea una instancia de DbConex y se ejecuta un SELECT cualquiera, luego se elimina la variable que contiene la instancia de DbConex.<br />Más adelante se simula otro punto de la aplicación instanciando de nuevo la clase DbConex, si esta clase no siguiese el patrón Singleton el objeto devuelto sería otro creado desde cero, pero como no es así, el objeto es el mismo que se creo en el punto anterior de la aplicación. Para demostrarlo se imprime en la salida la propiedad $lastQuery, que contiene el SQL ejecutado con éxito anteriormente.<br /><br />Como podéis ver este patrón es muy útil en ciertas ocasiones y puede ser la solución a un problema que se nos presenta a menudo.</p>]]></description>
      <guid isPermaLink="false">patron-singleton-restringir-a-una-clase-para-una-unica-instancia-posible</guid>
      <pubDate>Fri, 11 Sep 2009 17:30:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[Patrones de diseño en PHP]]></title>
      <link>http://www.aramirez.es/blog/patrones-de-diseno-en-php/</link>
      <description><![CDATA[<p>En mi afán constante de aprendizaje, una de las cosas que desde hace tiempo me está rondando la cabeza es depurar mis conocimientos acerca de los distintos patrones de diseño para POO y su aplicación en PHP. Ya utilizo algunos de ellos en mis desarrollos pero siempre por lo que he ido viendo en software externo, así que ya es hora de obtener una buena base.<br /><br />Según la Wiki la definición de Patrones de diseño es:</p>
<blockquote>
<p>Los patrones de diseño (design patterns) son la base para la búsqueda de soluciones a problemas comunes en el desarrollo de software y otros ámbitos referentes al diseño de interacción o interfaces.</p>
<p>Un patrón de diseño es una solución a un problema de diseño. Para que una solución sea considerada un patrón debe poseer ciertas características. Una de ellas es que debe haber comprobado su efectividad resolviendo problemas similares en ocasiones anteriores. Otra es que debe ser reusable, lo que significa que es aplicable a diferentes problemas de diseño en distintas circunstancias.</p>
</blockquote>
<p><br />En realidad los patrones de diseño nacen de la solución a problemas comunes, tienen ventajas y desventajas, pero normalmente sus ventajas hacen que usarlos sea la solución óptima a un problema concreto. Por ejemplo, si necesitamos asegurarnos de que una clase cualquiera solo sea instanciada una vez a lo largo de la ejecución de la aplicación (como la conexión a la base de datos) podremos recurrir al patrón de diseño Singleton. O para separar en capas la lógica de negocio, el controlador de la aplicación y la vista, se puede usar el patrón MVC.<br /><br />Para formarme en este campo, he mirado diferentes libros que tratan esta temática y al final he optado por "phparchitect's. Guide PHP Design Patterns" en magnífico inglés :D.<br /><br />A lo largo de una serie de post iré describiendo los patrones que creo, pueden se los más interesantes para los programadores PHP en el mundo web.</p>
<p>Capítulos:</p>
<ol>
<li><a href="/blog/patron-singleton-restringir-a-una-clase-para-una-unica-instancia-posible/" target="_blank">Patrón Singleton.</a></li>
<li><a href="/blog/patron-factory-creacion-de-objetos-complejos-de-forma-facil/" target="_blank">Patrón Factory.</a></li>
</ol>
<p>Enlaces:</p>
<p><a href="http://es.wikipedia.org/wiki/Patrones_de_dise%C3%B1o" target="_blank">wikipedia :: patrón de diseño</a></p>]]></description>
      <guid isPermaLink="false">patrones-de-diseno-en-php</guid>
      <pubDate>Fri, 11 Sep 2009 17:14:00 +0200</pubDate>
    </item>
    <item>
      <title><![CDATA[¿Cómo crear un canal Podcast con Symfony?]]></title>
      <link>http://www.aramirez.es/blog/como-crear-un-canal-podcast-con-symfony/</link>
      <description><![CDATA[<p>Antes de nada vamos a empezar explicando qué es un <strong>Podcast</strong>. Un Podcast es una subscripción a un canal <strong>RSS</strong> que contiene información <strong>multimedia</strong> de audio o vídeo. Cada canal podcast se puede agregar a programas o dispositivos específicos que se encargan de actualizar y reproducir el contenido de dicho canal, por ejemplo: iTunes, iPhone, etc.<br /> <br /> Como todos sabemos a estas alturas un RSS es un documento en formato XML con unas especificaciones comunes. Cada canal RSS contiene información acerca de dicho canal (Autor, imagen o logo, etc) y una serie de entradas que, entre otras informaciones, suele contener título, imagen, descripción y enlace hacia la entrada en su web correspondiente.<br /> <br /> Para crear un canal Podcast no tenemos más que modificar el XML del RSS y añadirle un objeto <strong>enclosure</strong> que va a informar al software que lee el canal de los datos del archivo multimedia incrustado (audio o video) mediante los datos siguientes: tipo <strong>mime</strong> del archivo, <strong>peso</strong> y <strong>ruta</strong> de donde leer el archivo.<br /> <br /> Se puede crear un Podcast muy fácilmente <strong>con Symfony y su plugin sfFeed2Plugin</strong> que ya viene preparado para la inserción de objetos enclosure.<br /> <br /> El plugin incorpora una clase llamada <strong>sfFeedEnclosure</strong> que permite insertar un objeto enclosure en cada <strong>sfFeedItem</strong> de la siguiente forma:<br /> <br /> En la acción creamos el objeto Feed:</p>
<pre><code><br /> &lt;?php<br /> &nbsp;&nbsp;&nbsp; // Se abre el canal RSS<br /> &nbsp;&nbsp;&nbsp; $feed = sfFeedPeer::newInstance('rss201');<br /> &nbsp;&nbsp;&nbsp; $feed-&gt;setTitle("Canal Podcast de prueba");<br /> &nbsp;&nbsp;&nbsp; $feed-&gt;setLink('http://www.dominio.com/');<br /> &nbsp;&nbsp;&nbsp; $feed-&gt;setAuthorName('Alberto Ramírez');<br /> &nbsp;&nbsp;&nbsp; $feedImage = new sfFeedImage();<br /> &nbsp;&nbsp;&nbsp; $feedImage-&gt;setFavicon('http://www.dominio.com/favicon.ico');<br /> &nbsp;&nbsp;&nbsp; $feed-&gt;setImage($feedImage);<br /> <br /> &nbsp;&nbsp;&nbsp; // Ahora se crean las entradas del canal<br /> &nbsp;&nbsp;&nbsp; $articulos = ArticulosPeer::getFeeds(); // metodo del modelo que devuelve los ultimos articulos<br /> &nbsp;&nbsp;&nbsp; foreach ($articulos as $entrada){<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Se declara el item<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item = new sfFeedItem();<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item-&gt;setTitle($entrada-&gt;getTitulo());<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item-&gt;setLink('http://www.dominio.com/'.$entrada-&gt;getId());<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item-&gt;setPubdate($entrada-&gt;getFechaPublicacion('U'));<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item-&gt;setUniqueId($entrada-&gt;getId());<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Se declara el objeto enclosure con los datos<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // url, tamaño y mime del archivo<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $enclosure = new sfFeedEnclosure();<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $enclosure-&gt;initialize(array(<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'url' &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; =&gt; 'http://www.dominio.com/podcast/file.mp3',<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'length'&nbsp;&nbsp; =&gt; 3072,<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 'mimeType' =&gt; 'audio/mpeg'<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ));<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Se asigna al item del feed<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $item-&gt;setEnclosure($enclosure);<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Se añade el item al canal<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; $feed-&gt;addItem($item);<br /> &nbsp;&nbsp;&nbsp; }<br /> &nbsp;&nbsp;&nbsp; // Paso de variables al template<br /> &nbsp;&nbsp;&nbsp; $this-&gt;feed = $feed;<br /> ?&gt;<br /> </code></pre>
<p><br /> Luego en el template convertimos el objeto Feed en XML:</p>
<pre><code><br /> &lt;?php decorate_with(false) ?&gt;<br /> &lt;?php echo $feed-&gt;asXml() ?&gt;<br /> </code></pre>
<p><br /> Con esto ya tenemos creado un canal podcast básico creado.</p>
<p>&nbsp;</p>]]></description>
      <guid isPermaLink="false">como-crear-un-canal-podcast-con-symfony</guid>
      <pubDate>Fri, 11 Sep 2009 17:12:00 +0200</pubDate>
    </item>
  </channel>
</rss>

