PageRenderTime 64ms CodeModel.GetById 6ms RepoModel.GetById 1ms app.codeStats 0ms

/jobeet/pt_BR/04.markdown

https://gitlab.com/intelij/symfony1-docs
Markdown | 902 lines | 739 code | 163 blank | 0 comment | 0 complexity | ba52c2d8988ab1a8fc2a960cdaa34c67 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, CC-BY-SA-4.0
  1. Dia 4: O Controller e a View
  2. ============================
  3. Ontem exploramos como o symfony simplifica o gerenciamento do banco de dados
  4. abstraindo as diferenças entre os SGBDs, e fazendo a conversão dos elementos
  5. relacionais para boas classes orientadas a objetos. Nós também brincamos com o
  6. ##ORM## para fazer a descrição do schema do banco, criar as tabelas e popular
  7. o banco com alguns dados iniciais.
  8. Hoje vamos personalizar o módulo básico `job` que criamos anteriormente. O
  9. módulo `job` tem todo o código que precisamos para o Jobeet:
  10. * Uma página para listar todos os empregos
  11. * Uma página para criar um novo emprego
  12. * Uma página para atualizar um emprego existente
  13. * Uma página para apagar um emprego
  14. Embora o código esteja pronto para ser usado da forma que está, iremos
  15. refatorar os templates para corresponder mais de perto com os mockups do
  16. Jobeet.
  17. A Arquitetura MVC
  18. -----------------
  19. Se você estiver acostumado a desenvolver sites PHP sem um framework,
  20. provavelmente deve usar o paradigma de um arquivo PHP por página HTML. Esses
  21. arquivos PHP provavelmente contém o mesmo tipo de estrutura: configuração
  22. inicial e global, a lógica de negócios relacionada a página requisitada,
  23. consultas no banco de dados e finalmente o código HTML que monta a página.
  24. Você pode usar um sistema de template para separar a lógica do HTML. Talvez
  25. você use uma camada de abstração de banco de dados para separar a interação de
  26. models da lógica de negócios. Mas na maioria das vezes, você acaba com um monte
  27. de código que é um pesadelo para ser mantido. Ele é rápido para criar, mas, com
  28. o tempo, é cada vez mais difícil para fazer mudanças, especialmente porque
  29. ninguém além de você entende como tudo foi feito e como está funcionando.
  30. Tal como acontece com todo problema, existem boas soluções. Para o
  31. desenvolvimento web, a solução mais comum atualmente para organizar seu código
  32. é o [**padrão de projeto MVC**](http://en.wikipedia.org/wiki/Model-view-controller). Ele define uma forma de
  33. organizar seu código de acordo com sua natureza. Esse padrão separa o código em
  34. **três camadas**:
  35. * A camada **Model** define a regra de negócios (o banco de dados pertence a
  36. esta camada). Você sabe que o symfony guarda todas as classes e os
  37. arquivos relacionados ao Model no diretório `lib/model/`.
  38. * A **View** é aquela com a qual o usuário interage (um sistema de template
  39. é parte dessa camada). No symfony, a camada layer é feita principalmente de
  40. templates PHP. Eles são guardados em vários diretórios `templates/` como
  41. veremos mais tarde por aqui.
  42. * O **Controller** é um pedaço de código que chama o Model para pegar alguns
  43. dados e passá-los para a View fazer a renderização para o cliente. Quando
  44. instalamos o symfony no começo desse livro, nós vimos que todas as
  45. requisições são gerenciadas pelos front controllers (`index.php` e
  46. `frontend_dev.php`). Esses front controllers delegam o trabalho de verdade
  47. para **actions**. Como vimos anteriormente, essas actions são agrupadas de
  48. forma lógica em **módulos**.
  49. ![MVC](http://www.symfony-project.org/images/jobeet/1_4/04/mvc.png)
  50. Hoje, iremos usar o mockup definido no dia 2 para personalizar a página inicial
  51. e a página de empregos. Iremos também deixá-las dinâmicas. Ao longo do caminho
  52. iremos ajustar um série de coisas em vários arquivos diferentes para demonstrar
  53. a estrutura de diretórios do symfony e a maneira de separar código entre as
  54. camadas.
  55. O Layout
  56. --------
  57. Primeiro, se você verificar bem os mockups, irá notar que boa parte das páginas
  58. é quase igual. Você sabe que duplicar código é ruim, tanto código HTML ou
  59. PHP, então precisamos encontrar um jeito de prevenir que esses elementos comuns
  60. da view resultem em duplicação de código.
  61. Um jeito de resolver o problema é definir um cabeçalho e um rodapé e incluí-los
  62. em cada um dos templates.
  63. ![Cabeçalho e rodapé](http://www.symfony-project.org/images/jobeet/1_4/04/header_footer.png)
  64. Mas aqui os arquivos do cabeçalho e do rodapé não contém HTML válido. Deve
  65. haver um jeito melhor. Em vez de reinventar a roda, vamos usar outro padrão de
  66. projeto para resolver esse problema: o
  67. [padrão de projeto Decorator](http://en.wikipedia.org/wiki/Decorator_pattern).
  68. Esse padrão resolve o problema no sentido inverso: o template é decorado depois
  69. de o conteúdo ser renderizado por um template global, chamado de **Layout** no
  70. symfony:
  71. ![Layout](http://www.symfony-project.org/images/jobeet/1_4/04/layout.png)
  72. O layout padrão de uma aplicação é chamado `layout.php` e pode ser encontrado
  73. no diretório `apps/frontend/templates/`. Esse diretório contém todos os
  74. templates globais para uma aplicação.
  75. Substitua o layout padrão com o seguinte código:
  76. [php]
  77. <!-- apps/frontend/templates/layout.php -->
  78. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  79. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  80. <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  81. <head>
  82. <title>Jobeet - Your best job board</title>
  83. <link rel="shortcut icon" href="/favicon.ico" />
  84. <?php include_javascripts() ?>
  85. <?php include_stylesheets() ?>
  86. </head>
  87. <body>
  88. <div id="container">
  89. <div id="header">
  90. <div class="content">
  91. <h1><a href="<?php echo url_for('job/index') ?>">
  92. <img src="/images/logo.jpg" alt="Jobeet Job Board" />
  93. </a></h1>
  94. <div id="sub_header">
  95. <div class="post">
  96. <h2>Ask for people</h2>
  97. <div>
  98. <a href="<?php echo url_for('job/index') ?>">Post a Job</a>
  99. </div>
  100. </div>
  101. <div class="search">
  102. <h2>Ask for a job</h2>
  103. <form action="" method="get">
  104. <input type="text" name="keywords"
  105. id="search_keywords" />
  106. <input type="submit" value="search" />
  107. <div class="help">
  108. Enter some keywords (city, country, position, ...)
  109. </div>
  110. </form>
  111. </div>
  112. </div>
  113. </div>
  114. </div>
  115. <div id="content">
  116. <?php if ($sf_user->hasFlash('notice')): ?>
  117. <div class="flash_notice">
  118. <?php echo $sf_user->getFlash('notice') ?>
  119. </div>
  120. <?php endif ?>
  121. <?php if ($sf_user->hasFlash('error')): ?>
  122. <div class="flash_error">
  123. <?php echo $sf_user->getFlash('error') ?>
  124. </div>
  125. <?php endif ?>
  126. <div class="content">
  127. <?php echo $sf_content ?>
  128. </div>
  129. </div>
  130. <div id="footer">
  131. <div class="content">
  132. <span class="symfony">
  133. <img src="/images/jobeet-mini.png" />
  134. powered by <a href="http://www.symfony-project.org/">
  135. <img src="/images/symfony.gif" alt="symfony framework" />
  136. </a>
  137. </span>
  138. <ul>
  139. <li><a href="">About Jobeet</a></li>
  140. <li class="feed"><a href="">Full feed</a></li>
  141. <li><a href="">Jobeet API</a></li>
  142. <li class="last"><a href="">Affiliates</a></li>
  143. </ul>
  144. </div>
  145. </div>
  146. </div>
  147. </body>
  148. </html>
  149. Um Template do symfony é apenas um arquivo PHP puro. No template layout, você
  150. enxerga chamadas para funções PHP e referências à variáveis. A variável mais
  151. interessante é a `$sf_content`: ela é definida pelo próprio framework e contém
  152. o HTML gerado pela action.
  153. Se você navegar pelo módulo `job`
  154. (`http://www.jobeet.com.localhost/frontend_dev.php/job`), verá que agora todas
  155. as ações estão decoradas pelo layout.
  156. As Folhas de Estilo, Imagens e JavaScripts
  157. ------------------------------------------
  158. Como esse tutorial não é sobre web design, nós deixamos preparados todos os
  159. "assets" que vamos precisar usar com o Jobeet:
  160. [faça o download](http://www.symfony-project.org/get/jobeet/images.zip) dos
  161. arquivos de imagem e os coloque dentro do diretório `web/images/`;
  162. [faça o download](http://www.symfony-project.org/get/jobeet/css.zip) dos
  163. arquivos de folha de estilo e os coloque dentro do diretório `web/css/`.
  164. >**NOTE**
  165. >Nós incluímos um *favicon* no layout. Você pode
  166. >[baixar o do Jobeet](http://www.symfony-project.org/get/jobeet/favicon.ico) e
  167. >colocá-lo dentro do diretório `web/`.
  168. ![O módulo job com o layout e os "assets"](http://www.symfony-project.org/images/jobeet/1_4/04/job_layout_assets.png)
  169. >**TIP**
  170. >Por padrão, o comando `generate:project` criou três diretórios para os
  171. >"assets" do projeto: `web/images` para imagens, `web/css` para as folhas de
  172. >estilo e `web/js` para os JavaScripts. Essa é uma das muitas convenções
  173. >definidas pelo symfony, mas é claro que você pode guardá-los em qualquer
  174. >lugar dentro do diretório `web\`.
  175. O leitor que está mais ligado deve ter notado que o arquivo `main.css` não
  176. é mencionado em nenhum lugar no layout padrão, mas definitivamente está
  177. presente no HTML gerado. E não está nos outros. Como isso é possível?
  178. A folha de estilo foi incluída pela chamada à função `include_stylesheets()`,
  179. encontrada dentro da tag `<head>` do layout. Essa função é chamada como um
  180. **helper**. Um helper é uma função, definida pelo symfony, que pode receber
  181. parâmetros e retornar código HTML. Na maior parte do tempo os helpers são
  182. poupadores de tempo, eles agrupam trechos de código usados frequentemente pelos
  183. templates. O helper `include_stylesheets()` cria as tags `<link>` para as folhas
  184. de estilo.
  185. Mas como o helper sabe quais folhas de estilo incluir?
  186. A camada View pode ser configurada editando o arquivo de configuração da
  187. aplicação `view.yml`. Aqui está o padrão gerado pelo comando `generate:app`:
  188. [yml]
  189. # apps/frontend/config/view.yml
  190. default:
  191. http_metas:
  192. content-type: text/html
  193. metas:
  194. #title: symfony project
  195. #description: symfony project
  196. #keywords: symfony, project
  197. #language: en
  198. #robots: index, follow
  199. stylesheets: [main.css]
  200. javascripts: []
  201. has_layout: true
  202. layout: layout
  203. O arquivo `view.yml` configura os definições padrões para todos os templates da
  204. aplicação. Por exemplo, a entrada `stylesheets` define um array de folhas de
  205. estilo para incluir em cada página da aplicação (a inclusão é feita pelo helper
  206. `include_stylesheets()`).
  207. >**NOTE**
  208. >No arquivo de configuração `view.yml` padrão, o arquivo referenciado é
  209. >`main.css`, e não `/css/main.css`. Na verdade, ambas as definições são
  210. >equivalentes pois o symfony põe o prefixo `/css/` nos caminhos relativos.
  211. Se vários arquivos estiverem definidos, o symfony fará a inclusão deles na
  212. mesma ordem da definição:
  213. [yml]
  214. stylesheets: [main.css, jobs.css, job.css]
  215. Você também pode mudar o atributo `media` e omitir o sufixo `.css`:
  216. [yml]
  217. stylesheets: [main.css, jobs.css, job.css, print: { media: print }]
  218. Essa configuração será renderizada assim:
  219. [php]
  220. <link rel="stylesheet" type="text/css" media="screen"
  221. href="/css/main.css" />
  222. <link rel="stylesheet" type="text/css" media="screen"
  223. href="/css/jobs.css" />
  224. <link rel="stylesheet" type="text/css" media="screen"
  225. href="/css/job.css" />
  226. <link rel="stylesheet" type="text/css" media="print"
  227. href="/css/print.css" />
  228. >**TIP**
  229. >O arquivo de configuração `view.yml` também define o Layout padrão a ser usado
  230. >pela aplicação. Por padrão o nome é `layout`, e então o symfony decora cada
  231. >uma das páginas com o arquivo `layout.php`. Você também pode desabilitar o
  232. >processo de decoração completamente alterando a entrada `has_layout` para
  233. >`false`.
  234. Isso funciona assim mas o arquivo `jobs.css` é necessário para a página
  235. inicial e o arquivo `job.css` é necessário para a página de emprego. O
  236. arquivo de configuração `view.yml` pode ser personalizado por módulo se quiser.
  237. Mude a chave stylesheets do arquivo `view.yml` da aplicação para conter apenas
  238. o arquivo `main.css`:
  239. [yml]
  240. # apps/frontend/config/view.yml
  241. stylesheets: [main.css]
  242. Para personalizar a view do módulo `job`, crie um arquivo `view.yml` no
  243. diretório `apps/frontend/modules/job/config/`:
  244. [yml]
  245. # apps/frontend/modules/job/config/view.yml
  246. indexSuccess:
  247. stylesheets: [jobs.css]
  248. showSuccess:
  249. stylesheets: [job.css]
  250. Debaixo das seções `indexSuccess` e `showSuccess` (existem nomes de template
  251. associados com as actions `index` e `show` como veremos mais à frente), você
  252. pode personalizar cada entrada encontrada na seção `default` do `view.yml` da
  253. aplicação. Todas as entradas específicas são mescladas com a configuração da
  254. aplicação. Você também pode definir algumas configurações para todas as actions
  255. de um módulo com a seção especial `all`.
  256. >**SIDEBAR**
  257. >Princípios de Configuração no symfony
  258. >
  259. >Em muitos dos arquivos de configuração do symfony, a mesma configuração pode
  260. >ser definida em diferentes níveis:
  261. >
  262. > * A configuração global localizada no framework
  263. > * A configuração global do projeto (em `config/`)
  264. > * A configuração local de uma aplicação (em `apps/APP/config`)
  265. > * A configuração local restrita a um módulo (in
  266. > `apps/APP/modules/MODULE/config/`)
  267. >
  268. >Em tempo de execução, o sistema de configuração mescla todos os valores dos
  269. >diferentes arquivos se eles existirem e faz cache do resultado para melhorar
  270. >o desempenho.
  271. Como regra geral, quando algo é configurável via um arquivo de configuração, o
  272. mesmo pode ser feito com código PHP. Em vez de criar o arquivo `view.yml` para
  273. o módulo `job` por exemplo, você também poderia ter usado o helper
  274. `use_stylesheet()` para incluir uma folha de estilo a partir do template:
  275. [php]
  276. <?php use_stylesheet('main.css') ?>
  277. Você também pode usar esse helper em um layout para incluir uma folha de estilo
  278. globalmente.
  279. Escolher entre um método ou outro é realmente uma questão de gosto. O arquivo
  280. `view.yml` fornece um meio de definir coisas para todas as ações de um módulo,
  281. o que não é possível em um template, mas a configuração é bastante estática.
  282. Por outro lado, usar o helper `use_stylesheet()` é mais flexível e, além disso,
  283. tudo fica no mesmo lugar: a definição das folhas de estilo e o código HTML.
  284. Para o Jobeet, usaremos o helper `use_stylesheet()`, então você pode remover o
  285. arquivo `view.yml` que acabmos de criar e atualizar o template `job` com as
  286. chamadas `use_stylesheet()`:
  287. [php]
  288. <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
  289. <?php use_stylesheet('jobs.css') ?>
  290. <!-- apps/frontend/modules/job/templates/showSuccess.php -->
  291. <?php use_stylesheet('job.css') ?>
  292. >**NOTE**
  293. >De forma semelhante, a configuração dos JavaScripts é feita pela entrada
  294. >`javascripts` do arquivo de configuração `view.yml` e o helper
  295. >`use_javascripts()` define os arquivos JavaScript a serem incluídos pelo
  296. >template.
  297. A Página Inicial de Emprego
  298. ---------------------------
  299. Como visto no dia 3, a página inicial de emprego é gerada pela action `index`
  300. do módulo `job`. A action `index` é a parte do Controller da página e o
  301. template associado, `indexSuccess.php` é a parte da View:
  302. apps/
  303. frontend/
  304. modules/
  305. job/
  306. actions/
  307. actions.class.php
  308. templates/
  309. indexSuccess.php
  310. ### A Action
  311. Cada Action é representada por um método de uma classe. Para a página de
  312. emprego a classe é a `jobActons` (o nome do módulo com o sufixo `Actions`) e
  313. o método é o `executeIndex()` (`execute` seguido do sufixo que é o nome da
  314. action). Ele retorna todos os empregos do banco de dados:
  315. [php]
  316. // apps/frontend/modules/job/actions/actions.class.php
  317. class jobActions extends sfActions
  318. {
  319. public function executeIndex(sfWebRequest $request)
  320. {
  321. <propel>
  322. $this->jobeet_jobs = JobeetJobPeer::doSelect(new Criteria());
  323. </propel>
  324. <doctrine>
  325. $this->jobeet_jobs = Doctrine::getTable('JobeetJob')
  326. ->createQuery('a')
  327. ->execute();
  328. </doctrine>
  329. }
  330. // ...
  331. }
  332. <propel>
  333. Vamos examinar o código mais de perto: o método `executeIndex()` (o Controller)
  334. chama o Model `JobeetJobPeer` para buscar todos os empregos (`new Criteria()`).
  335. Ele retorna um array de objetos `JobeetJob` que são atribuídos à propriedade
  336. `jobeet_jobs` do objeto.
  337. </propel>
  338. <doctrine>
  339. Vamos examinar o código mais de perto: o método `executeIndex()` (o Controller)
  340. chama a Tabela `JobeetJob` para criar uma consulta para buscar todos os
  341. empregos. Ele retorna uma `Doctrine_Collection` de objetos `JobeetJob` que
  342. são atribuídos à propriedade `jobeet_jobs` do objeto.
  343. </doctrine>
  344. Todas as propriedades do objeto são automaticamente passadas para o template
  345. (a View). Para passar dados do Controller para a View, simplesmente crie uma
  346. nova propriedade:
  347. [php]
  348. public function executeFooBar(sfWebRequest $request)
  349. {
  350. $this->foo = 'bar';
  351. $this->bar = array('bar', 'baz');
  352. }
  353. Esse código fará com que as variáveis `$foo` e `$bar` fiquem acessíveis no
  354. template.
  355. ### O Template
  356. Por padrão, o nome dos Templates associados com uma action é deduzido pelo
  357. symfony graças à convenção (o nome da action com o sufixo `Success`).
  358. O template `indexSuccess.php` gera uma tabela HTML com todos os empregos. Aqui
  359. está o código atual do template:
  360. [php]
  361. <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
  362. <?php use_stylesheet('jobs.css') ?>
  363. <h1>Job List</h1>
  364. <table>
  365. <thead>
  366. <tr>
  367. <th>Id</th>
  368. <th>Category</th>
  369. <th>Type</th>
  370. <!-- more columns here -->
  371. <th>Created at</th>
  372. <th>Updated at</th>
  373. </tr>
  374. </thead>
  375. <tbody>
  376. <?php foreach ($jobeet_jobs as $jobeet_job): ?>
  377. <tr>
  378. <td>
  379. <a href="<?php echo url_for('job/show?id='.$jobeet_job->getId()) ?>">
  380. <?php echo $jobeet_job->getId() ?>
  381. </a>
  382. </td>
  383. <td><?php echo $jobeet_job->getCategoryId() ?></td>
  384. <td><?php echo $jobeet_job->getType() ?></td>
  385. <!-- more columns here -->
  386. <td><?php echo $jobeet_job->getCreatedAt() ?></td>
  387. <td><?php echo $jobeet_job->getUpdatedAt() ?></td>
  388. </tr>
  389. <?php endforeach ?>
  390. </tbody>
  391. </table>
  392. <a href="<?php echo url_for('job/new') ?>">New</a>
  393. No código do template, um `foreach` faz a iteração pela lista de objetos `Job`
  394. (`$jobeet_jobs`), e para cada um dos emprego, suas colunas são mostradas.
  395. Lembre-se, acessar o valor de uma coluna é um simplesmente chamar um método
  396. "accessor" cujo nome começa com `get` e o nome da coluna em camelCase (por
  397. exemplo o método `getCreatedAt()` para a coluna `created_at`).
  398. Vamos limpar isso um pouco para mostrar apenas um subconjunto com as colunas
  399. disponíveis:
  400. [php]
  401. <!-- apps/frontend/modules/job/templates/indexSuccess.php -->
  402. <?php use_stylesheet('jobs.css') ?>
  403. <div id="jobs">
  404. <table class="jobs">
  405. <?php foreach ($jobeet_jobs as $i => $job): ?>
  406. <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>">
  407. <td class="location"><?php echo $job->getLocation() ?></td>
  408. <td class="position">
  409. <a href="<?php echo url_for('job/show?id='.$job->getId()) ?>">
  410. <?php echo $job->getPosition() ?>
  411. </a>
  412. </td>
  413. <td class="company"><?php echo $job->getCompany() ?></td>
  414. </tr>
  415. <?php endforeach ?>
  416. </table>
  417. </div>
  418. ![Homepage](http://www.symfony-project.org/images/jobeet/1_4/04/homepage.png)
  419. A chamada à função `url_for()` no template é um helper do symfony que iremos
  420. discutir amanhã.
  421. O Template da Página de Empregos
  422. --------------------------------
  423. Agora vamos personalizar o template da página de empregos. Abra o arquivo
  424. `showSucess.php` e substitua o conteúdo com o seguinte código:
  425. [php]
  426. <!-- apps/frontend/modules/job/templates/showSuccess.php -->
  427. <?php use_stylesheet('job.css') ?>
  428. <?php use_helper('Text') ?>
  429. <div id="job">
  430. <h1><?php echo $job->getCompany() ?></h1>
  431. <h2><?php echo $job->getLocation() ?></h2>
  432. <h3>
  433. <?php echo $job->getPosition() ?>
  434. <small> - <?php echo $job->getType() ?></small>
  435. </h3>
  436. <?php if ($job->getLogo()): ?>
  437. <div class="logo">
  438. <a href="<?php echo $job->getUrl() ?>">
  439. <img src="/uploads/jobs/<?php echo $job->getLogo() ?>"
  440. alt="<?php echo $job->getCompany() ?> logo" />
  441. </a>
  442. </div>
  443. <?php endif ?>
  444. <div class="description">
  445. <?php echo simple_format_text($job->getDescription()) ?>
  446. </div>
  447. <h4>How to apply?</h4>
  448. <p class="how_to_apply"><?php echo $job->getHowToApply() ?></p>
  449. <div class="meta">
  450. <propel>
  451. <small>posted on <?php echo $job->getCreatedAt('m/d/Y') ?></small>
  452. </propel>
  453. <doctrine>
  454. <small>posted on <?php echo $job->getDateTimeObject('created_at')->format('m/d/Y') ?></small>
  455. </doctrine>
  456. </div>
  457. <div style="padding: 20px 0">
  458. <a href="<?php echo url_for('job/edit?id='.$job->getId()) ?>">
  459. Edit
  460. </a>
  461. </div>
  462. </div>
  463. O template usa a variável `$job` passada pela action para mostrar a informação
  464. do emprego. Como renomeamos a variável passada para o template de `$jobeet_job`
  465. para `$job`, você precisa fazer essa mudança também na action `show` (tome
  466. cuidado, existem outras duas ocorrências da variável):
  467. [php]
  468. // apps/frontend/modules/job/actions/actions.class.php
  469. public function executeShow(sfWebRequest $request)
  470. {
  471. <propel>
  472. $this->job =
  473. JobeetJobPeer::retrieveByPk($request->getParameter('id'));
  474. </propel>
  475. <doctrine>
  476. $this->job = Doctrine::getTable('JobeetJob')->
  477. find($request->getParameter('id'));
  478. </doctrine>
  479. $this->forward404Unless($this->job);
  480. }
  481. <propel>
  482. Note que alguns dos métodos Accessors do Propel recebem argumentos. Como
  483. definimos a coluna `created_at` como um timestamp, o accessor `getCreatedAt()`
  484. recebe um padrão de formatação de data como seu primeiro argumento:
  485. [php]
  486. $job->getCreatedAt('m/d/Y');
  487. </propel>
  488. <doctrine>
  489. Note que as colunas de data podem ser convertidas para objetos instâncias do
  490. DateTime do PHP. Como definimos a coluna `created_at` com um timestamp, você
  491. pode converter o valor para um objeto DateTime usando o método
  492. `getDateTimeObject()` e chamando o método `format()` que recebe o padrão de
  493. formatação de data como seu primeiro argumento:
  494. [php]
  495. $job->getDateTimeObject('created_at')->format('m/d/Y');
  496. </doctrine>
  497. >**NOTE**
  498. >A descrição do emprego usa o helper `simple_format_text()` para formatá-lo
  499. >como HTML, substituindo os símbolos de retorno de carro por `<br />` por
  500. >exemplo. Como esse helper pertence ao grupo de helpers `Text`, que não é
  501. >carregado por padrão, temos que carregá-lo manualmente usando o helper
  502. >`use_helper`.
  503. ![Página de Emprego](http://www.symfony-project.org/images/jobeet/1_4/04/job.png)
  504. Slots
  505. -----
  506. Nesse momento, o título de todas as páginas é definido na tage `<title>` do
  507. layout:
  508. [php]
  509. <title>Jobeet - Your best job board</title>
  510. Mas para o página de emprego, queremos fornecer informações mais úteis, como o
  511. nome da empresa e o cargo.
  512. No symfony, quando uma área do layout depende de um template para ser mostrado,
  513. você precisa definir um "slot":
  514. ![Slots](http://www.symfony-project.org/images/jobeet/1_4/04/layout_slots.png)
  515. Adicione um slot no layout para permitir que o título seja dinâmico:
  516. [php]
  517. // apps/frontend/templates/layout.php
  518. <title><?php include_slot('title') ?></title>
  519. Cada slot é definido por um nome (`title`) e pode ser mostrado utilizando o
  520. helper `include_slot()`. Agora, no início do template `showSuccess.php`, use o
  521. helper `slot()` para definir o conteúdo do slot da página de empregos:
  522. [php]
  523. // apps/frontend/modules/job/templates/showSuccess.php
  524. <?php slot(
  525. 'title',
  526. sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()))
  527. ?>
  528. Se o título for complexo para ser gerado, o helper `slot()` também pode ser
  529. usado como um bloco de código:
  530. [php]
  531. // apps/frontend/modules/job/templates/showSuccess.php
  532. <?php slot('title') ?>
  533. <?php echo sprintf('%s is looking for a %s', $job->getCompany(), $job->getPosition()) ?>
  534. <?php end_slot() ?>
  535. Para algumas páginas, como a página inicial, precisamos apenas de um título
  536. genérico. Em vez de repetir o mesmo título inúmeras vezes nos templates,
  537. podemos definir um título padrão no layout:
  538. [php]
  539. // apps/frontend/templates/layout.php
  540. <title>
  541. <?php include_slot('title', 'Jobeet - Your best job board') ?>
  542. </title>
  543. O segundo argumento do método `include_slot()` é o valor padrão para o slot se
  544. ele não tiver sido definido. Se o valor padrão for maior ou tiver algumas tags
  545. HTML, você também pode defini-las como no código a seguir:
  546. [php]
  547. // apps/frontend/templates/layout.php
  548. <title>
  549. <?php if (!include_slot('title')): ?>
  550. Jobeet - Your best job board
  551. <?php endif ?>
  552. </title>
  553. O helper `include_slot()` retorna `true` se o slot for definido. Então, quando
  554. você definir o conteúdo do slot `title` em um template, ele será usado; senão,
  555. o título padrão será usado.
  556. >**TIP**
  557. >Nós vimos vários helpers começando com `include_`. Esses helpers geram um
  558. >HTML como saída e na maioria dos casos tem um helper `get_` em contrapartida
  559. >apenas para retornar o conteúdo:
  560. >
  561. > [php]
  562. > <?php include_slot('title') ?>
  563. > <?php echo get_slot('title') ?>
  564. >
  565. > <?php include_stylesheets() ?>
  566. > <?php echo get_stylesheets() ?>
  567. A Action da Página de Emprego
  568. -----------------------------
  569. A página de emprego é gerada pela action `show`, definida no método
  570. `executeShow()` do módulo `job`:
  571. [php]
  572. class jobActions extends sfActions
  573. {
  574. public function executeShow(sfWebRequest $request)
  575. {
  576. <propel>
  577. $this->job =
  578. JobeetJobPeer::retrieveByPk($request->getParameter('id'));
  579. </propel>
  580. <doctrine>
  581. $this->job = Doctrine::getTable('JobeetJob')->
  582. find($request->getParameter('id'));
  583. </doctrine>
  584. $this->forward404Unless($this->job);
  585. }
  586. // ...
  587. }
  588. <propel>
  589. Como na action `index`, a classe `JobeetJobPeer` é usada para retornar um
  590. emprego, dessa vez usando o método `retrieveByPk()`. O parâmetro desse método
  591. é o identificador único de um emprego, sua Chave Primária. A próxima seção irá
  592. explicar porque o comando `$request->getParameter('id')` retorna a chave
  593. primária do emprego.
  594. </propel>
  595. <doctrine>
  596. Como na action `index`, a classe de tabela `JobeetJob` é usada para retornar um
  597. emprego, dessa vez usando o método `find()`. O parâmetro desse método
  598. é o identificador único de um emprego, sua Chave Primária. A próxima seção irá
  599. explicar porque o comando `$request->getParameter('id')` retorna a chave
  600. primária do emprego.
  601. </doctrine>
  602. <propel>
  603. >**TIP**
  604. >A classe model gerada contém uma série de métodos úteis para interagir com os
  605. >objetos do projeto. Tire algum tempo para navegar pelo código localizado no
  606. >diretório `lib/om` e descobrir todo o poder embutido nessas classes.
  607. </propel>
  608. Se o emprego não existir no banco de dados, queremos redirecionar o usuário
  609. para uma página com um Erro 404, que é exatamente o que o método
  610. `forward404Unless()` faz. Ele recebe um Booleano como seu primeiro argumento e,
  611. ao menos que ele seja verdadeiro, ele para a o fluxo de execução atual. Como os
  612. métodos "forward" param a execução da action logo de imediato lançando uma
  613. `sfError404Exception`, você não precisa fazer um "return" depois disso.
  614. Como no caso das exceções, a página mostrada para o usuário é diferente no
  615. ambiente `prod` e no ambiente `dev`:
  616. ![erro 404 no ambiente dev](http://www.symfony-project.org/images/jobeet/1_4/05/404_dev.png)
  617. ![erro 404 no ambiente prod](http://www.symfony-project.org/images/jobeet/1_4/05/404_prod.png)
  618. >**NOTE**
  619. >Antes de você implantar o site do Jobeet no servidor de produção, você irá
  620. >aprender como personalizar a página 404 padrão.
  621. -
  622. >**SIDEBAR**
  623. >A Família de Métodos Forward
  624. >
  625. >A chamada `forward404Unless` é equivalente na verdade a:
  626. >
  627. > [php]
  628. > $this->forward404If(!$this->job);
  629. >
  630. >que também é equivalente a:
  631. >
  632. > [php]
  633. > if (!$this->job)
  634. > {
  635. > $this->forward404();
  636. > }
  637. >
  638. >O próprio método `forward404()` é apenas um atalho para:
  639. >
  640. > [php]
  641. > $this->forward('default', '404');
  642. >
  643. >O método `forward()` direciona para outra ação da mesma aplicação;
  644. >no exemplo anterior, a action `404` do módulo `default`.
  645. >O módulo `default` está embutido no symfony e fornece actions padrões para
  646. >renderizar páginas 404, páginas seguras e páginas de login.
  647. A Requisição e a Resposta
  648. -------------------------
  649. Quando você navega pelas páginas `/job` ou `/job/show/id/id` no seu navegador,
  650. você está iniciando uma viagem de ida e volta no servidor web. O navegador está
  651. enviando uma **Requisição HTTP** e o servidor manda de volta uma **Resposta
  652. HTTP**.
  653. Nós vimos que o symfony encapsula a requisão no objeto `sfWebRequest` (veja
  654. a assinatura do método `executeShow()`). E como o symfony é um framework
  655. Orientado a Objetos, a resposta também é um objeto, da classe `sfWebResponse`.
  656. Você pode acessar o objeto response em uma action chamando
  657. `$this->getResponse()`.
  658. Esses objetos fornecem vários métodos práticos para acessar informações de
  659. funções e variáveis globais do PHP.
  660. >**NOTE**
  661. >Por que o symfony envolve funcionalidades existentes no PHP? Primeiro, porque
  662. >os métodos do symfony são mais poderosos do que suas contrapartidas no PHP.
  663. >Assim, quando você testa uma aplicação é muito mais fácil simular um objeto
  664. >requisição ou um objeto resposta do que ficar tentando lidar com
  665. >variáveis globais ou trabalhar com funções PHP como `header()` que fazem muita
  666. >mágica.
  667. ### A Requisição
  668. A classe `sfWebRequest` envolve os arrays globais do PHP `$_SERVER`,
  669. `$_COOKIE`, `$_GET`, `$_POST` e `$_FILES`:
  670. Nome do método | Equivalente no PHP
  671. -------------------- | --------------------------------------------------
  672. `getMethod()` | `$_SERVER['REQUEST_METHOD']`
  673. `getUri()` | `$_SERVER['REQUEST_URI']`
  674. `getReferer()` | `$_SERVER['HTTP_REFERER']`
  675. `getHost()` | `$_SERVER['HTTP_HOST']`
  676. `getLanguages()` | `$_SERVER['HTTP_ACCEPT_LANGUAGE']`
  677. `getCharsets()` | `$_SERVER['HTTP_ACCEPT_CHARSET']`
  678. `isXmlHttpRequest()` | `$_SERVER['X_REQUESTED_WITH'] == 'XMLHttpRequest'`
  679. `getHttpHeader()` | `$_SERVER`
  680. `getCookie()` | `$_COOKIE`
  681. `isSecure()` | `$_SERVER['HTTPS']`
  682. `getFiles()` | `$_FILES`
  683. `getGetParameter()` | `$_GET`
  684. `getPostParameter()` | `$_POST`
  685. `getUrlParameter()` | `$_SERVER['PATH_INFO']`
  686. `getRemoteAddress()` | `$_SERVER['REMOTE_ADDR']`
  687. Nós acessamos parâmetros da requisição usando o método `getParameter()`. Ele
  688. retorna um valor das variáveis globais `$_GET` ou `$_POST`, ou da variável
  689. `PATH_INFO`.
  690. Se quiser garantir que um parâmetro da requisição vem de uma dessas variáveis
  691. em particular, você precisa usar os métodos `getGetParameter()`,
  692. `getPostParameter()` e `getUrlParameter()` respectivamente.
  693. >**NOTE**
  694. >Quando quiser restringir uma action para um Método HTTP específico, por
  695. >exemplo quando você quer garantir que um formulário foi submetido usando
  696. >`POST`, você pode usar o método `isMethod()`:
  697. >`$this->forwardUnless($request->isMethod('POST'));`.
  698. ### A Resposta
  699. A classe `sfWebResponse` envolve os métodos de Cabeçalhos HTTP e o
  700. `setrawcookie()` do PHP:
  701. Nome do método | Equivalente no PHP
  702. ----------------------------- | ------------------
  703. `setCookie()` | `setrawcookie()`
  704. `setStatusCode()` | `header()`
  705. `setHttpHeader()` | `header()`
  706. `setContentType()` | `header()`
  707. `addVaryHttpHeader()` | `header()`
  708. `addCacheControlHttpHeader()` | `header()`
  709. É claro, a classe `sfWebResponse` também fornece um meio de definir o conteúdo
  710. da resposta (`setContent()`) e enviar a resposta para o navegador (`send()`).
  711. Hoje mais cedo vimos como gerenciar folhas de estilo e JavaScripts tanto no
  712. arquivo `view.yml` quanto nos templates. No fim, ambas as técnicas usam os
  713. métodos de objeto Resposta `addStylesheet()` e `addJavascript()`.
  714. >**TIP**
  715. >As classes [`sfAction`](http://www.symfony-project.org/api/1_4/sfAction),
  716. >[`sfRequest`](http://www.symfony-project.org/api/1_4/sfRequest) e
  717. >[`sfResponse`](http://www.symfony-project.org/api/1_4/sfResponse)
  718. >fornecem muitos outros métodos úteis. Não hesite em navegar pela
  719. >[documentação da API](http://www.symfony-project.org/api/1_4/) para aprender
  720. >mais sobre as classes internas do symfony.
  721. Considerações finais
  722. --------------------
  723. Hoje descrevemos alguns dos padrões de projeto usados pelo symfony.
  724. Agora a estrutura de diretórios do projeto faz mais sentido. Nós brincamos com os
  725. templates manipulando os arquivos de layout e template. Também deixamos eles
  726. mais dinâmicos graças aos slots e as actions.
  727. Amanhã, iremos nos dedicar ao helper `url_for()` que usamos aqui, e o
  728. sub-framework de rotas associado com ele.
  729. Feedback
  730. --------
  731. >**Dica - pt_BR**
  732. >Este capítulo foi traduzido por **Rogerio Prado de Jesus**.
  733. >Se encontrar algum erro que deseja corrigir ou quiser fazer algum comentário
  734. >não deixe de enviar um e-mail para **rogeriopradoj [at] gmail.com**
  735. >**Tip - en**
  736. >This chapter was translated by **Rogerio Prado Jesus**.
  737. >If you find any errors to be corrected or you have any comments
  738. >do not hesitate to send an email to **rogeriopradoj [at] gmail.com**
  739. __ORM__