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