Changeset 28395
- Timestamp:
- 03/06/10 00:29:04 (3 years ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
doc/branches/1.4/more-with-symfony/pt/09-Doctrine-Form-Inheritance.markdown
r28262 r28395 5 5 6 6 No symfony 1.3, o ~Doctrine~ tornou-se, oficialmente, a biblioteca ORM padrão 7 enquanto o desenvolvimento utilizando o Propel teve quedanos últimos meses.8 O suporte e melhorias ao projeto ~Propel~ ainda continua graças aos esforços dos membros da comunidade symfony.7 enquanto houve queda com o desenvolvimento utilizando o Propel nos últimos meses. 8 O suporte e melhorias ao projeto ~Propel~ ainda continuam graças aos esforços dos membros da comunidade symfony. 9 9 10 10 O projeto Doctrine 1.2 tornou-se a nova biblioteca ORM padrão symfony por … … 138 138 ~`column_aggregation`~ e dois novos atributos foram adicionados. O primeiro 139 139 atributo, `keyField`, especifica a coluna que será criada para armazenar o 140 tipo de informação para cada registro. O `keyField` é uma seqüência dacoluna140 tipo de informação para cada registro. O `keyField` é uma coluna string coluna 141 141 chamada `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 pertence màs classes142 O segundo atributo define o valor do tipo para cada registro que pertence às classes 143 143 `Professor` ou `Aluno`. 144 144 … … 146 146 147 147 A 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 `t ipo`.148 cria uma única tabela (`Pessoa`), contendo todos os campos definidos mais o campo `type`. 149 149 Por conseguinte, não há necessidade de criar várias tabelas e fazer joins com uma 150 150 consulta SQL. Abaixo estão alguns exemplos de como realizar consultas nas tabelas … … 169 169 Ao recuperar os dados de uma subclasse (`Professor`, `Aluno`), o 170 170 Doctrine anexará automaticamente a cláusula SQL `WHERE` para a consulta na 171 coluna `t ipo` com o valor correspondente.171 coluna `type` com o valor correspondente. 172 172 173 173 No entanto, existem algumas desvantagens de usar a estratégia de agregação de coluna em 174 174 determinados 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 exist em, a tabela `Pessoa`175 seja definido como `obrigatório` (`required`). Dependendo de quantos campos existirem, a tabela `Pessoa` 176 176 pode conter vários registros com valores vazios. 177 177 … … 226 226 Esta abordagem tem várias vantagens em relação as estratégias anteriores. A primeira 227 227 é 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 `t ipo` não é incluída.228 Além disso, não há mais campos em branco e a coluna extra `type` não é incluída. 229 229 O resultado é que cada tabela será mais leve e isolada uma das outras. 230 230 … … 237 237 duplicação de campos compartilhados (embora a duplicação geralmente seja a chave para o desempenho) 238 238 e 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.239 uma tabela `Pessoa`, apesar de que ela não será preenchida ou referenciada por qualquer consulta. 240 240 Nenhuma consulta será realizada na tabela já que tudo é armazenado nas sub-tabelas. 241 241 … … 260 260 O restante deste capítulo irá explicar como usar herança de tabelas do Doctrine 261 261 e 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 a262 geradores de interface administrativa. Exemplos de estudo de caso reais vão nos ajudar a 263 263 entender melhor como a herança funciona com o symfony para que você utilize facilmente 264 264 em suas necessidades. 265 265 266 ### Introdução ao Estudo de Caso Real266 ### Introdução aos Estudos de Caso Reais 267 267 268 268 Ao longo deste capítulo, vários estudos de caso do mundo real serão apresentados 269 269 expondo as muitas vantagens da abordagem da herança de tabelas do Doctrine em diversos 270 níveis: em `modelos`, `formulários`, `filtros` e o `geradoresde interface administrativa`.271 272 O primeiro exemplo vem de uma aplicação desenvolvida pel oSensio273 para uma empresa francesa bem conhecida. Ele mostra como a herança de umatabela com o Doctrine é uma270 níveis: em `modelos`, `formulários`, `filtros` e no `gerador de interface administrativa`. 271 272 O primeiro exemplo vem de uma aplicação desenvolvida pela Sensio 273 para uma empresa francesa bem conhecida. Ele mostra como a herança de tabela com o Doctrine é uma 274 274 boa solução para gerenciar dezenas de conjuntos de dados referenciais idênticos, a fim de 275 275 compartilhar métodos e propriedades e evitar a reescrita de código. … … 291 291 #### O Problema #### 292 292 293 Muitas dasaplicações web exigem dados "referenciais" para funcionar. Um293 Muitas aplicações web exigem dados "referenciais" para funcionar. Um 294 294 referencial é geralmente um pequeno conjunto de dados representados por uma tabela simples 295 295 contendo pelo menos dois campos (por exemplo, `id` e `label`). Em alguns casos, porém, … … 297 297 Este foi o caso recentemente na Sensio com uma aplicação de cliente. 298 298 299 O cliente queria administrar um grande conjunto de dados, que levouos principais299 O cliente queria administrar um grande conjunto de dados, que envolvia os principais 300 300 formulários e visões da aplicação. Todas estas tabelas referenciais foram construídas 301 301 em 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*).302 campo `position` ajuda a classificar os registros, graças a uma função ajax *arraste e solte* (*drag and drop*). 303 303 O campo `is_default` é um sinalizador que indica se um registro deve ou não estar 304 "selecionado" por padrão, quando ele preenche um a elementeHTML de caixa para seleção *dropdown*.304 "selecionado" por padrão, quando ele preenche um elemento HTML de caixa para seleção *dropdown*. 305 305 306 306 #### A Solução #### 307 307 308 Gerenciando mais deque duas tabelas iguais é um dos melhores problemas para resolver com308 O gerenciamento de mais do que duas tabelas iguais é um dos melhores problemas para resolver com 309 309 herança de tabelas. No problema acima, a herança das tabelas concreta 310 310 foi selecionada para se adequar às necessidades e compartilhar os métodos de cada objeto em uma … … 344 344 345 345 Vamos construir o modelo e ver o que acontece. O Doctrine e symfony geraram 346 três tabelas SQLe seis classes de modelo no diretório `lib/model/doctrine`:346 SQL para três tabelas e seis classes de modelo no diretório `lib/model/doctrine`: 347 347 348 348 * `SfReferential`: gerencia os registros `sf_referential`, … … 354 354 355 355 Explorando a herança gerada vemos que ambas as classes base do 356 `sfReferentialContractType` e modelo `sfReferentialProductType` herdaram356 `sfReferentialContractType` e o modelo `sfReferentialProductType` herdaram 357 357 da classe `sfReferential`. Assim, todos os métodos protegidos e públicos (incluindo 358 propriedades) colocadona classe `sfReferential` serão compartilhado entre as duas359 subclasses e pode ser sobre-escrito, se necessário.358 propriedades) inseridos na classe `sfReferential` serão compartilhado entre as duas 359 subclasses e poderão ser sobrescritos, se necessário. 360 360 361 361 Isso é exatamente o objetivo esperado. A classe `sfReferential` agora pode conter … … 368 368 public function promote() 369 369 { 370 //Mover para cima o registona lista370 //Mover o registo para cima na lista 371 371 } 372 372 373 373 public function demote() 374 374 { 375 // Mover para baixo o registro na lista375 // Mover o registro para baixo na lista 376 376 } 377 377 … … 403 403 404 404 Graç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. 405 um mesmo lugar. O código se torna mais fácil de depurar, manter, melhorar e realizar testes unitários. 406 407 Essa é a primeira vantagem real quando se trata de herança de tabelas. 408 Além disso, graças a esta abordagem, os objetos do modelo podem ser usados para 409 centralizar as ações de código como no exemplo abaixo. 410 O `sfBaseReferentialActions` é uma classe de ações especiais herdada por cada 411 classe de ações que gera um modelo referencial. 408 412 409 413 [php] … … 431 435 432 436 O 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. 437 precisaria ser repetido em cada subclasse referencial. Esta abordagem não 438 contempla o conceito DRY, (*Don't Repeat Yourself*, ou não faça retrabalho), 439 especialmente para uma aplicação com uma dúzia de tabelas referenciais. 440 441 ### Herança de tabelas na camada dos Formulários ### 442 443 Vamos continuar o nosso *tour* pelas vantagens da Herança de tabelas do Doctrine. 438 444 A seção anterior demonstrou como esse recurso pode ser muito útil para 439 445 compartilhar métodos e propriedades entre vários modelos herdados. Agora vamos dar uma olhada 440 na forma como ele se comporta quando se lidacom 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`.446 na forma como ele se comporta quando ao lidar com formulários gerados pelo symfony. 447 448 #### O Modelo do Estudo de Caso #### 449 450 O esquema YAML abaixo descreve um modelo para gerenciar inúmeros documentos. 451 O objetivo é armazenar informações genéricas na tabela arquivos e dados específicos 452 nas sub-tabelas `Vídeo` e `PDF`. 447 453 448 454 [yml] … … 499 505 notnull: true 500 506 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. 507 Tanto a tabela `PDF` quanto a `Video` compartilham a mesma tabela `Arquivo`, que 508 contém informações globais sobre os inúmeros arquivos. O modelo `Video` encapsula os dados 509 relacionados à objetos de vídeo, tais como `formato` (4/3, 16/9 ...) ou `duracao`, enquanto 510 o modelo `PDF` contém o número de `paginas` ou `orientacao` do documento. 511 Vamos construir esse modelo e gerar os formulários correspondentes. 502 512 503 513 $php symfony doctrine:build-all 504 514 505 515 A 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`: 516 em classes de formulário graças ao novo método ~`setupInheritance()`~, método 517 de configuração da herança. 518 519 ### Descobrindo o método ~setupInheritance()~ ### 520 521 Como esperado, o Doctrine gerou seis classes de formulários nos diretórios 522 `lib/form/doctrine` e `form/lib/doctrine/base`: 511 523 512 524 * `BaseArquivoForm` … … 518 530 * `PDFForm` 519 531 520 Vamos abrir as três classes `Base` dos formulário edescobrir algo novo no532 Vamos abrir as três classes `Base` dos formulário para descobrir algo novo no 521 533 método ~`setup()`~. Um novo método ~`setupInheritance()`~ foi adicionado pelo 522 534 symfony 1.3. Este método está vazio por padrão. 523 535 524 A coisa mais importante a notar é que a herança est apresente nos formulários525 `BaseVideoForm` e `BasePDFForm` ambos estendendo as classes `ArquivoForm` e `BaseArquivoForm 526 . Consequentemente, cada classe herdará de `Arquivo` os métodos compartilhados dabase.527 528 A listagem a seguir s ubstitui o método `setupInheritance()` e configura536 A 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`. 538 Consequentemente, cada classe herdará da classe `Arquivo` e poderá compartilhar os mesmos métodos base. 539 540 A listagem a seguir sobrescreve o método `setupInheritance()` e configura 529 541 a classe `ArquivoForm` para que ela possa ser usada em qualquer subformulário de modo mais eficaz. 530 542 … … 537 549 parent:: setupInheritance(); 538 550 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( 543 555 'path' => sfConfig::get('sf_upload_dir') 544 556 )); … … 547 559 548 560 O 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 seu561 `PDFForm`, remove todos os campos, exceto `nome_do_arquivo` e `descricao`. 562 O widget do campo `nome_do_arquivo` foi transformado em um widget de arquivo e seu 551 563 validador correspondente foi alterado para um validador ~`sfValidatorFile`~. 552 564 Desta forma, o usuário será capaz de carregar um arquivo e salvá-lo no servidor. 553 565 554  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  567 568 #### Configurando o tipo e tamanho do arquivo atual 569 570 Todos os formulários estão prontos e personalizados. No entanto, há mais uma coisa para 571 configurar antes de poder utilizá-los. Como os campos `mime_type` e `tamanho` foram removidos 572 do objeto `ArquivoForm`, eles devem ser definidos automaticamente. O melhor lugar 573 para fazer isso é em um novo método `generateFilenameFilename()` na classe `Arquivo`. 559 574 560 575 [php] … … 563 578 { 564 579 / ** 565 * Gera um nome para o arquivo objeto atual.580 * Gera um nome para o arquivo do objeto atual. 566 581 * 567 582 * @param sfValidatedFile $file … … 578 593 579 594 Este 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 595 sistema de arquivos. Embora o método `generateFilenameFilename()` retorne, por padrão, 596 o nome do arquivo gerado automaticamente, que também define o `mime_type` e `tamanho` do objeto, 597 graças ao objeto ~`sfValidatedFile`~ passado como o primeiro argumento. 598 599 Devido ao symfony 1.3 ter suporte total à herança de tabelas do Doctrine, os formulários são 584 600 agora 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`. 601 permite formulários poderosos e funcionais, com poucas linhas de código. 602 603 O exemplo acima pode ser amplamente e facilmente melhorado graças à herança da classe. 604 Por exemplo, tanto as classes `Videoform` quanto `PDFForm` podem 605 substituir o validador `nome_do_arquivo` para validadores personalizados mais específicos, 606 tais como `sfValidatorVideo` ou `sfValidatorPDF`. 592 607 593 608 ### Herança de tabelas na Camada de Filtros ### 594 609 595 Assim como os formulários são também os filtros, eles também herdam os métodos e propriedades dos596 filtros d eformulários pai. Consequentemente, os objetos `VideoFormFilter` e `PDFFormFilter`610 Devido aos filtros serem como os formulários, eles também herdam os métodos e propriedades dos 611 filtros dos formulários pai. Consequentemente, os objetos `VideoFormFilter` e `PDFFormFilter` 597 612 herdam de `ArquivoFormFilter` e podem ser personalizados usando 598 613 o método ~`setupInheritance()`~. 599 614 600 Da mesma forma, tanto `VideoFormFilter` e`PDFFormFilter` podem compartilhar os mesmos615 Da mesma forma, tanto o `VideoFormFilter` quanto o `PDFFormFilter` podem compartilhar os mesmos 601 616 métodos personalizados da classe `ArquivoFormFilter`. 602 617 603 ### Herança de tabelas na Camada Gerador Admin###618 ### Herança de tabelas na Camada do Gerador de Interface Administrativa ### 604 619 605 620 Agora 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 621 bem como das novas funcionalidades do Gerador de Interface Administrativa: a definição __actions base class__ . 622 O Gerador de Interface Administrativa é uma das características que mais cresceu 608 623 desde a versão symfony 1.0. 609 624 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. 625 Em novembro de 2008, o symfony introduziu o novo sistema do Gerador de Interface Administrativa junto com a 626 versã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... 627 O Gerador de Interface Administrativa é uma ferramenta poderosa, que facilita e acelera 628 a geração de *backend* e a personalização para qualquer desenvolvedor. 612 629 613 630 #### Exemplo prático de Introdução 614 631 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 632 O objetivo da última parte deste capítulo é ilustrar como tirar proveito da herança 633 de tabelas do Doctrine juntamente com o Gerador de Interface Administrativa. Para 634 conseguir isso, uma simples área de *backend* será construída para gerenciar 635 duas tabelas, que contêm dados que podem ser ordenados/priorizados. 636 637 Como o mantra symfony é para não reinventar a roda toda vez, o modelo do Doctrine 618 638 usará o [csDoctrineActAsSortablePlugin](http://www.symfony-project.org/plugins/csDoctrineActAsSortablePlugin "Página do plugin csDoctrineActAsSortablePlugin") 619 para fornecer todas a sAPI necessária para classificar os objetos entre si. O620 ~`csDoctrineActAsSortablePlugin`~ pluginé desenvolvido e mantido pela639 para fornecer todas a API necessária para classificar os objetos entre si. O 640 plugin ~`csDoctrineActAsSortablePlugin`~ é desenvolvido e mantido pela 621 641 CentreSource, uma das empresas mais ativas no ecossistema symfony. 622 642 623 643 O 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 uma625 lista de compras. Cada item em ambas as listas é classificável para permitir que os itens asejam644 `sfTodoItem` e `sfShoppingItem`, que ajudam a gerenciar uma lista de tarefas e uma 645 lista de compras. Cada item em ambas as listas é classificável para permitir que os itens sejam 626 646 priorizados dentro da lista. 627 647 … … 660 680 default: 1 661 681 662 O esquema acima descreve o modelo de dados dividido sem três classes de modelo. As duas663 classes filhas (`sfTodoItem`, `sfShoppingItem`), usam comportamentos `Sortable` e 682 O esquema acima descreve o modelo de dados dividido em três classes de modelo. As duas 683 classes filhas (`sfTodoItem`, `sfShoppingItem`), usam comportamentos `Sortable` e 664 684 `Timestampable`. O comportamento `Sortable` é fornecido pelo 665 685 plugin `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 colunas686 cada tabela. Ambas as classes herdam da classe base `sfItem`. Esta classe contém as colunas 667 687 `id` e `name`. 668 688 669 689 Vamos adicionar alguns dados para testarmos o funcionamento 670 do back -end. Os dados fixos, como de costume, localizadono690 do backend. Os dados *fixtures*, como de costume, estão localizados no 671 691 arquivo `data/fixtures.yml` do projeto symfony. 672 692 … … 678 698 assigned_to: "Fabien Potencier" 679 699 sfTodoItem_2: 680 name: "Release Doctrine 2 ,0"700 name: "Release Doctrine 2.0" 681 701 priority: "minor" 682 assigned_to: " Salário Jonathan"702 assigned_to: "Jonathan Wage" 683 703 sfTodoItem_3: 684 704 name: "Release symfony 1.4" 685 priori dade: "major"705 priority: "major" 686 706 assigned_to: "Kris Wallsmith" 687 707 sfTodoItem_4: 688 708 name: "Documento Lime Core 2 API" 689 priori dade: "médium"709 priority: "medium" 690 710 assigned_to: "Bernard Schussek" 691 711 692 712 sfShoppingItem: 693 713 sfShoppingItem_1: 694 name: "MacBook Apple Pro de 15 ,4 polegadas"714 name: "MacBook Apple Pro de 15.4 polegadas" 695 715 quantity: 3 696 716 sfShoppingItem_2: … … 704 724 quantity: 1 705 725 706 Uma vez que o plugin `csDoctrineActAsSortablePlugin` éinstalado e os dados726 Uma vez que o plugin `csDoctrineActAsSortablePlugin` está instalado e os dados 707 727 do modelo estão prontos, o novo plugin precisa ser ativado na classe ~`ProjectConfiguration`~ 708 localizad oem `config/ProjectConfiguration.class.php`:728 localizada em `config/ProjectConfiguration.class.php`: 709 729 710 730 [php] … … 720 740 } 721 741 722 Em seguida, o banco de dados, o modelo, as formas e os filtros podem ser gerados e723 os dados fixoscarregados no banco de dados para alimentar as tabelas recém-criadas.742 Em seguida, o banco de dados, o modelo, os formulários e os filtros podem ser gerados e 743 os dados *fixtures* carregados no banco de dados para alimentar as tabelas recém-criadas. 724 744 Isso pode ser feito de uma só vez, graças a tarefa ~`doctrine:build`~ 725 745 726 746 $ php symfony doctrine:build --all --no-confirmation 727 747 728 O cache symfony deve ser limpo e o plugin publicado em`web` para concluir o processo:748 O cache symfony deve ser limpo e os *assets* do plugin linkados no diretório `web` para concluir o processo: 729 749 730 750 $ php symfony cache: clear 731 751 $ php symfony plugin:publish-assets 732 752 733 A seção seguinte explica como construir os módulos d e back-end com a734 ferramenta Admin Geradore como tirar proveito do novo recurso de ações da classe base.753 A seção seguinte explica como construir os módulos do backend com a 754 ferramenta Gerador de Interface Administrativa e como tirar proveito do novo recurso de ações da classe base. 735 755 736 756 #### Configurando o Backend … … 738 758 Esta seção descreve os passos necessários para instalar a nova aplicação backend 739 759 contendo 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 backend760 Por conseguinte, a primeira coisa a fazer é gerar uma aplicação `backend` 741 761 para conter os módulos: 742 762 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 765 O Gerador de Interface Administrativa é uma poderosa ferramenta, antes do symfony 1.3, o 766 desenvolvedor era forçado a duplicar código comum entre os módulos gerados. 767 Agora, 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. 747 769 748 770 Como os dois módulos são ligeramente semelhantes, eles certamente vão precisar compartilhar alguns … … 766 788 $ php symfony doctrine:generate-admin --module=todo --actions-base-class=sfSortableModuleActions backend sfTodoItem 767 789 768 O Gerador de administraçãogera módulos em duas listas separadas. O790 O Gerador de Interface Administrativa gera módulos em duas listas separadas. O 769 791 primeiro 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 cache772 seja limpo ou quando as mudanças de configuraçãodo módulo.792 arquivos gerados pelo módulo, no entanto, estão localizados no diretório `cache/backend/dev/modules`. 793 Arquivos localizados neste local são regenerados toda vez que o cache 794 for limpo ou quando forem modificadas as configurações do módulo. 773 795 774 796 >**Note** 775 797 >Percorrer os arquivos em cache é uma ótima maneira de entender como symfony e o 776 > Admin Gerador trabalham juntos internamente. Por conseguinte, a777 >nova subclasse `sfSortableModuleActions` pode ser encontrados em798 >Gerador de Interface Administrativa trabalham juntos internamente. Consequentemente, as 799 >novas subclasses do `sfSortableModuleActions` podem ser encontradas em 778 800 >`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  783 784  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  805 806  807 808 Os dois módulos de backend estão prontos para serem utilizados e personalizados. Não é a meta 809 deste capítulo, no entanto, explorar a configuração dos módulos auto-gerados. 810 Existe 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 815 A seção anterior descreve como configurar dois módulos backend totalmente funcionais, 816 tanto que herdam ações da mesma classe. A próxima meta é criar uma ação compartilhada, 817 que permite ao desenvolvedor classificar os objetos a partir de uma lista entre si. 818 Como o plugin instalado fornece uma API completa para reordenar 819 os objetos, isto é muito fácil de fazer. 820 821 O primeiro passo é a criação de duas novas rotas capazes de mover um registo para cima 822 ou para baixo na lista. Como o *Gerador de Interface Administrativa* usa a rota 823 ~`sfDoctrineRouteCollection`~, novas rotas podem ser facilmente declaradas à 824 coleção através do `config/generator.yml` de ambos os módulos: 796 825 797 826 [yml] 798 827 # apps/backend/modules/shopping/config/generator.yml 799 ge rador:828 generator: 800 829 class: sfDoctrineGenerator 801 830 param: 802 model_class: sfShoppingItem803 t ema:admin831 model_class: sfShoppingItem 832 theme: admin 804 833 non_verbose_templates: true 805 with_show: false806 singular: ~807 plural: ~808 route_prefix: sf_shopping_item809 with_ Doctrine_route:true810 actions_base_class: sfSortableModuleActions834 with_show: false 835 singular: ~ 836 plural: ~ 837 route_prefix: sf_shopping_item 838 with_doctrine_route: true 839 actions_base_class: sfSortableModuleActions 811 840 812 841 config: 813 842 actions: ~ 814 fields: ~843 fields: ~ 815 844 list: 816 max_per_page: 100817 sort: [position, asc]818 display: [position, name, quantity]845 max_per_page: 100 846 sort: [position, asc] 847 display: [position, name, quantity] 819 848 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: ~ 828 857 829 858 As mudanças precisam ser repetidas para o módulo `todo`: … … 831 860 [yml] 832 861 # apps/backend/modules/todo/config/generator.yml 833 ge rador:862 generator: 834 863 class: sfDoctrineGenerator 835 864 param: 836 model_class: sfTodoItem837 t ema:admin865 model_class: sfTodoItem 866 theme: admin 838 867 non_verbose_templates: true 839 with_show: false840 singular: ~841 plural: ~842 route_prefix: sf_todo_item843 with_ Doctrine_route:true844 actions_base_class: sfSortableModuleActions868 with_show: false 869 singular: ~ 870 plural: ~ 871 route_prefix: sf_todo_item 872 with_doctrine_route: true 873 actions_base_class: sfSortableModuleActions 845 874 846 875 config: 847 876 actions: ~ 848 fields: ~877 fields: ~ 849 878 list: 850 max_per_page: 100851 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] 853 882 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  870 871  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 893 Os dois arquivos YAML descrevem as configurações para ambos os módulos `shopping` e `todo`. 894 Cada um foi customizado para atender às necessidades do usuário final. Primeiro, a 895 exibição da lista é ordenada pela coluna `position` de forma ascendente. Em seguida, 896 o número máximo de itens por página foi aumentado para 100 para evitar a paginação. 897 898 Finalmente, 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  903 904  872 905 873 906 Essas 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. 907 deve ser criada na classe de ações compartilhadas, `sfSortableModuleActions`, tal como descrito abaixo. 908 O plugin extra ~`csDoctrineActAsSortablePlugin`~ fornece dois métodos úteis em cada 909 classe do modelo: `promote()` e `demote()`. Cada uma é usada para construir as ações `moveUp` e `moveDown`. 875 910 876 911 [php] … … 878 913 class sfSortableModuleActions extends sfActions 879 914 { 880 / **881 * Move um item para cima na lista.915 /** 916 * Moves an item up in the list. 882 917 * 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 */ 885 934 public function executeMoveDown(sfWebRequest $request) 886 935 { 887 936 $this->item = $this->getRoute()->getObject(); 888 937 889 $this->item-> promote();938 $this->item->demote(); 890 939 891 940 $this->redirect($this->getModuleName()); 892 941 } 893 894 / **895 * Move um item para baixo na lista.896 *897 * @ Param $ sfWebRequest pedido898 * /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 }907 942 } 908 943 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 944 Graças a estas duas ações compartilhadas, tanto a lista todo quando a de compras são 945 classificáveis. Além disso, elas são fáceis de manter e realizar testes funcionais. 946 Sinta-se livre para alterar a aparência dos módulos, reescrever os métodos, substituindo as 947 ações do objeto modelo, removendo os links `move up` e `move down`. 948 949 #### Presente Especial: Melhorando a Experiência do Usuário 914 950 915 951 Antes de terminar, vamos ilustrar as duas ações para melhorar a experiência do usuário. 916 952 Todos concordam que mover um registro para cima (ou para baixo), clicando em um link não é 917 953 realmente 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 954 comportamentos JavaScript Ajax. Neste caso, todas as linhas de tabela HTML serão arrastáveis 955 e, isso é possível graças ao plugin `Table Drag and Drop` do jQuery. Uma requisição Ajax 956 será feita sempre que o usuário mover uma linha na tabela HTML. 957 958 Primeiro baixe e instale o framework jQuery no diretório `web/js` e repita a operação 959 para 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 961 Para funcionar, a view da lista de cada módulo deverá incluir um pequeno código JavaScript 962 e ambas as tabelas precisam de um atributo `id`. Como todos os templates do Gerador de Interface Administrativa 963 e partials podem ser sobrescritos, o arquivo `_list.php`, localizado no cache por 926 964 padrão, deve ser copiado para ambos os módulos. 927 965 928 Mas espere, copiando o arquivo `_list.php` sobo diretório `templates/` de cada929 módulo não ficará enxuto. Basta copiar o arquivo do cache `/backend/dev/modules/autoShopping/templates/_list.php`966 Mas espere, copiando o arquivo `_list.php` para o diretório `templates/` de cada 967 módulo você estará quebrando o conceito DRY. Basta copiar o arquivo do cache `/backend/dev/modules/autoShopping/templates/_list.php` 930 968 para o `apps/backend/templates/` e renomeá-lo para `_table.php`. 931 969 Vamos sobrescrever o conteúdo atual com o seguinte código: … … 933 971 [php] 934 972 <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: ?> 938 976 <table cellspacing="0" id="sf_item_table"> 939 977 <thead> … … 943 981 $sf_request->getParameter('module').'/list_th_tabular', 944 982 array('sort' => $sort) 945 ) ?>983 ) ?> 946 984 <th id="sf_admin_list_th_actions"> 947 <? <?php echo __('Actions', array(), 'sf_admin') ?>>985 <?php echo __('Actions', array(), 'sf_admin') ?> 948 986 </th> 949 987 </tr> … … 952 990 <tr> 953 991 <th colspan="<?php echo $colspan ?>"> 954 <?php if ($pager-> haveToPaginate()):?>992 <?php if ($pager->haveToPaginate()): ?> 955 993 <?php include_partial( 956 994 $sf_request->getParameter('module').'/pagination', 957 995 array('pager' => $pager) 958 ) ?>996 ) ?> 959 997 <?php endif; ?> 960 998 <?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', 962 1000 array('%1%' => $pager->getNbResults()), 963 1001 $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(), 968 1006 '%%nb_pages%%' => $pager->getLastPage()), 969 1007 'sf_admin' 970 ) ?>1008 ) ?> 971 1009 <?php endif; ?> 972 1010 </th> … … 974 1012 </tfoot> 975 1013 <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 ?>"> 979 1017 <?php include_partial( 980 1018 $sf_request->getParameter('module').'/list_td_batch_actions', 981 1019 array( 982 'sf_'. $sf_request->getParameter('module').'_item'=>$item,1020 'sf_'. $sf_request->getParameter('module') .'_item' => $item, 983 1021 'helper' => $helper 984 )) ?>1022 )) ?> 985 1023 <?php include_partial( 986 1024 $sf_request->getParameter('module').'/list_td_tabular', 987 1025 array( 988 1026 'sf_'. $sf_request->getParameter('module') .'_item' => $item 989 )) ?>1027 )) ?> 990 1028 <?php include_partial( 991 1029 $sf_request->getParameter('module').'/list_td_actions', … … 993 1031 'sf_'. $sf_request->getParameter('module') .'_item' => $item, 994 1032 'helper' => $helper 995 )) ?>1033 )) ?> 996 1034 </tr> 997 1035 <?php endforeach; ?> … … 1008 1046 if ( 1009 1047 box.type == 'checkbox' 1010 & &1048 && 1011 1049 box.className == 'sf_admin_batch_checkbox' 1012 1050 ) … … 1028 1066 'sort' => $sort, 1029 1067 'colspan' => 5 1030 )) ?>1068 )) ?> 1031 1069 1032 - -1070 - 1033 1071 1034 1072 // apps/backend/modules/shopping/templates/_list.php 1035 1073 <?php include_partial('global/table', array( 1036 'pager' => $ pager,1074 'pager' => $pager, 1037 1075 'helper' => $helper, 1038 1076 'sort' => $sort, 1039 1077 'colspan' => 8 1040 )) ?>1078 )) ?> 1041 1079 1042 1080 Para 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: 1081 que processa a requisição Ajax. Como visto antes, a nova ação compartilhada 1082 `executeMove()` será colocada na classe das ações `sfSortableModuleActions`: 1046 1083 1047 1084 [php] … … 1050 1087 { 1051 1088 /** 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. 1053 1090 * 1054 1091 * @param sfWebRequest $request … … 1057 1094 { 1058 1095 $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'))); 1060 1097 1061 1098 $item->moveToPosition((int) $request->getParameter('rank', 1)); … … 1065 1102 } 1066 1103 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 na1069 `shoppingGeneratorConfiguration` como se segue:1104 A ação `executeMove()` exige o método `getModel()` no objeto de configuração. 1105 Implemente este novo método, tanto na classe `todoGeneratorConfiguration` quanto na 1106 `shoppingGeneratorConfiguration` como a seguir: 1070 1107 1071 1108 [php] … … 1079 1116 } 1080 1117 1081 - -1118 - 1082 1119 1083 1120 // apps/backend/modules/todo/lib/todoGeneratorConfiguration.class.php … … 1090 1127 } 1091 1128 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: 1129 Há uma última operação pendente. Até agora, as linhas de tabelas não são arrastáveis 1130 e nenhuma requisição ajax é executada quando uma linha movida é liberada. Para 1131 conseguir isso, ambos os módulos precisam de uma rota específica para acessar suas 1132 ações correspondentes `move`. Consequentemente, o arquivo `apps/backend/config/routing.yml` 1133 necessita das novas rotas seguintes: 1093 1134 1094 1135 [php] 1095 1136 <?php foreach (array('shopping', 'todo') as $module) : ?> 1096 1137 1097 <?php echo $module ?>_move:1138 <?php echo $module ?>_move: 1098 1139 class: sfRequestRoute 1099 1140 url: /<?php echo $module ?>/move 1100 1141 param: 1101 module: "<?php echo $module ?>"1142 module: "<?php echo $module ?>" 1102 1143 action: move 1103 1144 requirements: 1104 1145 sf_method: [get] 1105 1146 1106 <?php endforeach ;?>1147 <?php endforeach ?> 1107 1148 1108 1149 Para 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 JavaScript1111 , a fim de inicializar o comportamento arraste e solte e as correspondentes 1112 requisições ajax:1150 e serão baseadas no nome do módulo para recuperá-las facilmente na exibição. 1151 Finalmente, o `apps/backend/templates/_table.php` deve implementar o código JavaScript, 1152 a fim de inicializar o comportamento arraste e solte (*drag and drop*) e as requisições 1153 ajax correspondentes: 1113 1154 1114 1155 [php] … … 1119 1160 var rows = table.tBodies[0].rows; 1120 1161 1121 // Recupera o id da linha do item movido1162 // Get the moved item's id 1122 1163 var movedId = $(row).find('td input:checkbox').val(); 1123 1164 1124 // Calcula a posição da nova linha de1165 // Calculate the new row's position 1125 1166 var pos = 1; 1126 1167 for (var i = 0; i<rows.length; i++) { 1127 1168 var cells = rows[i].childNodes; 1128 // P rocessa a requisição Ajax para a nova posição1169 // Perform the ajax request for the new position 1129 1170 if (movedId == $(cells[1]).find('input:checkbox').val()) { 1130 1171 $.ajax({ 1131 1172 url:"<?php echo url_for('@'. $sf_request->getParameter('module').'_move') ?>?id="+ movedId +"&rank="+ pos, 1132 type: "GET"1173 type:"GET" 1133 1174 }); 1134 1175 break; … … 1141 1182 </script> 1142 1183 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 1184 A tabela HTML está agora totalmente funcional. As linhas são "arraste e solte" (*draggable e droppable*) e 1185 a nova posição de uma linha é automaticamente salva graças a uma requisição AJAX. 1186 Com apenas poucos pedaços de código, a usabilidade do backend foi significativamente melhorada 1187 para 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 1189 com a herança de tabela do Doctrine. 1190 1191 Sinta-se livre para melhorar os dois módulos, removendo as duas ações obsoletas `moveUp` e 1147 1192 `moveDown` e acrescentando outras customizações que se ajustem às suas necessidades. 1148 1193 … … 1150 1195 -------------- 1151 1196 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. Essa1154 funcionalidadeDoctrine é totalmente integrada em diversos níveis no symfony.1155 Os desenvolvedores são encorajados a tirar partido dela para aumentar a eficiência e1197 Este capítulo descreveu como a Herança de Tabelas do Doctrine é um recurso poderoso, 1198 que ajuda o desenvolvedor a codificar mais rápido e melhor além de organizar o código. 1199 Essa funcionalidade do Doctrine é totalmente integrada em diversos níveis no symfony. 1200 Os desenvolvedores são encorajados a aproveitá-la para aumentar a eficiência e 1156 1201 promover a organização de código.