Development

Changeset 28395

You must first sign up to be able to contribute.

Changeset 28395

Show
Ignore:
Timestamp:
03/06/10 00:29:04 (5 years ago)
Author:
Andreia.Bellini
Message:

Corrections in the chapter 09-Doctrine-Form-Inheritance

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • doc/branches/1.4/more-with-symfony/pt/09-Doctrine-Form-Inheritance.markdown

    r28262 r28395  
    55 
    66No symfony 1.3, o ~Doctrine~ tornou-se, oficialmente, a biblioteca ORM padrão  
    7 enquanto o desenvolvimento utilizando o Propel teve queda nos últimos meses.  
    8 O suporte e melhorias ao projeto ~Propel~ ainda continua graças aos esforços dos membros da comunidade symfony. 
     7enquanto houve queda com o desenvolvimento utilizando o Propel nos últimos meses.  
     8O suporte e melhorias ao projeto ~Propel~ ainda continuam graças aos esforços dos membros da comunidade symfony. 
    99 
    1010O projeto Doctrine 1.2 tornou-se a nova biblioteca ORM padrão symfony por 
     
    138138~`column_aggregation`~ e dois novos atributos foram adicionados. O primeiro 
    139139atributo, `keyField`, especifica a coluna que será criada para armazenar o 
    140 tipo de informação para cada registro. O `keyField` é uma seqüência da coluna 
     140tipo de informação para cada registro. O `keyField` é uma coluna string coluna 
    141141chamada `type`, que é o nome padrão da coluna, se nenhum `keyField` for especificado. 
    142 O segundo atributo define o valor do tipo para cada registro que pertencem às classes  
     142O segundo atributo define o valor do tipo para cada registro que pertence às classes  
    143143`Professor` ou `Aluno`. 
    144144 
     
    146146 
    147147A estratégia de agregação de coluna é um bom método para herança de tabelas, uma vez que 
    148 cria uma única tabela (`Pessoa`), contendo todos os campos definidos mais o campo `tipo`.  
     148cria uma única tabela (`Pessoa`), contendo todos os campos definidos mais o campo `type`.  
    149149Por conseguinte, não há necessidade de criar várias tabelas e fazer joins com uma  
    150150consulta SQL. Abaixo estão alguns exemplos de como realizar consultas nas tabelas  
     
    169169Ao recuperar os dados de uma subclasse (`Professor`, `Aluno`), o  
    170170Doctrine anexará automaticamente a cláusula SQL `WHERE` para a consulta na 
    171 coluna `tipo` com o valor correspondente. 
     171coluna `type` com o valor correspondente. 
    172172 
    173173No entanto, existem algumas desvantagens de usar a estratégia de agregação de coluna em  
    174174determinados casos. Primeiro, a agregação de coluna impede que cada sub-campo da tabela  
    175 seja definido como `obrigatório` (`required`). Dependendo de quantos campos existem, a tabela `Pessoa` 
     175seja definido como `obrigatório` (`required`). Dependendo de quantos campos existirem, a tabela `Pessoa` 
    176176pode conter vários registros com valores vazios. 
    177177 
     
    226226Esta abordagem tem várias vantagens em relação as estratégias anteriores. A primeira 
    227227é que todas as tabelas são isoladas e permanecem independentes umas das outras.  
    228 Além disso, não há mais campos em branco e a coluna extra `tipo` não é incluída.  
     228Além disso, não há mais campos em branco e a coluna extra `type` não é incluída.  
    229229O resultado é que cada tabela será mais leve e isolada uma das outras. 
    230230 
     
    237237duplicação de campos compartilhados (embora a duplicação geralmente seja a chave para o desempenho)  
    238238e o fato que a super tabela gerada estará sempre vazia. Na verdade, o Doctrine gerou  
    239 uma `Pessoa`, apesar que a tabela não será preenchida ou referenciada por qualquer consulta.  
     239uma tabela `Pessoa`, apesar de que ela não será preenchida ou referenciada por qualquer consulta.  
    240240Nenhuma consulta será realizada na tabela já que tudo é armazenado nas sub-tabelas. 
    241241 
     
    260260O restante deste capítulo irá explicar como usar herança de tabelas do Doctrine 
    261261e como aproveitá-la em várias situações, inclusive em modelos, formulários, filtros e  
    262 geradores de interface administrativa. Exemplos de estudo de caso vão nos ajudar a  
     262geradores de interface administrativa. Exemplos de estudo de caso reais vão nos ajudar a  
    263263entender melhor como a herança funciona com o symfony para que você utilize facilmente  
    264264em suas necessidades. 
    265265 
    266 ### Introdução ao Estudo de Caso Real 
     266### Introdução aos Estudos de Caso Reais 
    267267 
    268268Ao longo deste capítulo, vários estudos de caso do mundo real serão apresentados 
    269269expondo as muitas vantagens da abordagem da herança de tabelas do Doctrine em diversos  
    270 níveis: em `modelos`, `formulários`, `filtros` e o `geradores de interface administrativa`. 
    271  
    272 O primeiro exemplo vem de uma aplicação desenvolvida pelo Sensio 
    273 para uma empresa francesa bem conhecida. Ele mostra como a herança de uma tabela com o Doctrine é uma  
     270níveis: em `modelos`, `formulários`, `filtros` e no `gerador de interface administrativa`. 
     271 
     272O primeiro exemplo vem de uma aplicação desenvolvida pela Sensio 
     273para uma empresa francesa bem conhecida. Ele mostra como a herança de tabela com o Doctrine é uma  
    274274boa solução para gerenciar dezenas de conjuntos de dados referenciais idênticos, a fim de 
    275275compartilhar métodos e propriedades e evitar a reescrita de código. 
     
    291291#### O Problema #### 
    292292 
    293 Muitas das aplicações web exigem dados "referenciais" para funcionar. Um  
     293Muitas aplicações web exigem dados "referenciais" para funcionar. Um  
    294294referencial é geralmente um pequeno conjunto de dados representados por uma tabela simples 
    295295contendo pelo menos dois campos (por exemplo, `id` e `label`). Em alguns casos, porém, 
     
    297297Este foi o caso recentemente na Sensio com uma aplicação de cliente. 
    298298 
    299 O cliente queria administrar um grande conjunto de dados, que levou os principais 
     299O cliente queria administrar um grande conjunto de dados, que envolvia os principais 
    300300formulários e visões da aplicação. Todas estas tabelas referenciais foram construídas 
    301301em torno do mesmo modelo básico: `id`, `label`, `position` e `is_default`. O 
    302 campo `position` ajuda para classificar os registros, graças a uma função ajax *arraste e solte* (*drag and drop*).  
     302campo `position` ajuda a classificar os registros, graças a uma função ajax *arraste e solte* (*drag and drop*).  
    303303O campo `is_default` é um sinalizador que indica se um registro deve ou não estar  
    304 "selecionado" por padrão, quando ele preenche uma elemente HTML de caixa para seleção *dropdown*. 
     304"selecionado" por padrão, quando ele preenche um elemento HTML de caixa para seleção *dropdown*. 
    305305 
    306306#### A Solução #### 
    307307 
    308 Gerenciando mais de que duas tabelas iguais é um dos melhores problemas para resolver com 
     308O gerenciamento de mais do que duas tabelas iguais é um dos melhores problemas para resolver com 
    309309herança de tabelas. No problema acima, a herança das tabelas concreta  
    310310foi selecionada para se adequar às necessidades e compartilhar os métodos de cada objeto em uma 
     
    344344 
    345345Vamos construir o modelo e ver o que acontece. O Doctrine e symfony geraram 
    346 três tabelas SQL e seis classes de modelo no diretório `lib/model/doctrine`: 
     346SQL para três tabelas e seis classes de modelo no diretório `lib/model/doctrine`: 
    347347 
    348348  * `SfReferential`: gerencia os registros `sf_referential`, 
     
    354354 
    355355Explorando a herança gerada vemos que ambas as classes base do 
    356 `sfReferentialContractType` e modelo `sfReferentialProductType` herdaram 
     356`sfReferentialContractType` e o modelo `sfReferentialProductType` herdaram 
    357357da classe `sfReferential`. Assim, todos os métodos protegidos e públicos (incluindo 
    358 propriedades) colocado na classe `sfReferential` serão compartilhado entre as duas 
    359 subclasses e pode ser sobre-escrito, se necessário. 
     358propriedades) inseridos na classe `sfReferential` serão compartilhado entre as duas 
     359subclasses e poderão ser sobrescritos, se necessário. 
    360360 
    361361Isso é exatamente o objetivo esperado. A classe `sfReferential` agora pode conter 
     
    368368      public function promote() 
    369369      { 
    370         //Mover para cima o registo na lista 
     370        //Mover o registo para cima na lista 
    371371      } 
    372372 
    373373      public function demote() 
    374374      { 
    375         // Mover para baixo o registro na lista 
     375        // Mover o registro para baixo na lista 
    376376      } 
    377377 
     
    403403 
    404404Graças à Herança concreta de tabelas do Doctrine, todo o código é compartilhado em 
    405 um mesmo lugar. O código se torna mais fácil de depurar, manter, melhorar e testar seus trechos. 
    406  
    407 Essa é a primeira vantagem real quando se trata de herança de tabelas. Além disso, graças a esta abordagem, os objetos do modelo podem ser usados para centralizar as ações de código como segue abaixo. O `sfBaseReferentialActions` é uma classe de ações especiais herdada por cada classe de ações que gera um modelo referencial. 
     405um mesmo lugar. O código se torna mais fácil de depurar, manter, melhorar e realizar testes unitários. 
     406 
     407Essa é a primeira vantagem real quando se trata de herança de tabelas.  
     408Além disso, graças a esta abordagem, os objetos do modelo podem ser usados para  
     409centralizar as ações de código como no exemplo abaixo.  
     410O `sfBaseReferentialActions` é uma classe de ações especiais herdada por cada  
     411classe de ações que gera um modelo referencial. 
    408412 
    409413    [php] 
     
    431435 
    432436O que aconteceria se o esquema não usasse a herança de tabelas? O código 
    433 precisaria ser repetido em cada subclasse referencial. Esta abordagem não contempla DRY, (*Don't Repeat Yourself*, ou não faça retrabalho), especialmente para uma aplicação com uma dúzia de tabelas referenciais. 
    434  
    435 ### Herança de tabelas na camada de Formulários ### 
    436  
    437 Vamos continuar o guia de vantagens Herança de tabelas do Doctrine. 
     437precisaria ser repetido em cada subclasse referencial. Esta abordagem não  
     438contempla o conceito DRY, (*Don't Repeat Yourself*, ou não faça retrabalho),  
     439especialmente para uma aplicação com uma dúzia de tabelas referenciais. 
     440 
     441### Herança de tabelas na camada dos Formulários ### 
     442 
     443Vamos continuar o nosso *tour* pelas vantagens da Herança de tabelas do Doctrine. 
    438444A seção anterior demonstrou como esse recurso pode ser muito útil para 
    439445compartilhar métodos e propriedades entre vários modelos herdados. Agora vamos dar uma olhada 
    440 na forma como ele se comporta quando se lida com formulários gerados pelo symfony. 
    441  
    442 #### Estudo de caso #### 
    443  
    444 O esquema YAML abaixo descreve um modelo para gerenciar inúmeros documentos. O objetivo é 
    445 armazenar informações genéricas na tabela arquivos e dados específicos nas sub-tabelas 
    446 `Vídeo` e `PDF`. 
     446na forma como ele se comporta quando ao lidar com formulários gerados pelo symfony. 
     447 
     448#### O Modelo do Estudo de Caso #### 
     449 
     450O esquema YAML abaixo descreve um modelo para gerenciar inúmeros documentos.  
     451O objetivo é armazenar informações genéricas na tabela arquivos e dados específicos  
     452nas sub-tabelas `Vídeo` e `PDF`. 
    447453 
    448454    [yml] 
     
    499505          notnull: true 
    500506 
    501 Tanto as tabelas `PDF` e `Video` compartilham a mesma tabela `Arquiv`, que contém informações globais sobre os inúmeros arquivos. O modelo `Vídeo` encapsula os dados relacionados a objetos de vídeo, tais como `formato` (4/3, 16/9 ...) ou `duração`, considerando que o modelo `PDF` contém o número de `páginas` ou `orientação` do documento. Vamos construir esse modelo e gerar os formulários correspondentes. 
     507Tanto a tabela `PDF` quanto a `Video` compartilham a mesma tabela `Arquivo`, que  
     508contém informações globais sobre os inúmeros arquivos. O modelo `Video` encapsula os dados  
     509relacionados à objetos de vídeo, tais como `formato` (4/3, 16/9 ...) ou `duracao`, enquanto   
     510o modelo `PDF` contém o número de `paginas` ou `orientacao` do documento.  
     511Vamos construir esse modelo e gerar os formulários correspondentes. 
    502512 
    503513    $php symfony doctrine:build-all 
    504514 
    505515A seção seguinte descreve como tirar vantagem da herança de tabelas 
    506 em classes de formulário graças ao novo método ~`setupInheritance()`~, método de configuração da herança. 
    507  
    508 ### Descubrindo o método ~setupInheritance()~ ### 
    509  
    510 Como esperado, o Doctrine gerou seis classes de formulários nos diretórios `lib/form/doctrine` e `form/lib/doctrine/base`: 
     516em classes de formulário graças ao novo método ~`setupInheritance()`~, método  
     517de configuração da herança. 
     518 
     519### Descobrindo o método ~setupInheritance()~ ### 
     520 
     521Como esperado, o Doctrine gerou seis classes de formulários nos diretórios  
     522`lib/form/doctrine` e `form/lib/doctrine/base`: 
    511523 
    512524  * `BaseArquivoForm` 
     
    518530  * `PDFForm` 
    519531 
    520 Vamos abrir as três classes `Base` dos formulário e descobrir algo novo no 
     532Vamos abrir as três classes `Base` dos formulário para descobrir algo novo no 
    521533método ~`setup()`~. Um novo método ~`setupInheritance()`~ foi adicionado pelo 
    522534symfony 1.3. Este método está vazio por padrão. 
    523535 
    524 A coisa mais importante a notar é que a herança esta presente nos formulários 
    525 `BaseVideoForm` e `BasePDFForm` ambos estendendo as classes `ArquivoForm` e `BaseArquivoForm 
    526 . Consequentemente, cada classe herdará de `Arquivo` os métodos compartilhados da base. 
    527  
    528 A listagem a seguir substitui o método `setupInheritance()` e configura 
     536A coisa mais importante a notar é que a herança está presente nos formulários 
     537`BaseVideoForm` e `BasePDFForm` ambos estendendo as classes `ArquivoForm` e `BaseArquivoForm`.  
     538Consequentemente, cada classe herdará da classe `Arquivo` e poderá compartilhar os mesmos métodos base. 
     539 
     540A listagem a seguir sobrescreve o método `setupInheritance()` e configura  
    529541a classe `ArquivoForm` para que ela possa ser usada em qualquer subformulário de modo mais eficaz. 
    530542 
     
    537549        parent:: setupInheritance(); 
    538550 
    539         $this->useFields(array('filename', description')); 
    540  
    541         $this->widgetSchema['filename'] = new sfWidgetFormInputFile(); 
    542         $this->validatorSchema['filename'] = new sfValidatorFile(array( 
     551        $this->useFields(array('nome_do_arquivo', descricao')); 
     552 
     553        $this->widgetSchema['nome_do_arquivo'] = new sfWidgetFormInputFile(); 
     554        $this->validatorSchema['nome_do_arquivo'] = new sfValidatorFile(array( 
    543555          'path' => sfConfig::get('sf_upload_dir') 
    544556        )); 
     
    547559 
    548560O método `setupInheritance()`, que é chamado pelas subclasses `VideoForm` e 
    549 `PDFForm`, remove todos os campos, exceto `filename` e `description`. 
    550 O widget do campo `filename` foi transformado em um widget de arquivo e seu 
     561`PDFForm`, remove todos os campos, exceto `nome_do_arquivo` e `descricao`. 
     562O widget do campo `nome_do_arquivo` foi transformado em um widget de arquivo e seu 
    551563validador correspondente foi alterado para um validador ~`sfValidatorFile`~. 
    552564Desta forma, o usuário será capaz de carregar um arquivo e salvá-lo no servidor. 
    553565 
    554 ![Formulários herdados com o método setupInheritance() personalizados](http://www.symfony-project.org/images/more-with-symfony/05_table_inheritance_forms.png "Formulários com herança de tabelas do Doctrine") 
    555  
    556 #### Configurando o tipo e tamanho arquivo atual 
    557  
    558 Todos os formulários estão prontos e personalizados. Há mais uma coisa para configurar, no entanto, antes de poder utilizá-los. Como os campos `mime_type` e `tamanho` foram removidos do objeto `ArquivoForm`, eles devem ser definidos automáticamente. O melhor lugar para fazer isso é em um novo método `generateFilenameFilename()` na classe `Arquivo`. 
     566![Formulários herdados com o método setupInheritance() personalizado](http://www.symfony-project.org/images/more-with-symfony/05_table_inheritance_forms.png "Formulários com herança de tabelas do Doctrine") 
     567 
     568#### Configurando o tipo e tamanho do arquivo atual 
     569 
     570Todos os formulários estão prontos e personalizados. No entanto, há mais uma coisa para  
     571configurar antes de poder utilizá-los. Como os campos `mime_type` e `tamanho` foram removidos  
     572do objeto `ArquivoForm`, eles devem ser definidos automaticamente. O melhor lugar  
     573para fazer isso é em um novo método `generateFilenameFilename()` na classe `Arquivo`. 
    559574 
    560575    [php] 
     
    563578    { 
    564579      / ** 
    565        * Gera um nome para o arquivo objeto atual. 
     580       * Gera um nome para o arquivo do objeto atual. 
    566581       * 
    567582       * @param sfValidatedFile $file 
     
    578593 
    579594Este novo método tem como objetivo gerar um nome personalizado para o arquivo para armazena-lo no 
    580 sistema de arquivos. Embora o método `generateFilenameFilename()` retorne por padrão 
    581 nome do arquivo do auto-generated, que também define o `mime_type` e `tamanho` do objeto, graças ao objeto ~`sfValidatedFile`~ passado como primeiro argumento. 
    582  
    583 No symfony 1.3 suporta totalmente a herança de tabelas do Doctrine, formulários são 
     595sistema de arquivos. Embora o método `generateFilenameFilename()` retorne, por padrão,  
     596o nome do arquivo gerado automaticamente, que também define o `mime_type` e `tamanho` do objeto,  
     597graças ao objeto ~`sfValidatedFile`~ passado como o primeiro argumento. 
     598 
     599Devido ao symfony 1.3 ter suporte total à herança de tabelas do Doctrine, os formulários são  
    584600agora capazes de salvar um objeto e seus valores herdados. O suporte à herança nativa 
    585 permite formulários poderosos e funcionais, com poucos 
    586 linhas de código. 
    587  
    588 O exemplo acima pode ser amplamente e facilmente melhorado graças à herança da classe 
    589 . Por exemplo, tanto as classes `Videoform` e `PDFForm` podem 
    590 substituir o validador `nome_do_arquivo` para um validador personalizado mais específicos, tais 
    591 como `sfValidatorVideo` ou `sfValidatorPDF`. 
     601permite formulários poderosos e funcionais, com poucas linhas de código. 
     602 
     603O exemplo acima pode ser amplamente e facilmente melhorado graças à herança da classe.  
     604Por exemplo, tanto as classes `Videoform` quanto `PDFForm` podem 
     605substituir o validador `nome_do_arquivo` para validadores personalizados mais específicos,  
     606tais como `sfValidatorVideo` ou `sfValidatorPDF`. 
    592607 
    593608### Herança de tabelas na Camada de Filtros ### 
    594609 
    595 Assim como os formulários são também os filtros, eles também herdam os métodos e propriedades dos 
    596 filtros de formulários pai. Consequentemente, os objetos `VideoFormFilter` e `PDFFormFilter` 
     610Devido aos filtros serem como os formulários, eles também herdam os métodos e propriedades dos 
     611filtros dos formulários pai. Consequentemente, os objetos `VideoFormFilter` e `PDFFormFilter` 
    597612herdam de `ArquivoFormFilter` e podem ser personalizados usando 
    598613o método ~`setupInheritance()`~. 
    599614 
    600 Da mesma forma, tanto `VideoFormFilter` e `PDFFormFilter` podem compartilhar os mesmos 
     615Da mesma forma, tanto o `VideoFormFilter` quanto o `PDFFormFilter` podem compartilhar os mesmos 
    601616métodos personalizados da classe `ArquivoFormFilter`. 
    602617 
    603 ### Herança de tabelas na Camada Gerador Admin ### 
     618### Herança de tabelas na Camada do Gerador de Interface Administrativa ### 
    604619 
    605620Agora vamos descobrir como tirar proveito da Herança de tabelas do Doctrine 
    606 bem como das novas funcionalidades do Gerador Admin: a definição das __actions base classe__ 
    607 . O Gerador de administração é uma das características que mais cresceu 
     621bem como das novas funcionalidades do Gerador de Interface Administrativa: a definição __actions base class__ .  
     622O Gerador de Interface Administrativa é uma das características que mais cresceu  
    608623desde a versão symfony 1.0. 
    609624 
    610 Em novembro de 2008, symfony introduziu o novo sistema gerador de administração junto com 
    611 versão 1.2. Esta ferramenta vem com um monte de funcionalidade extras, como operações CRUD básicos, a lista de filtragem e paginação, exclusão em lote e assim por diante... O Gerador de Admin é uma ferramenta poderosa, que facilita e acelera a geração de back-end e personalização para qualquer desenvolvedor. 
     625Em novembro de 2008, o symfony introduziu o novo sistema do Gerador de Interface Administrativa junto com a  
     626versão 1.2. Esta ferramenta vem com várias funcionalidades extras, como operações CRUD básicas, a lista de filtragem e paginação, exclusão em lote e assim por diante...  
     627O Gerador de Interface Administrativa é uma ferramenta poderosa, que facilita e acelera  
     628a geração de *backend* e a personalização para qualquer desenvolvedor. 
    612629 
    613630#### Exemplo prático de Introdução 
    614631 
    615 O objetivo da última parte deste capítulo é ilustrar como tirar proveito da herança de tabelas do Doctrine juntamente com o Gerador de Admin. Para conseguir isso, uma simples área de infra-estrutura irá ser construída para gerir duas tabelas, que contêm dados que podem ser classificadas/priorizados. 
    616  
    617 Como o mantra symfony é para não reinventar a roda toda vez que, o modelo do Doctrine 
     632O objetivo da última parte deste capítulo é ilustrar como tirar proveito da herança  
     633de tabelas do Doctrine juntamente com o Gerador de Interface Administrativa. Para  
     634conseguir isso, uma simples área de *backend* será construída para gerenciar   
     635duas tabelas, que contêm dados que podem ser ordenados/priorizados. 
     636 
     637Como o mantra symfony é para não reinventar a roda toda vez, o modelo do Doctrine 
    618638usará o [csDoctrineActAsSortablePlugin](http://www.symfony-project.org/plugins/csDoctrineActAsSortablePlugin "Página do plugin csDoctrineActAsSortablePlugin") 
    619 para fornecer todas as API necessária para classificar os objetos entre si. O 
    620 ~`csDoctrineActAsSortablePlugin`~ plugin é desenvolvido e mantido pela 
     639para fornecer todas a API necessária para classificar os objetos entre si. O 
     640plugin ~`csDoctrineActAsSortablePlugin`~ é desenvolvido e mantido pela 
    621641CentreSource, uma das empresas mais ativas no ecossistema symfony. 
    622642 
    623643O modelo de dados é bastante simples. Há três classes de modelo, `sfItem`, 
    624 `sfTodoItem` e `sfShoppingItem, que ajudam a gerenciar uma lista de tarefas e uma 
    625 lista de compras. Cada item em ambas as listas é classificável para permitir que os itens a sejam 
     644`sfTodoItem` e `sfShoppingItem`, que ajudam a gerenciar uma lista de tarefas e uma 
     645lista de compras. Cada item em ambas as listas é classificável para permitir que os itens sejam 
    626646priorizados dentro da lista. 
    627647 
     
    660680          default: 1 
    661681 
    662 O esquema acima descreve o modelo de dados divididos em três classes de modelo. As duas 
    663 classes filhas (`sfTodoItem`, `sfShoppingItem`), usam comportamentos `Sortable` e 
     682O esquema acima descreve o modelo de dados dividido em três classes de modelo. As duas 
     683classes filhas (`sfTodoItem`, `sfShoppingItem`), usam comportamentos `Sortable` e  
    664684`Timestampable`. O comportamento `Sortable` é fornecido pelo 
    665685plugin `csDoctrineActAsSortablePlugin` e adiciona uma coluna `position` do tipo inteiro para 
    666 cada tabela. Ambas as classes herdam da classe base `sfItem`. Esta classe contém colunas 
     686cada tabela. Ambas as classes herdam da classe base `sfItem`. Esta classe contém as colunas 
    667687`id` e `name`. 
    668688 
    669689Vamos adicionar alguns dados para testarmos o funcionamento 
    670 do back-end. Os dados fixos, como de costume, localizado no 
     690do backend. Os dados *fixtures*, como de costume, estão localizados no 
    671691arquivo `data/fixtures.yml` do projeto symfony. 
    672692 
     
    678698        assigned_to: "Fabien Potencier" 
    679699      sfTodoItem_2: 
    680         name: "Release Doctrine 2,0" 
     700        name: "Release Doctrine 2.0" 
    681701        priority: "minor" 
    682         assigned_to: "Salário Jonathan
     702        assigned_to: "Jonathan Wage
    683703      sfTodoItem_3: 
    684704        name: "Release symfony 1.4" 
    685         prioridade: "major" 
     705        priority: "major" 
    686706        assigned_to: "Kris Wallsmith" 
    687707      sfTodoItem_4: 
    688708        name: "Documento Lime Core 2 API" 
    689         prioridade: "médium" 
     709        priority: "medium" 
    690710        assigned_to: "Bernard Schussek" 
    691711 
    692712    sfShoppingItem: 
    693713      sfShoppingItem_1: 
    694         name: "MacBook Apple Pro de 15,4 polegadas" 
     714        name: "MacBook Apple Pro de 15.4 polegadas" 
    695715        quantity: 3 
    696716      sfShoppingItem_2: 
     
    704724        quantity: 1 
    705725 
    706 Uma vez que o plugin `csDoctrineActAsSortablePlugin` é instalado e os dados 
     726Uma vez que o plugin `csDoctrineActAsSortablePlugin` está instalado e os dados 
    707727do modelo estão prontos, o novo plugin precisa ser ativado na classe ~`ProjectConfiguration`~ 
    708 localizado em `config/ProjectConfiguration.class.php`: 
     728localizada em `config/ProjectConfiguration.class.php`: 
    709729 
    710730    [php] 
     
    720740    } 
    721741 
    722 Em seguida, o banco de dados, o modelo, as formas e os filtros podem ser gerados e 
    723 os dados fixos carregados no banco de dados para alimentar as tabelas recém-criadas. 
     742Em seguida, o banco de dados, o modelo, os formulários e os filtros podem ser gerados e 
     743os dados *fixtures* carregados no banco de dados para alimentar as tabelas recém-criadas. 
    724744Isso pode ser feito de uma só vez, graças a tarefa ~`doctrine:build`~ 
    725745 
    726746    $ php symfony doctrine:build --all --no-confirmation 
    727747 
    728 O cache symfony deve ser limpo e o plugin publicado em `web` para concluir o processo: 
     748O cache symfony deve ser limpo e os *assets* do plugin linkados no diretório `web` para concluir o processo: 
    729749 
    730750    $ php symfony cache: clear 
    731751    $ php symfony plugin:publish-assets 
    732752 
    733 A seção seguinte explica como construir os módulos de back-end com a 
    734 ferramenta Admin Gerador e como tirar proveito do novo recurso de ações da classe base. 
     753A seção seguinte explica como construir os módulos do backend com a 
     754ferramenta Gerador de Interface Administrativa e como tirar proveito do novo recurso de ações da classe base. 
    735755 
    736756#### Configurando o Backend 
     
    738758Esta seção descreve os passos necessários para instalar a nova aplicação backend 
    739759contendo dois módulos gerados para gerenciar tanto as listas do shopping quanto do todo. 
    740 Por conseguinte, a primeira coisa a fazer é gerar uma `aplicação` de backend 
     760Por conseguinte, a primeira coisa a fazer é gerar uma aplicação `backend`  
    741761para conter os módulos: 
    742762 
    743     $ php symfony generate: app backend 
    744  
    745 O Gerador de Admin é uma poderosa ferramenta, mesmo antes do symfony 1.3, o 
    746 programador foi forçado a duplicar código comum entre os módulos gerados. Agora, porém, o comando ~`doctrine:generate-admin`~ \introduz um novo ~`-- actions-base-classe`~ opção que permite ao desenvolvedor definir o módulo de classe base das ações. 
     763    $ php symfony generate:app backend 
     764 
     765O Gerador de Interface Administrativa é uma poderosa ferramenta, antes do symfony 1.3, o  
     766desenvolvedor era forçado a duplicar código comum entre os módulos gerados.  
     767Agora, porém, a tarefa ~`doctrine:generate-admin`~ introduz uma nova opção  
     768~`--actions-base-class`~  que permite ao desenvolvedor definir a classe base das ações para o módulo. 
    747769 
    748770Como os dois módulos são ligeramente semelhantes, eles certamente vão precisar compartilhar alguns 
     
    766788    $ php symfony doctrine:generate-admin --module=todo --actions-base-class=sfSortableModuleActions backend sfTodoItem 
    767789 
    768 O Gerador de administração gera módulos em duas listas separadas. O 
     790O Gerador de Interface Administrativa gera módulos em duas listas separadas. O 
    769791primeiro diretório é, naturalmente, `apps/backend/modules`. A maioria dos 
    770 arquivos gerados pelo módulo, no entanto, estão localizados no diretório `cache/backend/dev/modules` 
    771 . Arquivos localizados neste local são regenerados cada vez que o cache 
    772 seja limpo ou quando as mudanças de configuração do módulo. 
     792arquivos gerados pelo módulo, no entanto, estão localizados no diretório `cache/backend/dev/modules`.  
     793Arquivos localizados neste local são regenerados toda vez que o cache 
     794for limpo ou quando forem modificadas as configurações do módulo. 
    773795 
    774796>**Note** 
    775797>Percorrer os arquivos em cache é uma ótima maneira de entender como symfony e o 
    776 >Admin Gerador trabalham juntos internamente. Por conseguinte, a 
    777 >nova subclasse `sfSortableModuleActions` pode ser encontrados em 
     798>Gerador de Interface Administrativa trabalham juntos internamente. Consequentemente, as 
     799>novas subclasses do `sfSortableModuleActions` podem ser encontradas em 
    778800>`cache/backend/dev/modules/autoShopping/actions/actions.class.php` e 
    779 >`cache/backend/dev/modules/autoTodo/actions/actions.class.php. Por padrão, 
    780 >symfony iria gerar essas classes para herdar diretamente de ~`sfActions`~. 
    781  
    782 ![Backend padrão todo list](http://www.symfony-project.org/images/more-with-symfony/06_table_inheritance_backoffice_todo_1.png "Lista backend todo padrão") 
    783  
    784 ![Backend padrão lista de compras](http://www.symfony-project.org/images/more-with-symfony/07_table_inheritance_backoffice_shopping_1.png "Lista Shopping backend padrão") 
    785  
    786 Os dois módulos de back-end estão prontos para serem utilizados e personalizados. Não é a meta 
    787 deste capítulo, no entanto, para explorar a configuração dos módulos auto-gerados. Existe documentação relevante sobre esse assunto, inclusive no 
    788 [Symfony - Livro de referência](http://www.symfony-project.org/reference/1_3/en/06-Admin-Generator). 
    789  
    790 #### Alterando posição de um item 
    791  
    792 A seção anterior descreve como configurar dois módulos de back-end totalmente funcionais, tanto que herdam ações da mesma classe. A próxima meta é criar uma ação compartilhada, que permite ao desenvolvedor classificar os objetos a partir de uma lista entre si. Isto é bastante fácil como o plug-in instalado que fornece uma API completa para lidar com o recurso dos objetos. 
    793  
    794 O primeiro passo é a criação de duas novas rotas capaz de mover um registo de 
    795 ou para baixo na lista. Como o *Admin Generator* usa a rota ~`sfDoctrineRouteCollection`~, novas rotas podem ser facilmente declaradas à coleção através do `config/generator.yml` de ambos os módulos: 
     801>`cache/backend/dev/modules/autoTodo/actions/actions.class.php. Por padrão, o 
     802>symfony gera essas classes para herdar diretamente de ~`sfActions`~. 
     803 
     804![Backend padrão do lista todo](http://www.symfony-project.org/images/more-with-symfony/06_table_inheritance_backoffice_todo_1.png "Lista backend todo padrão") 
     805 
     806![Backend padrão da lista de compras](http://www.symfony-project.org/images/more-with-symfony/07_table_inheritance_backoffice_shopping_1.png "Lista Shopping backend padrão") 
     807 
     808Os dois módulos de backend estão prontos para serem utilizados e personalizados. Não é a meta 
     809deste capítulo, no entanto, explorar a configuração dos módulos auto-gerados.  
     810Existe documentação relevante sobre esse assunto, inclusive no 
     811[Livro de referência do symfony](http://www.symfony-project.org/reference/1_3/en/06-Admin-Generator). 
     812 
     813#### Alterando a posição de um item 
     814 
     815A seção anterior descreve como configurar dois módulos backend totalmente funcionais,  
     816tanto que herdam ações da mesma classe. A próxima meta é criar uma ação compartilhada,  
     817que permite ao desenvolvedor classificar os objetos a partir de uma lista entre si.  
     818Como o plugin instalado fornece uma API completa para reordenar  
     819os objetos, isto é muito fácil de fazer. 
     820 
     821O primeiro passo é a criação de duas novas rotas capazes de mover um registo para cima  
     822ou para baixo na lista. Como o *Gerador de Interface Administrativa* usa a rota  
     823~`sfDoctrineRouteCollection`~, novas rotas podem ser facilmente declaradas à  
     824coleção através do `config/generator.yml` de ambos os módulos: 
    796825 
    797826    [yml] 
    798827    # apps/backend/modules/shopping/config/generator.yml 
    799     gerador: 
     828    generator: 
    800829      class: sfDoctrineGenerator 
    801830      param: 
    802         model_class: sfShoppingItem 
    803         tema: admin 
     831        model_class:           sfShoppingItem 
     832        theme:                admin 
    804833        non_verbose_templates: true 
    805         with_show: false 
    806         singular:
    807         plural:
    808         route_prefix: sf_shopping_item 
    809         with_Doctrine_route: true 
    810         actions_base_class: sfSortableModuleActions 
     834        with_show:             false 
     835        singular:              
     836        plural:                
     837        route_prefix:          sf_shopping_item 
     838        with_doctrine_route:  true 
     839        actions_base_class:    sfSortableModuleActions 
    811840 
    812841        config: 
    813842          actions: ~ 
    814           fields:
     843          fields:
    815844          list: 
    816             max_per_page: 100 
    817             sort: [position, asc] 
    818             display: [position, name, quantity] 
     845            max_per_page:      100 
     846            sort:              [position, asc] 
     847            display:           [position, name, quantity] 
    819848            object_actions: 
    820               moveUp: { label: "move up", action: "moveUp" } 
    821               moveDown: { label: "move down", action: "moveDown" } 
    822               _edit:
    823               _delete:
    824           filter:
    825           form:
    826           edit:
    827           new:
     849              moveUp:          { label: "move up", action: "moveUp" } 
     850              moveDown:        { label: "move down", action: "moveDown" } 
     851              _edit:      
     852              _delete:    
     853          filter:
     854          form:    
     855          edit:    
     856          new:    
    828857 
    829858As mudanças precisam ser repetidas para o módulo `todo`: 
     
    831860    [yml] 
    832861    # apps/backend/modules/todo/config/generator.yml 
    833     gerador: 
     862    generator: 
    834863      class: sfDoctrineGenerator 
    835864      param: 
    836         model_class: sfTodoItem 
    837         tema: admin 
     865        model_class:           sfTodoItem 
     866        theme:                admin 
    838867        non_verbose_templates: true 
    839         with_show: false 
    840         singular:
    841         plural:
    842         route_prefix: sf_todo_item 
    843         with_Doctrine_route: true 
    844         actions_base_class: sfSortableModuleActions 
     868        with_show:             false 
     869        singular:              
     870        plural:                
     871        route_prefix:          sf_todo_item 
     872        with_doctrine_route:  true 
     873        actions_base_class:    sfSortableModuleActions 
    845874 
    846875        config: 
    847876          actions: ~ 
    848           fields:
     877          fields:
    849878          list: 
    850             max_per_page: 100 
    851             sort: [position, asc] 
    852             display: [position, name, priority, assigned_to] 
     879            max_per_page:      100 
     880            sort:              [position, asc] 
     881            display:           [position, name, priority, assigned_to] 
    853882            object_actions: 
    854               moveUp: { label: "move up", action: "moveUp" } 
    855               moveDown: { label: "move down", action: "moveDown" } 
    856               _edit: ~ 
    857               _delete: ~ 
    858           filter: ~ 
    859           form: ~ 
    860           edit: ~ 
    861           new: ~ 
    862  
    863  
    864 Os dois arquivos YAML descrevem as configurações para ambos os módulos `shopping` e `todo` 
    865 . Cada um foi customizado para atender às necessidades do usuário final. Primeiro, a exibição da lista é ordenada pela coluna `position` de forma ascendente. Em seguida, o número máximo de itens por página foi aumentado para 100 para evitar a paginação. 
    866  
    867 Finalmente, o número de colunas exibidas foi reduzido para a `position`, `name`, `priority`, `assigned_to` e `quantity` somente. Além disso, cada módulo tem duas novas ações: `moveUp` e `moveDown`. A apresentação final deverá ser parecido com as imagens a seguir: 
    868  
    869 ![Lista personalizada de todo no backend](http://www.symfony-project.org/images/more-with-symfony/09_table_inheritance_backoffice_todo_2.png "Lista personalizada de todo no backend") 
    870  
    871 ![Lista de compras personalizada do backend](http://www.symfony-project.org/images/more-with-symfony/08_table_inheritance_backoffice_shopping_2.png "Lista de compras personalizada do backend") 
     883              moveUp:          { label: "move up", action: "moveUp" } 
     884              moveDown:        { label: "move down", action: "moveDown" } 
     885              _edit:      ~ 
     886              _delete:    ~ 
     887          filter:  ~ 
     888          form:    ~ 
     889          edit:    ~ 
     890          new:     ~ 
     891 
     892 
     893Os dois arquivos YAML descrevem as configurações para ambos os módulos `shopping` e `todo`.  
     894Cada um foi customizado para atender às necessidades do usuário final. Primeiro, a  
     895exibição da lista é ordenada pela coluna `position` de forma ascendente. Em seguida,  
     896o número máximo de itens por página foi aumentado para 100 para evitar a paginação. 
     897 
     898Finalmente, o número de colunas exibidas foi reduzido para somente `position`, `name`,  
     899`priority`, `assigned_to` e `quantity`. Além disso, cada módulo tem duas novas ações:  
     900`moveUp` e `moveDown`. A apresentação final deverá ser parecida com as imagens a seguir: 
     901 
     902![Backend da lista todo personalizada](http://www.symfony-project.org/images/more-with-symfony/09_table_inheritance_backoffice_todo_2.png "Lista personalizada de todo no backend") 
     903 
     904![Backend da lista de compras personalizada](http://www.symfony-project.org/images/more-with-symfony/08_table_inheritance_backoffice_shopping_2.png "Lista de compras personalizada do backend") 
    872905 
    873906Essas duas novas ações foram declaradas, mas ainda não fazem nada. Cada uma 
    874 deve ser criada na classe de ações compartilhadas, `sfSortableModuleActions`, tal como descrito abaixo. O plugin extra ~`csDoctrineActAsSortablePlugin`~ fornece dois métodos úteis em cada classe de modelo: `promote()` e `demote()`. Cada uma é usada para construir o `moveUp` e `moveDown ações. 
     907deve ser criada na classe de ações compartilhadas, `sfSortableModuleActions`, tal como descrito abaixo.  
     908O plugin extra ~`csDoctrineActAsSortablePlugin`~ fornece dois métodos úteis em cada  
     909classe do modelo: `promote()` e `demote()`. Cada uma é usada para construir as ações `moveUp` e `moveDown`. 
    875910 
    876911    [php] 
     
    878913    class sfSortableModuleActions extends sfActions 
    879914    { 
    880       / ** 
    881        * Move um item para cima na lista
     915      /** 
     916       * Moves an item up in the list
    882917       * 
    883        * @ Param $ sfWebRequest pedido 
    884        * / 
     918       * @param sfWebRequest $request 
     919       */ 
     920      public function executeMoveUp(sfWebRequest $request) 
     921      { 
     922        $this->item = $this->getRoute()->getObject(); 
     923 
     924        $this->item->promote(); 
     925 
     926        $this->redirect($this->getModuleName()); 
     927      } 
     928 
     929      /** 
     930       * Moves an item down in the list. 
     931       * 
     932       * @param sfWebRequest $request 
     933       */ 
    885934      public function executeMoveDown(sfWebRequest $request) 
    886935      { 
    887936        $this->item = $this->getRoute()->getObject(); 
    888937 
    889         $this->item->promote(); 
     938        $this->item->demote(); 
    890939 
    891940        $this->redirect($this->getModuleName()); 
    892941      } 
    893  
    894       / ** 
    895        * Move um item para baixo na lista. 
    896        * 
    897        * @ Param $ sfWebRequest pedido 
    898        * / 
    899       public function executeMoveDown(sfWebRequest $request) 
    900       { 
    901         $this->item = $this->getRoute()->getObject(); 
    902  
    903         $this->item->demote(); 
    904  
    905         $this->redirect($this->getModuleName()); 
    906       } 
    907942    } 
    908943 
    909 Graças a estas duas ações compartilhadas, tanto a lista de tarefas e lista de compras são 
    910 classificáveis. Além disso, eles são fáceis de manter e realizar testes funcionais. 
    911 Sinta-se livre alterar a aparência dos módulos, para reescrever os métodos, substituindo as ações do objeto modelo, removendo os links `mover para cima` e `mover para baixo`. 
    912  
    913 #### Brinde: Melhorando a experiência do usuário 
     944Graças a estas duas ações compartilhadas, tanto a lista todo quando a de compras são 
     945classificáveis. Além disso, elas são fáceis de manter e realizar testes funcionais. 
     946Sinta-se livre para alterar a aparência dos módulos, reescrever os métodos, substituindo as  
     947ações do objeto modelo, removendo os links `move up` e `move down`. 
     948 
     949#### Presente Especial: Melhorando a Experiência do Usuário 
    914950 
    915951Antes de terminar, vamos ilustrar as duas ações para melhorar a experiência do usuário. 
    916952Todos concordam que mover um registro para cima (ou para baixo), clicando em um link não é 
    917953realmente intuitivo para o usuário final. Uma melhor abordagem seria incluir 
    918 comportamentos Ajax JavaScript. Neste caso, todas as linhas de tabela HTML serão arrastáveis 
    919 e isso é possível graças ao plugin `Table Drag and Drop` do jQuery. Uma requisição Ajax será feita sempre que o usuário mover uma linha na tabela HTML. 
    920  
    921 Primeiro baixe e instale o framework jQuery no diretório `web/js` e repita a operação para o plugin `Table Drag and Drop`, cujo código-fonte está hospedado em um repositório [Google Code](http://code.google.com/p/tablednd/). 
    922  
    923 Para funcionar, a exibição da lista de cada módulo devemos incluir um pouco de trecho JavaScript 
    924 e ambas as tabelas precisam do atributo `id`. Como todos os templates parciais do admin generator 
    925 podem ser substituídos, o arquivo `_list.php`, localizado no cache 
     954comportamentos JavaScript Ajax. Neste caso, todas as linhas de tabela HTML serão arrastáveis 
     955e, isso é possível graças ao plugin `Table Drag and Drop` do jQuery. Uma requisição Ajax  
     956será feita sempre que o usuário mover uma linha na tabela HTML. 
     957 
     958Primeiro baixe e instale o framework jQuery no diretório `web/js` e repita a operação  
     959para o plugin `Table Drag and Drop`, cujo código-fonte está hospedado em um repositório do [Google Code](http://code.google.com/p/tablednd/). 
     960 
     961Para funcionar, a view da lista de cada módulo deverá incluir um pequeno código JavaScript 
     962e ambas as tabelas precisam de um atributo `id`. Como todos os templates do Gerador de Interface Administrativa  
     963e partials podem ser sobrescritos, o arquivo `_list.php`, localizado no cache por  
    926964padrão, deve ser copiado para ambos os módulos. 
    927965 
    928 Mas espere, copiando o arquivo `_list.php` sob o diretório `templates/` de cada 
    929 módulo não ficará enxuto. Basta copiar o arquivo do cache `/backend/dev/modules/autoShopping/templates/_list.php` 
     966Mas espere, copiando o arquivo `_list.php` para o diretório `templates/` de cada 
     967módulo você estará quebrando o conceito DRY. Basta copiar o arquivo do cache `/backend/dev/modules/autoShopping/templates/_list.php` 
    930968para o `apps/backend/templates/` e renomeá-lo para `_table.php`. 
    931969Vamos sobrescrever o conteúdo atual com o seguinte código: 
     
    933971    [php] 
    934972    <div class="sf_admin_list"> 
    935       <?php if(!$pager->getNbResults ()):?> 
    936         <p><?php echo __('Sem registros', array(), 'sf_admin')?><p> 
    937       <?php else:?> 
     973      <?php if (!$pager->getNbResults()): ?> 
     974        <p><?php echo __('No result', array(), 'sf_admin') ?></p> 
     975      <?php else: ?> 
    938976        <table cellspacing="0" id="sf_item_table"> 
    939977          <thead> 
     
    943981                $sf_request->getParameter('module').'/list_th_tabular', 
    944982                array('sort' => $sort) 
    945               )?> 
     983              ) ?> 
    946984              <th id="sf_admin_list_th_actions"> 
    947                 <? <?php echo __('Actions', array(), 'sf_admin') ?>
     985                <?php echo __('Actions', array(), 'sf_admin') ?
    948986              </th> 
    949987            </tr> 
     
    952990            <tr> 
    953991              <th colspan="<?php echo $colspan ?>"> 
    954                 <?php if($pager-> haveToPaginate()):?> 
     992                <?php if ($pager->haveToPaginate()): ?> 
    955993                  <?php include_partial( 
    956994                    $sf_request->getParameter('module').'/pagination', 
    957995                    array('pager' => $pager) 
    958                   )?> 
     996                  ) ?> 
    959997                <?php endif; ?> 
    960998                <?php echo format_number_choice( 
    961                   '[0] sem registros|[1] 1 registro|(1,+Inf] %1% registros',  
     999                  '[0] no result|[1] 1 result|(1,+Inf] %1% results',  
    9621000                  array('%1%' => $pager->getNbResults()), 
    9631001                  $pager->getNbResults(), 'sf_admin' 
    964                 )?> 
    965                 <?php if($pager->haveToPaginate()):?> 
    966                   <?php echo __('(página %%page%%/%%nb_pages%%)', array( 
    967                     '%%page%%' => $pager-> getPage(),  
     1002                ) ?> 
     1003                <?php if ($pager->haveToPaginate()): ?> 
     1004                  <?php echo __('(page %%page%%/%%nb_pages%%)', array( 
     1005                    '%%page%%' => $pager->getPage(),  
    9681006                    '%%nb_pages%%' => $pager->getLastPage()),  
    9691007                    'sf_admin' 
    970                   )?> 
     1008                  ) ?> 
    9711009                <?php endif; ?> 
    9721010              </th> 
     
    9741012          </tfoot> 
    9751013          <tbody> 
    976           <?php foreach($pager->getResults() as $i => $item): ?> 
    977             <?php $odd = fmod(++$i, 2)? 'odd': 'even' ?> 
    978             <tr class="sf_admin_row <?php echo $odd ?> "> 
     1014          <?php foreach ($pager->getResults() as $i => $item): ?> 
     1015            <?php $odd = fmod(++$i, 2) ? 'odd' : 'even' ?> 
     1016            <tr class="sf_admin_row <?php echo $odd ?>"> 
    9791017              <?php include_partial( 
    9801018                $sf_request->getParameter('module').'/list_td_batch_actions', 
    9811019                array( 
    982                   'sf_'.$sf_request->getParameter('module').'_item'=>$item, 
     1020                  'sf_'. $sf_request->getParameter('module') .'_item' => $item, 
    9831021                  'helper' => $helper 
    984               ))?> 
     1022              )) ?> 
    9851023              <?php include_partial( 
    9861024                $sf_request->getParameter('module').'/list_td_tabular',  
    9871025                array( 
    9881026                  'sf_'. $sf_request->getParameter('module') .'_item' => $item 
    989               ))?> 
     1027              )) ?> 
    9901028                <?php include_partial( 
    9911029                  $sf_request->getParameter('module').'/list_td_actions', 
     
    9931031                    'sf_'. $sf_request->getParameter('module') .'_item' => $item,  
    9941032                    'helper' => $helper 
    995                 ))?> 
     1033                )) ?> 
    9961034            </tr> 
    9971035          <?php endforeach; ?> 
     
    10081046            if ( 
    10091047              box.type == 'checkbox'  
    1010               & &  
     1048              &&  
    10111049              box.className == 'sf_admin_batch_checkbox' 
    10121050            )  
     
    10281066      'sort' => $sort, 
    10291067      'colspan' => 5 
    1030     ))?> 
     1068    )) ?> 
    10311069     
    1032 -- 
     1070- 
    10331071 
    10341072    // apps/backend/modules/shopping/templates/_list.php 
    10351073    <?php include_partial('global/table', array( 
    1036       'pager' => $ pager, 
     1074      'pager' => $pager, 
    10371075      'helper' => $helper, 
    10381076      'sort' => $sort, 
    10391077      'colspan' => 8 
    1040     ))?> 
     1078    )) ?> 
    10411079 
    10421080Para alterar a posição de uma linha, é necessário implementar uma nova ação em ambos os módulos  
    1043 que processa a requisição Ajax. Como visto antes, o novo método compartilhado 
    1044 `executeMove()` ação será colocado no `actions` do `sfSortableModuleActions` 
    1045 classe: 
     1081que processa a requisição Ajax. Como visto antes, a nova ação compartilhada 
     1082`executeMove()` será colocada na classe das ações `sfSortableModuleActions`: 
    10461083 
    10471084    [php] 
     
    10501087    { 
    10511088      /** 
    1052        * Realiza a requisição Ajax, movendo um item para uma nova posição
     1089       * Performs the Ajax request, moves an item to a new position
    10531090       * 
    10541091       * @param sfWebRequest $request 
     
    10571094      { 
    10581095        $this->forward404Unless($request->isXmlHttpRequest()); 
    1059         $this->forward404Unless($item = Doctrine::getTable($this->configuration->getModel())->find($request->getParameter('id'))); 
     1096        $this->forward404Unless($item = Doctrine_Core::getTable($this->configuration->getModel())->find($request->getParameter('id'))); 
    10601097 
    10611098        $item->moveToPosition((int) $request->getParameter('rank', 1)); 
     
    10651102    } 
    10661103 
    1067 O `executeMove()` exige a ação `getModel()`, método de configuração 
    1068 do objeto. Implemente este novo método, tanto na classe `todoGeneratorConfiguration` quanto na 
    1069 `shoppingGeneratorConfiguration` como se segue
     1104A ação `executeMove()` exige o método `getModel()` no objeto de configuração.  
     1105Implemente este novo método, tanto na classe `todoGeneratorConfiguration` quanto na 
     1106`shoppingGeneratorConfiguration` como a seguir
    10701107 
    10711108    [php] 
     
    10791116    } 
    10801117 
    1081 -- 
     1118- 
    10821119 
    10831120    // apps/backend/modules/todo/lib/todoGeneratorConfiguration.class.php 
     
    10901127    } 
    10911128 
    1092 Há uma última operação pendente. Por agora, as linhas de tabelas não são arrastáveis e nenhuma requisição ajax é executada quando uma linha movida é liberada. Para conseguir isso, ambos os módulos precisam de uma rota específica para acessar suas correspondentes ações `move`. Por conseguinte, o arquivo `apps/backend/config/routing.yml` necessita das duas novas rotas seguintes: 
     1129Há uma última operação pendente. Até agora, as linhas de tabelas não são arrastáveis  
     1130e nenhuma requisição ajax é executada quando uma linha movida é liberada. Para  
     1131conseguir isso, ambos os módulos precisam de uma rota específica para acessar suas  
     1132ações correspondentes `move`. Consequentemente, o arquivo `apps/backend/config/routing.yml`  
     1133necessita das novas rotas seguintes: 
    10931134 
    10941135    [php] 
    10951136    <?php foreach (array('shopping', 'todo') as $module) : ?> 
    10961137 
    1097     <?php echo $module?> _move: 
     1138    <?php echo $module ?>_move: 
    10981139      class: sfRequestRoute 
    10991140      url: /<?php echo $module ?>/move 
    11001141      param: 
    1101         module: "<?php echo $module?>" 
     1142        module: "<?php echo $module ?>" 
    11021143        action: move 
    11031144      requirements: 
    11041145        sf_method: [get] 
    11051146 
    1106     <?php endforeach; ?> 
     1147    <?php endforeach ?> 
    11071148 
    11081149Para evitar a duplicação de código, as duas rotas são geradas dentro de um `foreach` 
    1109 e são baseados no nome do módulo para recuperá-los facilmente na exibição. 
    1110 Finalmente, o `apps/backend/templates/_table.php` deve implementar o trecho JavaScript 
    1111 , a fim de inicializar o comportamento arraste e solte e as correspondentes 
    1112 requisições ajax
     1150e serão baseadas no nome do módulo para recuperá-las facilmente na exibição. 
     1151Finalmente, o `apps/backend/templates/_table.php` deve implementar o código JavaScript,  
     1152a fim de inicializar o comportamento arraste e solte (*drag and drop*) e as requisições  
     1153ajax correspondentes
    11131154 
    11141155    [php] 
     
    11191160            var rows = table.tBodies[0].rows; 
    11201161 
    1121             // Recupera o id da linha do item movido 
     1162            // Get the moved item's id 
    11221163            var movedId = $(row).find('td input:checkbox').val(); 
    11231164 
    1124             // Calcula a posição da nova linha de 
     1165            // Calculate the new row's position 
    11251166            var pos = 1; 
    11261167            for (var i = 0; i<rows.length; i++) { 
    11271168              var cells = rows[i].childNodes; 
    1128               // Processa a requisição Ajax para a nova posição 
     1169              // Perform the ajax request for the new position 
    11291170              if (movedId == $(cells[1]).find('input:checkbox').val()) { 
    11301171                $.ajax({ 
    11311172                  url:"<?php echo url_for('@'. $sf_request->getParameter('module').'_move') ?>?id="+ movedId +"&rank="+ pos, 
    1132                   type: "GET" 
     1173                  type:"GET" 
    11331174                }); 
    11341175                break; 
     
    11411182    </script> 
    11421183 
    1143 A tabela HTML está agora totalmente funcional. As linhas são "arraste e solte" (*draggable and droppable*) e 
    1144 a nova posição de uma linha é automaticamente salvo graças a uma requisição AJAX. Com apenas alguns poucos pedaços de código, facilidade de utilização a infra-estrutura foi melhorada para oferecer ao usuário final uma experiência melhor. O Gerador de Administração é suficientemente flexível para ser estendido e personalizado e funciona perfeitamente com a herança Doctrine da tabela. 
    1145  
    1146 Sinta-se livre para melhorar a dois módulos, removendo as duas ações obsoletas `moveUp` e 
     1184A tabela HTML está agora totalmente funcional. As linhas são "arraste e solte" (*draggable e droppable*) e 
     1185a nova posição de uma linha é automaticamente salva graças a uma requisição AJAX.  
     1186Com apenas poucos pedaços de código, a usabilidade do backend foi significativamente melhorada  
     1187para oferecer ao usuário final uma experiência melhor. O Gerador de Interface Administrativa 
     1188é suficientemente flexível para ser estendido e personalizado e funciona perfeitamente  
     1189com a herança de tabela do Doctrine. 
     1190 
     1191Sinta-se livre para melhorar os dois módulos, removendo as duas ações obsoletas `moveUp` e 
    11471192`moveDown` e acrescentando outras customizações que se ajustem às suas necessidades. 
    11481193 
     
    11501195-------------- 
    11511196 
    1152 Este capítulo descreveu como Herança de tabelas do Doctrine é um recurso poderoso, 
    1153 que ajuda o desenvolvedor a codificar mais rápido e melhor além de organizar o código. Essa 
    1154 funcionalidade Doctrine é totalmente integrada em diversos níveis no symfony. 
    1155 Os desenvolvedores são encorajados a tirar partido dela para aumentar a eficiência e 
     1197Este capítulo descreveu como a Herança de Tabelas do Doctrine é um recurso poderoso, 
     1198que ajuda o desenvolvedor a codificar mais rápido e melhor além de organizar o código.  
     1199Essa funcionalidade do Doctrine é totalmente integrada em diversos níveis no symfony. 
     1200Os desenvolvedores são encorajados a aproveitá-la para aumentar a eficiência e 
    11561201promover a organização de código.