/jobeet/es/14.markdown
Markdown | 384 lines | 313 code | 71 blank | 0 comment | 0 complexity | 019b056d07ac35073a88b1da6b0587c9 MD5 | raw file
1Día 14: Feeds o Canales 2======================= 3 4Ayer, se comenzó a elaborar tu primer aplicación symfony. No te detengas ahora. A medida que aprendas más sobre Symfony, trata de añadir nuevas funciones a tu aplicación, alojala en algún lugar, y compartelo con la comunidad. 5 6Vamos a pasar hoy a algo completamente diferente. 7 8Si estás buscando un puesto de trabajo, es probable que desees ser informado tan pronto como un nuevo puesto de trabajo se ha publicado. Y no es muy conveniente comprobar el sitio web a cada hora. Vamos hoy a añadir feeds (o canales) de varios puestos de trabajo, para mantener a nuestros usuarios Jobeet actualizados. 9 10Formatos 11-------- 12 13Symfony tiene soporte nativo para los formatos y tipos MIME. Esto significa que el modelo y el controlador pueden tener diferentes plantillas basadas en el formato solicitado. El formato predeterminado es HTML pero Symfony admite varios formatos de serie como ser `txt`, `js`, `css`, `json`, `xml`, `rdf`, o `atom`. 14 15El formato se puede configurar utilizando el método `setRequestFormat()` del objeto request: 16 17 [php] 18 $request->setRequestFormat('xml'); 19 20Pero la mayor parte del tiempo, el formato está incluído en la URL. En este caso, Symfony lo establecerá por tí si la variable especial `sf_format` se utiliza en la ruta correspondiente. Para la lista de puestos de trabajo (job), la URL es: 21 22 http://www.jobeet.com.localhost/frontend_dev.php/job 23 24Esta URL es equivalente a: 25 26 http://www.jobeet.com.localhost/frontend_dev.php/job.html 27 28Ambas URL son equivalentes porque las rutas generadas por la clase 29`sfPropelRouteCollection` tienen la `sf_format` como extension. Puedes comprobarlo por tí mismo ejecutando la tarea `app:routes`: 30 31 32 33Feeds 34----- 35 36### Feed de los Últimos Puestos de Trabajo 37 38Soportar diferentes formatos es tán fácil como la crear diferentes plantillas. Para crear un [feed Atom](http://en.wikipedia.org/wiki/Atom_(standard)) para los últimos puestos de trabajo, crea una plantilla `indexSuccess.atom.php`: 39 40 [php] 41 <!-- apps/frontend/modules/job/templates/indexSuccess.atom.php --> 42 <?xml version="1.0" encoding="utf-8"?> 43 <feed xmlns="http://www.w3.org/2005/Atom"> 44 <title>Jobeet</title> 45 <subtitle>Latest Jobs</subtitle> 46 <link href="" rel="self"/> 47 <link href=""/> 48 <updated></updated> 49 <author><name>Jobeet</name></author> 50 <id>Unique Id</id> 51 52 <entry> 53 <title>Job title</title> 54 <link href="" /> 55 <id>Unique id</id> 56 <updated></updated> 57 <summary>Job description</summary> 58 <author><name>Company</name></author> 59 </entry> 60 </feed> 61 62>**SIDEBAR** 63>Nombres de las Plantillas 64> 65>Como `html` es el formato más utilizado para aplicaciones web, éste puede ser omitido 66>del nombre de la plantilla. Ambas plantillas `indexSuccess.php` y `indexSuccess.html.php` 67>son equivalentes y Symfony utiliza la primero que encuentre. 68> 69>¿Por qué las plantillas predeterminadas tienen el sufijo `Success`? Una acción puede devolver un valor 70>para indicar que plantilla se mostrará. Si la acción no dice o devuelve nada, eso 71>equivalente al siguiente código: 72> 73> [php] 74> return sfView::SUCCESS; // == 'Success' 75> 76>Si deseas cambiar el sufijo, devuelve otra cosa: 77> 78> [php] 79> return sfView::ERROR; // == 'Error' 80> 81> return 'Foo'; 82> 83>También puedes cambiar el nombre de la plantilla utilizando el método 84>`setTemplate()`: 85 86> [php] 87> $this->setTemplate('foo'); 88 89Por defecto, Symfony cambiará la respuesta `Content-Type` de acuerdo con el formato, y para todos los formatos que no sean HTML, el layout es deshabilitado. Para un Atom feed, Symfony cambiará el `Content-Type` a `application/atom+xml; charset=utf-8`. 90 91En el pie de página Jobeet, actualiza el enlace para el feed: 92 93 [php] 94 <!-- apps/frontend/templates/layout.php --> 95 <li class="feed"> 96 <a href="<?php echo url_for('job', array('sf_format' => 'atom')) ?>">Full feed</a> 97 </li> 98 99El URI interno es el mismo que para la lista `job` con el `sf_format` añadido como una variable. 100 101Añade una etiqueta `<link>` en la sección head del layout: 102 103 [php] 104 <!-- apps/frontend/templates/layout.php --> 105 <link rel="alternate" type="application/atom+xml" title="Latest Jobs" 106 href="<?php echo url_for('job', array('sf_format' => 'atom'), true) ?>" /> 107 108Para el atributo `href` del enlace, se utiliza una URL absoluta gracias al segundo argumento del helper `url_for()`. 109 110Vamos a actualizar el header de la plantilla Atom: 111 112 [php] 113 <!-- apps/frontend/modules/job/templates/indexSuccess.atom.php --> 114 <title>Jobeet</title> 115 <subtitle>Latest Jobs</subtitle> 116 <link href="<?php echo url_for('job', array('sf_format' => 'atom'), true) ?>" rel="self"/> 117 <link href="<?php echo url_for('homepage', true) ?>"/> 118<propel> 119 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', JobeetJobPeer::getLatestPost()->getCreatedAt('U')) ?></updated> 120</propel> 121<doctrine> 122 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', Doctrine_Core::getTable('JobeetJob')->getLatestPost()->getDateTimeObject('created_at')->format('U')) ?></updated> 123</doctrine> 124 <author> 125 <name>Jobeet</name> 126 </author> 127 <id><?php echo sha1(url_for('job', array('sf_format' => 'atom'), true)) ?></id> 128 129<propel> 130Nota la utilización de `U` como argumento para `getCreatedAt()` para obtener la fecha como timestamp. Para obtener la fecha del envío, crea el método `getLatestPost()`: 131</propel> 132<doctrine> 133Nota la utilización de la función `strtotime()` para obtener la fecha `created_at` como timestamp. Para obtener la fecha del envío, crea el método `getLatestPost()`: 134</doctrine> 135 136<propel> 137 [php] 138 // lib/model/JobeetJobPeer.php 139 class JobeetJobPeer extends BaseJobeetJobPeer 140 { 141 static public function getLatestPost() 142 { 143 $criteria = new Criteria(); 144 self::addActiveJobsCriteria($criteria); 145 146 return JobeetJobPeer::doSelectOne($criteria); 147 } 148 149 // ... 150 } 151</propel> 152<doctrine> 153 [php] 154 // lib/model/doctrine/JobeetJobTable.class.php 155 class JobeetJobTable extends Doctrine_Table 156 { 157 public function getLatestPost() 158 { 159 $q = Doctrine_Query::create() 160 ->from('JobeetJob j'); 161 $this->addActiveJobsQuery($q); 162 163 return $q->fetchOne(); 164 } 165 166 // ... 167 } 168</doctrine> 169 170Los items del feed se pueden generar con el siguiente código: 171 172 [php] 173 <!-- apps/frontend/modules/job/templates/indexSuccess.atom.php --> 174 <?php use_helper('Text') ?> 175 <?php foreach ($categories as $category): ?> 176 <?php foreach ($category->getActiveJobs(sfConfig::get('app_max_jobs_on_homepage')) as $job): ?> 177 <entry> 178 <title> 179 <?php echo $job->getPosition() ?> (<?php echo $job->getLocation() ?>) 180 </title> 181 <link href="<?php echo url_for('job_show_user', $job, true) ?>" /> 182 <id><?php echo sha1($job->getId()) ?></id> 183<propel> 184 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', $job->getCreatedAt('U')) ?></updated> 185</propel> 186<doctrine> 187 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', $job->getDateTimeObject('created_at')->format('U')) ?></updated> 188</doctrine> 189 <summary type="xhtml"> 190 <div xmlns="http://www.w3.org/1999/xhtml"> 191 <?php if ($job->getLogo()): ?> 192 <div> 193 <a href="<?php echo $job->getUrl() ?>"> 194 <img src="http://<?php echo $sf_request->getHost().'/uploads/jobs/'.$job->getLogo() ?>" 195 alt="<?php echo $job->getCompany() ?> logo" /> 196 </a> 197 </div> 198 <?php endif ?> 199 200 <div> 201 <?php echo simple_format_text($job->getDescription()) ?> 202 </div> 203 204 <h4>How to apply?</h4> 205 206 <p><?php echo $job->getHowToApply() ?></p> 207 </div> 208 </summary> 209 <author> 210 <name><?php echo $job->getCompany() ?></name> 211 </author> 212 </entry> 213 <?php endforeach ?> 214 <?php endforeach ?> 215 216El método `getHost()` del objeto request (`$sf_request`) devuelve el actual host, que viene muy bien para crear un vínculo absoluto para el logo de la empresa. 217 218 219 220>**TIP** 221>Cuando se crea un feed, la depuración es más fácil si utiliza herramientas de línea de comandos como 222>[`curl`](http://curl.haxx.se/) o 223>[`wget`](http://www.gnu.org/software/wget/), ya que puedes ver el contenido real 224>del feed. 225 226### El Feed de los Últimos Puestos de Trabajo de una Categoría 227 228Uno de los objetivos de Jobeet es ayudar a la gente a encontrar puestos de trabajo específicos. Por lo tanto, tenemos que proporcionar un feed para cada categoría. 229 230En primer lugar, vamos a actualizar la ruta `category` para agregar el soporte para diferentes formatos: 231 232 [yml] 233 // apps/frontend/config/routing.yml 234 category: 235 url: /category/:slug.:sf_format 236 class: sfPropelRoute 237 param: { module: category, action: show, sf_format: html } 238 options: { model: JobeetCategory, type: object } 239 requirements: 240 sf_format: (?:html|atom) 241 242Ahora, la ruta `category` comprenderá tanto los formatos `html` como `atom`. Actualiza los enlaces de los feeds de la categoría en las plantillas: 243 244 [php] 245 <!-- apps/frontend/modules/job/templates/indexSuccess.php --> 246 <div class="feed"> 247 <a href="<?php echo url_for('category', array('sf_subject' => $category, 'sf_format' => 'atom')) ?>">Feed</a> 248 </div> 249 250 [php] 251 <!-- apps/frontend/modules/category/templates/showSuccess.php --> 252 <div class="feed"> 253 <a href="<?php echo url_for('category', array('sf_subject' => $category, 'sf_format' => 'atom')) ?>">Feed</a> 254 </div> 255 256El último paso es la creación de la plantilla `showSuccess.atom.php`. Pero como este feed también lista puestos de trabajo, podemos refactorizar el código que genera los items del feed mediante la creación de un partial `_list.atom.php`. Como el formato `html`, 257los partial son de un formato específico: 258 259 [php] 260 <!-- apps/frontend/job/templates/_list.atom.php --> 261 <?php use_helper('Text') ?> 262 263 <?php foreach ($jobs as $job): ?> 264 <entry> 265 <title><?php echo $job->getPosition() ?> (<?php echo $job->getLocation() ?>)</title> 266 <link href="<?php echo url_for('job_show_user', $job, true) ?>" /> 267 <id><?php echo sha1($job->getId()) ?></id> 268<propel> 269 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', $job->getCreatedAt('U')) ?></updated> 270</propel> 271<doctrine> 272 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', $job->getDateTimeObject('created_at')->format('U')) ?></updated> 273</doctrine> 274 <summary type="xhtml"> 275 <div xmlns="http://www.w3.org/1999/xhtml"> 276 <?php if ($job->getLogo()): ?> 277 <div> 278 <a href="<?php echo $job->getUrl() ?>"> 279 <img src="http://<?php echo $sf_request->getHost().'/uploads/jobs/'.$job->getLogo() ?>" 280 alt="<?php echo $job->getCompany() ?> logo" /> 281 </a> 282 </div> 283 <?php endif ?> 284 285 <div> 286 <?php echo simple_format_text($job->getDescription()) ?> 287 </div> 288 289 <h4>How to apply?</h4> 290 291 <p><?php echo $job->getHowToApply() ?></p> 292 </div> 293 </summary> 294 <author> 295 <name><?php echo $job->getCompany() ?></name> 296 </author> 297 </entry> 298 <?php endforeach ?> 299 300Puedes utilizar el partial `_list.atom.php` para simplificar la plantilla del feed de los puestos de trabajo: 301 302 [php] 303 <!-- apps/frontend/modules/job/templates/indexSuccess.atom.php --> 304 <?xml version="1.0" encoding="utf-8"?> 305 <feed xmlns="http://www.w3.org/2005/Atom"> 306 <title>Jobeet</title> 307 <subtitle>Latest Jobs</subtitle> 308 <link href="<?php echo url_for('job', array('sf_format' => 'atom'), true) ?>" rel="self"/> 309 <link href="<?php echo url_for('homepage', true) ?>"/> 310<propel> 311 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', JobeetJobPeer::getLatestPost()->getCreatedAt('U')) ?></updated> 312</propel> 313<doctrine> 314 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', Doctrine_Core::getTable('JobeetJob')->getLatestPost()->getDateTimeObject('created_at')->format('U')) ?></updated> 315</doctrine> 316 <author> 317 <name>Jobeet</name> 318 </author> 319 <id><?php echo sha1(url_for('job', array('sf_format' => 'atom'), true)) ?></id> 320 321 <?php foreach ($categories as $category): ?> 322 <?php include_partial('job/list', array('jobs' => $category->getActiveJobs(sfConfig::get('app_max_jobs_on_homepage')))) ?> 323 <?php endforeach ?> 324 </feed> 325 326Finalmente, crear la plantilla `showSuccess.atom.php`: 327 328 [php] 329 <!-- apps/frontend/modules/category/templates/showSuccess.atom.php --> 330 <?xml version="1.0" encoding="utf-8"?> 331 <feed xmlns="http://www.w3.org/2005/Atom"> 332 <title>Jobeet (<?php echo $category ?>)</title> 333 <subtitle>Latest Jobs</subtitle> 334 <link href="<?php echo url_for('category', array('sf_subject' => $category, 'sf_format' => 'atom'), true) ?>" rel="self" /> 335 <link href="<?php echo url_for('category', array('sf_subject' => $category), true) ?>" /> 336<propel> 337 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', $category->getLatestPost()->getCreatedAt('U')) ?></updated> 338</propel> 339<doctrine> 340 <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', $category->getLatestPost()->getDateTimeObject('created_at')->format('U')) ?></updated> 341</doctrine> 342 <author> 343 <name>Jobeet</name> 344 </author> 345 <id><?php echo sha1(url_for('category', array('sf_subject' => $category), true)) ?></id> 346 347 <?php include_partial('job/list', array('jobs' => $pager->getResults())) ?> 348 </feed> 349 350Para el feed principal, necesitamos la fecha del último puesto de trabajo para una categoría: 351 352 [php] 353<propel> 354 // lib/model/JobeetCategory.php 355</propel> 356<doctrine> 357 // lib/model/doctrine/JobeetCategory.class.php 358</doctrine> 359 class JobeetCategory extends BaseJobeetCategory 360 { 361 public function getLatestPost() 362 { 363 return $this->getActiveJobs(1)->getFirst(); 364 } 365 366 // ... 367 } 368 369 370 371Nos vemos mañana 372---------------- 373 374Al igual que con muchas características Symfony, el soporte de formatos nativos te permite añadir feeds a tus sitios web sin esfuerzo. Hoy, hemos mejorado la experiencia de alquien que busca empleo. Mañana, vamos a ver cómo dar una mayor exposición a los oferentes de empleo mediante el suministro de un servicio Web. 375 376Feedback 377-------- 378 379>**Tip** 380>Este capítulo ha sido traducido por **Roberto Germán Puentes Díaz**. 381>Si encuentras algún error que deseas corregir o realizar algún comentario, 382>no dudes en enviarlo por correo a **puentesdiaz [arroba] gmail.com** 383 384__ORM__