PageRenderTime 59ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/jobeet/es/04.markdown

https://gitlab.com/intelij/symfony1-docs
Markdown | 727 lines | 568 code | 159 blank | 0 comment | 0 complexity | cabf151ed44970f335f40cd04e2341d9 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, CC-BY-SA-4.0
  1. Día 4: El Controlador y la Vista
  2. ==============================
  3. Ayer, hemos explorado cómo Symfony simplifica la gestión de bases de datos por abstracción entre los diferentes motores de bases de datos, y mediante la conversión de elementos relacionales con útiles clases orientadas a objetos. También hemos jugado con ##ORM## para describir el esquema de base de datos, crear las tablas, y llenar la base de datos con algunos datos iniciales.
  4. Hoy, vamos a personalizar el módulo básico `job` creado ayer. El módulo `job` existente tiene todo el código que necesitamos para Jobeet:
  5. * Una página que lista todos los puestos de trabajo
  6. * Una página para crear un nuevo puesto de trabajo
  7. * Una página para actualizar un puesto de trabajo existente
  8. * Una página para eliminar un puesto de trabajo
  9. Aunque el código está listo para ser utilizado como esta, vamos a refactorizar las plantillas para adaptarlas lo más cerca a los mockups Jobeet.
  10. La arquitectura MVC
  11. -------------------
  12. Si estás desarrollando con PHP sitios web sin ningún framework, probablemente uses el paradigma de un archivo PHP por página HTML. Estos archivos PHP probablemente contengan el mismo tipo de estructura: inicialización y configuración global, lógica de negocio relacionada con la página solicitada, busqueda de registros en la base, y finalmente el código HTML que arma la página.
  13. Puedes utilizar un motor de plantillas para separar la lógica del HTML.
  14. Tal vez utilizas una capa de abstracción de la base de datos para separar el modelo de la lógica de negocio. Sin embargo, la mayoría de las veces, terminas con un montón de código que es una pesadilla para mantener. Es rápido para construir, pero con el tiempo, es más y más difícil de hacer cambios, especialmente porque nadie, excepto entiende cómo se construye y cómo funciona.
  15. Al igual que con todos los problemas, hay soluciones agradables. Para desarrollo web, la solución más común para la organización de su código de hoy en día es el [**patrón de diseño MVC**](http://en.wikipedia.org/wiki/Model-view-controller).
  16. En resumen, el patrón de diseño MVC define una manera de organizar el código de acuerdo a su naturaleza. Este patrón separa el código en **tres capas**:
  17. * La capa **Modelo** define la lógica de negocio (la base de datos pertenece a esta capa). Ya sabes que Symfony guarda todas las clases y archivos relacionados con el modelo en el directorio `lib/model/`.
  18. * La **Vista** es con lo que el usuario interactúa (un motor de plantillas es parte de esta capa). En Symfony, la vista es principalmente la capa de plantillas PHP. Estas son guardadas en varios directorios `templates/` como veremos más adelante en el día de hoy.
  19. * El **Controlador** es la pieza de código que llama al Modelo para obtener algunos datos que le pasa a la Vista para la presentación al cliente. Cuando instalamos Symfony el primer día, vimos que todas las solicitudes son gestionadas por un controlador frontal (`index.php` y `frontend_dev.php`). Estos controladores frontales delegadan la verdadera labor a las **acciones**. Como vimos ayer, estas acciones son, lógicamente, agrupadas en **módulos**.
  20. ![MVC](http://www.symfony-project.org/images/jobeet/1_4/04/mvc.png)
  21. Hoy, usaremos el mockup definido el día 2 para personalizar la página principal y la página de puestos de trabajos. Vamos hacerlas dinámicas. A lo largo del camino, vamos a modificar un montón de cosas en diferentes archivos para demostrar la estructura de directorios symfony y la forma de separar el código entre las capas.
  22. El diseño
  23. ---------
  24. En primer lugar, si miraste de cerca los mockups, te darás cuenta de que gran parte de cada una de las páginas tiene el mismo aspecto. Ya sabes que la duplicación de código esta mal, ya sea si estamos hablando de código HTML o PHP, por lo que necesitamos encontrar una manera de prevenir estos elementos de vista común resultantes de la duplicación de código.
  25. Una forma de resolver el problema es definir un encabezado y un pie de página y lo incluyes en cada plantilla:
  26. ![Header and footer](http://www.symfony-project.org/images/jobeet/1_4/04/header_footer.png)
  27. Pero los archivos de la cabecera y el pie de página no contienen HTML válido. Debe haber una mejor manera. En lugar de reinventar la rueda, vamos a utilizar otro patrón de diseño para resolver este problema: el
  28. [patrón de diseño decorador](http://en.wikipedia.org/wiki/Decorator_pattern).
  29. El patrón de diseño decorador resuelve el problema al revés: la plantilla es decorada después de que el contenido es mostrado por una plantilla global, llamada **layout** en Symfony:
  30. ![Layout](http://www.symfony-project.org/images/jobeet/1_4/04/layout.png)
  31. El layout de una aplicación se llama `layout.php` y se puede encontrar en el directorio `apps/frontend/templates/`. Este directorio contiene todas las plantillas globales para una aplicación.
  32. Reemplaza el layout por defecto de Symfony por el siguiente código:
  33. [php]
  34. <!-- apps/frontend/templates/layout.php -->
  35. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  36. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  37. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  38. <head>
  39. <title>Jobeet - Your best job board</title>
  40. <link rel="shortcut icon" href="/favicon.ico" />
  41. <?php include_javascripts() ?>
  42. <?php include_stylesheets() ?>
  43. </head>
  44. <body>
  45. <div id="container">
  46. <div id="header">
  47. <div class="content">
  48. <h1><a href="<?php echo url_for('job/index') ?>">
  49. <img src="/images/logo.jpg" alt="Jobeet Job Board" />
  50. </a></h1>
  51. <div id="sub_header">
  52. <div class="post">
  53. <h2>Ask for people</h2>
  54. <div>
  55. <a href="<?php echo url_for('job/index') ?>">Post a Job</a>
  56. </div>
  57. </div>
  58. <div class="search">
  59. <h2>Ask for a job</h2>
  60. <form action="" method="get">
  61. <input type="text" name="keywords"
  62. id="search_keywords" />
  63. <input type="submit" value="search" />
  64. <div class="help">
  65. Enter some keywords (city, country, position, ...)
  66. </div>
  67. </form>
  68. </div>
  69. </div>
  70. </div>
  71. </div>
  72. <div id="content">
  73. <?php if ($sf_user->hasFlash('notice')): ?>
  74. <div class="flash_notice">
  75. <?php echo $sf_user->getFlash('notice') ?>
  76. </div>
  77. <?php endif; ?>
  78. <?php if ($sf_user->hasFlash('error')): ?>
  79. <div class="flash_error">
  80. <?php echo $sf_user->getFlash('error') ?>
  81. </div>
  82. <?php endif; ?>
  83. <div class="content">
  84. <?php echo $sf_content ?>
  85. </div>
  86. </div>
  87. <div id="footer">
  88. <div class="content">
  89. <span class="symfony">
  90. <img src="/images/jobeet-mini.png" />
  91. powered by <a href="http://www.symfony-project.org/">
  92. <img src="/images/symfony.gif" alt="symfony framework" />
  93. </a>
  94. </span>
  95. <ul>
  96. <li><a href="">About Jobeet</a></li>
  97. <li class="feed"><a href="">Full feed</a></li>
  98. <li><a href="">Jobeet API</a></li>
  99. <li class="last"><a href="">Affiliates</a></li>
  100. </ul>
  101. </div>
  102. </div>
  103. </div>
  104. </body>
  105. </html>
  106. Una plantilla symfony es sólo un simple archivo PHP. En la plantilla layout, verás llamadas a funciones PHP y referencias a variables PHP. `$sf_content` es la variable más interesante: la define el mismo framework y contiene el código HTML generado por la acción.
  107. Si navegas el módulo `job` (`http://www.jobeet.com.localhost/frontend_dev.php/job`), verás que todas las acciones ahora son decoradas por el layout.
  108. Las Hojas de Estilo, Imágenes, y JavaScripts
  109. --------------------------------------------
  110. Ya que este tutorial no es acerca de diseño web, tenemos ya preparado todo lo necesario que usaremos para Jobeet:
  111. [descarga los archivo gráficos](http://www.symfony-project.org/get/jobeet/images.zip)
  112. y copiarlos dentro del directorio `web/images/`;
  113. [descarga los archivo de hojas de estilo](http://www.symfony-project.org/get/jobeet/css.zip)
  114. y copiarlos dentro del directorio `web/css/`.
  115. >**NOTE**
  116. >En el layout, hemos incluído un *favicon*. Puedes
  117. >[descargarlo de Jobeet](http://www.symfony-project.org/get/jobeet/favicon.ico)
  118. >y ponerlo bajo el directorio `web/`.
  119. ![El módulo job con un layout y recursos](http://www.symfony-project.org/images/jobeet/1_4/04/job_layout_assets.png)
  120. >**TIP**
  121. >Por defecto, la tarea `generate:project` ha creado tres directorios para los recusos de
  122. >project: `web/images/` para las imágenes, `web/css/` para las hojas de estilo, y
  123. >`web/js/` para los Javascripts. Esta es una de las muchas convenciones definidas por
  124. >Symfony, pero por supuesto puedes almacenar en otro lugar bajo el directorio
  125. >`web/`.
  126. El astuto lector habrá notado que, incluso si el archivo `main.css` no es mencionado en cualquier lugar del layout por defecto, está sin duda presentes en el código HTML generado. Pero no los otros. ¿Cómo es esto posible?
  127. El archivo de estilo se ha incluido por la llamada a la función `include_stylesheets()` que encuentra la etiqueta `<head>`. La función `include_stylesheets()` es llamada un **helper**. Un helper es una función, definida por Symfony, que puede tener parámetros y devolver código HTML. La mayoría de las veces, los helpers te ahorran tiempo, ellos empaquetan código en snippets (porciones de código) utilizados con frecuencia en las plantillas. El helper `include_stylesheets()` genera una etiqueta `<link>` para las hojas de estilo.
  128. Pero, ¿cómo hace el helper para saber que hojas de estilo incluir?
  129. La capa de la Vista se puede configurar editando el archivo de configuración
  130. `view.yml` de la aplicación. Aquí está el archivo por defecto generado por la tarea `generate:app`:
  131. [yml]
  132. # apps/frontend/config/view.yml
  133. default:
  134. http_metas:
  135. content-type: text/html
  136. metas:
  137. #title: symfony project
  138. #description: symfony project
  139. #keywords: symfony, project
  140. #language: en
  141. #robots: index, follow
  142. stylesheets: [main.css]
  143. javascripts: []
  144. has_layout: true
  145. layout: layout
  146. El archivo `view.yml` establece la configuración `por defecto` para todas las plantillas de la aplicación. Por ejemplo, para las `hojas de estilo` define un array de archivos de estilo para incluir en todas las páginas de la aplicación (la inclusión se hace por el helper `include_stylesheets()`).
  147. >**NOTE**
  148. >En el archivo de configuración `view.yml` por defecto, se hace referencia al archivo
  149. >`main.css`, y no al `/css/main.css`. Como cuestión de hecho, ambas definiciones
  150. >son equivalentes pues el prefijo relativo symfony es `/css/`.
  151. Si muchos archivos se definen, Symfony los incluye en el mismo orden que la definición:
  152. [yml]
  153. stylesheets: [main.css, jobs.css, job.css]
  154. También puedes cambiar el atributo `media` y omitir el sufijo `.css`:
  155. [yml]
  156. stylesheets: [main.css, jobs.css, job.css, print: { media: print }]
  157. Esta configuración se presentará así:
  158. [php]
  159. <link rel="stylesheet" type="text/css" media="screen"
  160. href="/css/main.css" />
  161. <link rel="stylesheet" type="text/css" media="screen"
  162. href="/css/jobs.css" />
  163. <link rel="stylesheet" type="text/css" media="screen"
  164. href="/css/job.css" />
  165. <link rel="stylesheet" type="text/css" media="print"
  166. href="/css/print.css" />
  167. >**TIP**
  168. >El archivo de configuración `view.yml` también define el layout por defecto usado por la
  169. >aplicación. Por defecto, el nombre es `layout`, y así Symfony decora cada
  170. >página con el archivo `layout.php`. Puedes también deshabilitar el proceso de decoración
  171. >completo cambiando el valor de `has_layout` a `false`.
  172. Funciona como está pero el archivo `jobs.css` es solo necesario en la página principal y el
  173. `job.css` sólo es necesario para la página job. El archivo `view.yml` se puede personalizar partiendo de una base por-módulo. Cambia el archivo `view.yml` de la aplicación sólo para tener el archivo `main.css`:
  174. [yml]
  175. # apps/frontend/config/view.yml
  176. stylesheets: [main.css]
  177. Para personalizar la vista del módulo `job`, crea un nuevo archivo `view.yml` en el directorio `apps/frontend/modules/job/config/`:
  178. [yml]
  179. # apps/frontend/modules/job/config/view.yml
  180. indexSuccess:
  181. stylesheets: [jobs.css]
  182. showSuccess:
  183. stylesheets: [job.css]
  184. Bajo las secciones `indexSuccess` y `showSuccess` (ellas son las plantillas asociadas a las acciones `index` y `show`, como veremos más adelante), puedes personalizar cualquiera de los items encontrados bajo la sección `default` del `view.yml` de la aplicación. Todos los items se fusionan con la configuración de la aplicación. También puedes definir algunas configuraciones para todas las acciones de un módulo con la sección especial `all`.
  185. >**SIDEBAR**
  186. >Principios de configuración en Symfony
  187. >
  188. >Para los muchos archivos de configuración de Symfony, la misma configuración se puede definir en
  189. >diferentes niveles:
  190. >
  191. > * La configuración por defecto se encuentra en el framework
  192. > * La configuración global para el proyecto (en `config/`)
  193. > * La configuración local de una aplicación (en `apps/APP/config/`)
  194. > * La configuración local limitada a un módulo (en
  195. > `apps/APP/modules/MODULE/config/`)
  196. >
  197. >En tiempo de ejecución, la configuración del sistema combina todos los valores de los diferentes
  198. >archivos si existen y guarda en la memoria cache el resultado para un mejor rendimiento.
  199. Como regla empírica, cuando algo es configurable a través de un archivo de configuración, la misma puede realizarse con código PHP. En lugar de crear un archivo `view.yml` para el módulo `job` por ejemplo, también puedes utilizar el helper `use_stylesheet()` a fin de incluir una hoja de estilos a partir de una plantilla:
  200. [php]
  201. <?php use_stylesheet('main.css') ?>
  202. También puedes utilizar este helper en el layout a fin de incluir una hoja de estilo globalmente.
  203. Elegir entre un método u otro es realmente una cuestión de gusto. El archivo `view.yml` proporciona una manera de definir las cosas para todas las acciones de un módulo, las cuales no son posible en una plantilla, pero la configuración es bastante estático. Por otro lado, utilizando el helper `use_stylesheet()` es más flexible y además, todo está en el mismo lugar: la definición de estilos y el código HTML. Para Jobeet, vamos a utilizar el helper `use_stylesheet()`, osea puedes quitar el `view.yml` que recién creamos y actualiza la plantilla `job` con la llamada a `use_stylesheet()`:
  204. [php]
  205. <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
  206. <?php use_stylesheet('jobs.css') ?>
  207. <!-- apps/frontend/modules/job/templates/showSuccess.php -->
  208. <?php use_stylesheet('job.css') ?>
  209. >**NOTE**
  210. >En consecuencia, la configuración de JavaScript se realiza a través de la linea `javascripts`
  211. >del archivo de configuración `view.yml` y el helper `use_javascript()`
  212. >define los archivos JavaScript a incluir para una plantilla.
  213. La Página Principal de Puesto de Trabajo
  214. ----------------------------------------
  215. Como se observa en el día 3, la página principal de puestos de trabajo (job) es generada por la acción `index` del módulo `job`. La acción `index` es la parte del Controlador de la página y la plantilla asociada, `indexSuccess.php`, en la parte de la Vista:
  216. apps/
  217. frontend/
  218. modules/
  219. job/
  220. actions/
  221. actions.class.php
  222. templates/
  223. indexSuccess.php
  224. ### La Acción
  225. Cada acción está representada por un método de una clase. Para la página principal de puestos de trabajo, la clase es `jobActions` (el nombre del módulo seguido de `Actions`) y el método es `executeIndex()` (`execute` seguido por el nombre de la acción).
  226. Esto recupera todos los puestos de trabajo de la base de datos:
  227. [php]
  228. // apps/frontend/modules/job/actions/actions.class.php
  229. class jobActions extends sfActions
  230. {
  231. public function executeIndex(sfWebRequest $request)
  232. {
  233. <propel>
  234. $this->jobeet_jobs = JobeetJobPeer::doSelect(new Criteria());
  235. </propel>
  236. <doctrine>
  237. $this->jobeet_jobs = Doctrine::getTable('JobeetJob')
  238. ->createQuery('a')
  239. ->execute();
  240. </doctrine>
  241. }
  242. // ...
  243. }
  244. <propel>
  245. Echemos un vistazo más de cerca al código: el método `executeIndex()` (el Controlador) llama al Módelo `JobeetJobPeer` para recuperar todos los puestos de trabajo
  246. (`new Criteria()`). Devuelve un array de objetos `Job` que se asignan a la propiedad `jobeet_jobs` del objeto.
  247. </propel>
  248. <doctrine>
  249. Echemos un vistazo más de cerca al código: el método `executeIndex()` (el Controlador) llama a la Tabla `JobeetJob` para crear una consulta que recupere todos los puestos de trabajo. Devuelve una `Doctrine_Collection` de objetos `JobeetJob` que se asignan a la propiedad `jobeet_jobs` del objeto.
  250. </doctrine>
  251. Todas las propiedades del objeto luego se pasan automáticamente a la plantilla (la Vista). Para pasar los datos del Controlador a la Vista, solo crea una nueva propiedad:
  252. [php]
  253. public function executeFooBar(sfWebRequest $request)
  254. {
  255. $this->foo = 'bar';
  256. $this->bar = array('bar', 'baz');
  257. }
  258. Este código hará que las variables `$foo` y `$bar` sean accesibles en la plantilla.
  259. ### La Plantilla
  260. De forma predeterminada, el nombre de plantilla asociado con una acción se deduce por Symfony gracias a un convención (el nombre de la acción seguida por `Success`).
  261. La plantilla `indexSuccess.php` genera una tabla HTML para todos los puestos de trabajo. Aquí esta el códido de la plantilla actual:
  262. [php]
  263. <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
  264. <?php use_stylesheet('jobs.css') ?>
  265. <h1>Job List</h1>
  266. <table>
  267. <thead>
  268. <tr>
  269. <th>Id</th>
  270. <th>Category</th>
  271. <th>Type</th>
  272. <!-- more columns here -->
  273. <th>Created at</th>
  274. <th>Updated at</th>
  275. </tr>
  276. </thead>
  277. <tbody>
  278. <?php foreach ($jobeet_jobs as $jobeet_job): ?>
  279. <tr>
  280. <td>
  281. <a href="<?php echo url_for('job/show?id='.$jobeet_job->getId()) ?>">
  282. <?php echo $jobeet_job->getId() ?>
  283. </a>
  284. </td>
  285. <td><?php echo $jobeet_job->getCategoryId() ?></td>
  286. <td><?php echo $jobeet_job->getType() ?></td>
  287. <!-- more columns here -->
  288. <td><?php echo $jobeet_job->getCreatedAt() ?></td>
  289. <td><?php echo $jobeet_job->getUpdatedAt() ?></td>
  290. </tr>
  291. <?php endforeach; ?>
  292. </tbody>
  293. </table>
  294. <a href="<?php echo url_for('job/new') ?>">New</a>
  295. En el código de plantilla, el `foreach` itera a través de la lista de objetos `Job` (`$jobeet_jobs`), y para cada puesto de trabajo, cada valor de la columna es mostrado.
  296. Recuerda, el acceso a un valor de una columna es tan simple como una llamada al método de acceso cuyo nombre comienza con `get` y el nombre de la columna en formato CamelCase (por ejemplo, el método `getCreatedAt()` para la columna `created_at`).
  297. Vamos a limpiar esto un poco para mostrar sólo un subconjunto de las columnas disponibles:
  298. [php]
  299. <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
  300. <?php use_stylesheet('jobs.css') ?>
  301. <div id="jobs">
  302. <table class="jobs">
  303. <?php foreach ($jobeet_jobs as $i => $job): ?>
  304. <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>">
  305. <td class="location"><?php echo $job->getLocation() ?></td>
  306. <td class="position">
  307. <a href="<?php echo url_for('job/show?id='.$job->getId()) ?>">
  308. <?php echo $job->getPosition() ?>
  309. </a>
  310. </td>
  311. <td class="company"><?php echo $job->getCompany() ?></td>
  312. </tr>
  313. <?php endforeach; ?>
  314. </table>
  315. </div>
  316. ![Página de inicio](http://www.symfony-project.org/images/jobeet/1_4/04/homepage.png)
  317. La función `url_for()` en esta plantilla es un helper symfony que vamos a discutir mañana.
  318. La Plantilla para los Puestos de Trabajo
  319. ----------------------------------------
  320. Ahora vamos a personalizar la plantilla de la página de puestos de trabajo. Abre el archivo `showSuccess.php` y reemplaza su contenido con el siguiente código:
  321. [php]
  322. <!-- apps/frontend/modules/job/templates/showSuccess.php -->
  323. <?php use_stylesheet('job.css') ?>
  324. <?php use_helper('Text') ?>
  325. <div id="job">
  326. <h1><?php echo $job->getCompany() ?></h1>
  327. <h2><?php echo $job->getLocation() ?></h2>
  328. <h3>
  329. <?php echo $job->getPosition() ?>
  330. <small> - <?php echo $job->getType() ?></small>
  331. </h3>
  332. <?php if ($job->getLogo()): ?>
  333. <div class="logo">
  334. <a href="<?php echo $job->getUrl() ?>">
  335. <img src="/uploads/jobs/<?php echo $job->getLogo() ?>"
  336. alt="<?php echo $job->getCompany() ?> logo" />
  337. </a>
  338. </div>
  339. <?php endif; ?>
  340. <div class="description">
  341. <?php echo simple_format_text($job->getDescription()) ?>
  342. </div>
  343. <h4>How to apply?</h4>
  344. <p class="how_to_apply"><?php echo $job->getHowToApply() ?></p>
  345. <div class="meta">
  346. <propel>
  347. <small>posted on <?php echo $job->getCreatedAt('m/d/Y') ?></small>
  348. </propel>
  349. <doctrine>
  350. <small>posted on <?php echo $job->getDateTimeObject('created_at')->format('m/d/Y') ?></small>
  351. </doctrine>
  352. </div>
  353. <div style="padding: 20px 0">
  354. <a href="<?php echo url_for('job/edit?id='.$job->getId()) ?>">
  355. Edit
  356. </a>
  357. </div>
  358. </div>
  359. Esta plantilla utiliza la variable `$job` pasada por la acción para mostrar la información del puesto de trabajo. Como hemos rebautizado el nombre de variable pasada a la plantilla de `$jobeet_job` a `$job`, es necesario también realizar este cambio en la acción `show` (tener cuidado, hay dos ocurrencias de la variable):
  360. [php]
  361. // apps/frontend/modules/job/actions/actions.class.php
  362. public function executeShow(sfWebRequest $request)
  363. {
  364. <propel>
  365. $this->job =
  366. JobeetJobPeer::retrieveByPk($request->getParameter('id'));
  367. </propel>
  368. <doctrine>
  369. $this->job = Doctrine::getTable('JobeetJob')->
  370. find($request->getParameter('id'));
  371. </doctrine>
  372. $this->forward404Unless($this->job);
  373. }
  374. <propel>
  375. Observa que algunos métodos de acceso de Propel toman argumentos. Como hemos definido la columna `created_at` como un timestamp, el método de acceso `getCreatedAt()` toma un día formateado como su primer argumento:
  376. [php]
  377. $job->getCreatedAt('m/d/Y');
  378. </propel>
  379. >**NOTE**
  380. >La descripción del trabajo usa el helper `simple_format_text()` para formatearlo como
  381. >HTML, sustituyendo los retornos de carro con `<br />` por ejemplo. Como este helper
  382. >pertenece al grupo de helper `Text`, que no se carga por defecto, tenemos
  383. >que cargarlo manualmente utilizando el helper `use_helper()`.
  384. ![Página del Puesto de Trabajo](http://www.symfony-project.org/images/jobeet/1_4/04/job.png)
  385. Los Slots
  386. ---------
  387. Ahora, el título de todas las páginas se define en la etiqueta `<title>` del layout:
  388. [php]
  389. <title>Jobeet - Your best job board</title>
  390. Sin embargo, para la página de puestos de trabajo, queremos darle más información útil, como el nombre de la empresa y el puesto de trabajo a ocupar.
  391. En Symfony, cuando una zona del layout depende de la plantilla para mostrarse, necesitas definir un slot:
  392. ![Slots](http://www.symfony-project.org/images/jobeet/1_4/04/layout_slots.png)
  393. Añade un slot al layout para permitir que el título sea dinámico:
  394. [php]
  395. // apps/frontend/templates/layout.php
  396. <title><?php include_slot('title') ?></title>
  397. Cada slot es definido por un nombre (`title`) y se pueden visualizar mediante el uso del helper `include_slot()`. Ahora, al comienzo de la plantilla `showSuccess.php`, usa el helper `slot()` para definir el contenido del slot para la página de puestos de trabajo:
  398. [php]
  399. // apps/frontend/modules/job/templates/showSuccess.php
  400. <?php slot(
  401. 'title',
  402. sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()))
  403. ?>
  404. Si el título es complejo de generar, el helper `slot()` también se puede utilizar con un bloque de código:
  405. [php]
  406. // apps/frontend/modules/job/templates/showSuccess.php
  407. <?php slot('title') ?>
  408. <?php echo sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()) ?>
  409. <?php end_slot(); ?>
  410. Para algunas páginas, como la página de inicio, sólo necesitamos un título genérico. En lugar de repetir el mismo título una y otra vez en las plantillas, podemos definir un título predeterminado en el layout:
  411. [php]
  412. // apps/frontend/templates/layout.php
  413. <title>
  414. <?php if (!include_slot('title')): ?>
  415. Jobeet - Your best job board
  416. <?php endif; ?>
  417. </title>
  418. El helper `include_slot()` regresa `true` si el slot se ha definido. Por lo tanto, cuando defines el contenido del slot `title` en una plantilla, éste es usado; sino, el título predeterminado será utilizado.
  419. >**TIP**
  420. >Ya hemos visto bastantes helpers comenzando con `include_`. Estos helpers
  421. >generan el HTML y en la mayoría de los casos tienen un helper `get_` como contrapartida
  422. >solo para devolver el contenido:
  423. >
  424. > [php]
  425. > <?php include_slot('title') ?>
  426. > <?php echo get_slot('title') ?>
  427. >
  428. > <?php include_stylesheets() ?>
  429. > <?php echo get_stylesheets() ?>
  430. La Acción de la Página de Puestos de Trabajo
  431. --------------------------------------------
  432. La página de puestos de trabajo es generada por la acción `show`, definida en el método `executeShow()` del módulo `job`:
  433. [php]
  434. class jobActions extends sfActions
  435. {
  436. public function executeShow(sfWebRequest $request)
  437. {
  438. <propel>
  439. $this->job =
  440. JobeetJobPeer::retrieveByPk($request->getParameter('id'));
  441. </propel>
  442. <doctrine>
  443. $this->job = Doctrine::getTable('JobeetJob')->
  444. find($request->getParameter('id'));
  445. </doctrine>
  446. $this->forward404Unless($this->job);
  447. }
  448. // ...
  449. }
  450. <propel>
  451. Como en la acción `index`, la clase `JobeetJobPeer` es usada para obtener un puesto de trabajo, esta vez es mediante el uso del método `retrieveByPk()`. El parámetro de este método es el identificador único de un puesto de trabajo, su clave principal. La siguiente sección explicará el por qué la sentencia `$request->getParameter('id')` devuelve la clave principal del puesto de trabajo.
  452. </propel>
  453. <doctrine>
  454. Como en la acción `index`, la clase `JobeetJob` es usada para obtener un puesto de trabajo, esta vez es mediante el uso del método `find()`. El parámetro de este método es el identificador único de un puesto de trabajo, su clave principal. La siguiente sección explicará el por qué la sentencia `$request->getParameter('id')` devuelve la clave principal del puesto de trabajo.
  455. </doctrine>
  456. <propel>
  457. >**TIP**
  458. >Las clases del modelo generadas tienen un montón de métodos útiles para interactuar con
  459. >los objetos del proyecto. Date un tiempo para navegar por el código ubicado en el directorio
  460. >`lib/om/` y descubre todo el poder de estas clases.
  461. </propel>
  462. Si el puesto de trabajo no existe en la base de datos, queremos llevar al usuario a una página 404, que es exactamente lo que hace el método `forward404Unless()`. Este toma un Booleano como primer argumento y, a menos que sea true, detiene el flujo de la actual ejecución. Como los métodos forward detienen la ejecución de la acción enseguida lanzando un `sfError404Exception`, no necesitas después volver atrás.
  463. En cuanto a las excepciones, la página que aparece al usuario es diferente en el entorno `prod` del entorno `dev`:
  464. ![error 404 en el entorno dev](http://www.symfony-project.org/images/jobeet/1_4/05/404_dev.png)
  465. ![error 404 en el entorno prod](http://www.symfony-project.org/images/jobeet/1_4/05/404_prod.png)
  466. >**NOTE**
  467. >Antes de implementar el sitio web Jobeet para el servidor de producción, aprenderás
  468. >cómo personalizar la página 404 por defecto.
  469. -
  470. >**SIDEBAR**
  471. >La Familia de Métodos "forward"
  472. >
  473. >El `forward404Unless` es en realidad equivalente a:
  474. >
  475. > [php]
  476. > $this->forward404If(!$this->job);
  477. >
  478. >que también es equivalente a:
  479. >
  480. > [php]
  481. > if (!$this->job)
  482. > {
  483. > $this->forward404();
  484. > }
  485. >
  486. >El método `forward404()` en mismo es sólo un atajo para:
  487. >
  488. > [php]
  489. > $this->forward('default', '404');
  490. >
  491. >El método `forward()` hace un forward a otra acción de la misma aplicación;
  492. >en el ejemplo anterior, para la acción `404` del módulo `default`.
  493. >El módulo `default` es incluído con Symfony y da acciones predeterminadas
  494. >para mostrar páginas 404, de seguridad, y de login.
  495. La Petición y la Respuesta
  496. --------------------------
  497. Cuando navegas por las páginas `/job` o `/job/show/id/1` en tu navegador, estás iniciando un viaje de ida y vuelta al servidor web. El navegador está enviando una **Petición** y el servidor devuelve una **Respuesta**.
  498. Ya hemos visto que Symfony encapsula la petición en un object `sfWebRequest` (mirá el método `executeShow()`). Y como Symfony es un Framework Orientado a Objetos, la respuesta es también un objeto, de la clase `sfWebResponse`. Puedes acceder al objeto respuesta en la acción llamando a `$this->getResponse()`.
  499. Estos objetos proporcionan una gran cantidad de métodos convenientes para acceder a la información de funciones PHP y variables globales PHP.
  500. >**NOTE**
  501. >¿Por qué Symfony envuelve funcionalidades PHP existentes? En primer lugar, porque
  502. >Symfony y sus métodos son más poderosos que su homólogo PHP. Luego, porque
  503. >cuando se prueba una aplicación, es mucho más fácil para simular un request o
  504. >un response con Objetos que tratar de ver alrededor de variables globales o trabajar
  505. >con funciones PHP como `header()` la cuales hacen demasiado magia por detrás.
  506. ### La Petición
  507. La clase `sfWebRequest` envuelve a los arrays PHP globales `$_SERVER`, `$_COOKIE`, `$_GET`, `$_POST`,
  508. y `$_FILES`:
  509. Nombre del método | PHP equivalente
  510. -------------------- | --------------------------------------------------
  511. `getMethod()` | `$_SERVER['REQUEST_METHOD']`
  512. `getUri()` | `$_SERVER['REQUEST_URI']`
  513. `getReferer()` | `$_SERVER['HTTP_REFERER']`
  514. `getHost()` | `$_SERVER['HTTP_HOST']`
  515. `getLanguages()` | `$_SERVER['HTTP_ACCEPT_LANGUAGE']`
  516. `getCharsets()` | `$_SERVER['HTTP_ACCEPT_CHARSET']`
  517. `isXmlHttpRequest()` | `$_SERVER['X_REQUESTED_WITH'] == 'XMLHttpRequest'`
  518. `getHttpHeader()` | `$_SERVER`
  519. `getCookie()` | `$_COOKIE`
  520. `isSecure()` | `$_SERVER['HTTPS']`
  521. `getFiles()` | `$_FILES`
  522. `getGetParameter()` | `$_GET`
  523. `getPostParameter()` | `$_POST`
  524. `getUrlParameter()` | `$_SERVER['PATH_INFO']`
  525. `getRemoteAddress()` | `$_SERVER['REMOTE_ADDR']`
  526. Ya hemos accedido a parámetros de solicitud usando el método `getParameter()`. Devuelve un valor de la variable global `$_GET` o `$_POST`, o de la variable `PATH_INFO`.
  527. Si deseas asegurarte de que un parámetro de la petición procede de uno particular de estas variables, necesitas utilizar los métodos `getGetParameter()`, `getPostParameter()`,
  528. y `getUrlParameter()` respectivamente.
  529. >**NOTE**
  530. >Si deseas restringir la acción de un método específico, por ejemplo, cuando
  531. >que deseas asegurarte de que un formulario es enviado como `POST`, puede utilizar el
  532. >método `isMethod()`:
  533. `$this->forwardUnless($request->isMethod('POST'));`.
  534. ### La Respuesta
  535. La clase `sfWebResponse` envuelve a los métodos PHP `header()` y `setrawcookie()`:
  536. Nombre del método | PHP equivalente
  537. ----------------------------- | ----------------
  538. `setCookie()` | `setrawcookie()`
  539. `setStatusCode()` | `header()`
  540. `setHttpHeader()` | `header()`
  541. `setContentType()` | `header()`
  542. `addVaryHttpHeader()` | `header()`
  543. `addCacheControlHttpHeader()` | `header()`
  544. Por supuesto, que la clase `sfWebResponse` también proporciona una manera de configurar el contenido de la respuesta (`setContent()`) y enviar la respuesta al navegador (`send()`).
  545. Hoy hemos visto cómo gestionar hojas de estilo y JavaScripts tanto en el `view.yml` como en las plantillas. Al final, ambas técnicas utilizan el objeto Response y sus métodos `addStylesheet()` y `addJavascript()`.
  546. >**TIP**
  547. >Las clases `sfAction`, `sfRequest`, y `sfResponse` dan muchos otros
  548. >métodos útiles. No dudes en navegar por la
  549. >[API documentation](http://www.symfony-project.org/api/1_2/) para aprender más
  550. >acerca de todas las clases internas de Symfony.
  551. >**TIP**
  552. >Las clases [`sfAction`](http://www.symfony-project.org/api/1_4/sfAction),
  553. >[`sfRequest`](http://www.symfony-project.org/api/1_4/sfRequest), y
  554. >[`sfResponse`](http://www.symfony-project.org/api/1_4/sfResponse)
  555. >dan muchos otros métodos útiles. No dudes en navegar por la
  556. >[documentación API](http://www.symfony-project.org/api/1_4/) para aprender más
  557. >acerca de todas las clases internas de Symfony.
  558. Nos vemos mañana
  559. ----------------
  560. Hoy, hemos descrito algunos patrones de diseño utilizados por Symfony. Esperemos que la estructura de directorios del proyecto ahora tenga más sentido. Hemos tocado con las plantillas mediante la manipulación del layout y los archivos de plantilla. También lo hemos hecho un poco más dinámico gracias a slots y las acciones.
  561. Mañana, vamos a aprender más acerca del helper `url_for()` que hemos utilizado hoy, y el sub-framework de enrutamiento asociado a él.
  562. Feedback
  563. --------
  564. >**Tip**
  565. >Este capítulo ha sido traducido por **Roberto Germán Puentes Díaz**.
  566. >Si encuentras algún error que deseas corregir o realizar algún comentario,
  567. >no dudes en enviarlo por correo a **puentesdiaz [arroba] gmail.com**
  568. __ORM__