Changeset 28263
- Timestamp:
- 02/25/10 03:00:40 (3 years ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
doc/branches/1.4/more-with-symfony/pt/05-Custom-Widgets-and-Validators.markdown
r27173 r28263 1 Widgets e Valida tors Personalizados2 =================================== 3 4 * por Thomas Rabaix*5 6 Este capítulo explica como criar um widgete validadores personalizados para uso1 Widgets e Validadores Personalizados 2 ==================================== 3 4 *por Thomas Rabaix* 5 6 Este capítulo explica como criar *widgets* e validadores personalizados para uso 7 7 no framework de formulário. Ele irá explicar os detalhes internos de `sfWidgetForm` e 8 `sfValidator`, bem como a forma de construir tanto um widgetsimples quanto um complexo.8 `sfValidator`, bem como a forma de construir tanto um *widget* simples quanto um complexo. 9 9 10 10 Por Dentro do Widget e Validator … … 29 29 pode ser facilmente substituído. 30 30 31 * `render()`: saídas de HTML para o widget. O método tem um aprimeiro argumento obrigatório,31 * `render()`: saídas de HTML para o widget. O método tem um primeiro argumento obrigatório, 32 32 o nome do elemento HTML, e um segundo argumento opcional, 33 33 o valor. 34 34 35 > **NOTE**36 > Um objeto `sfWidgetForm` não sabe nada sobre o seu nome ou o seu valor.37 > O componente é responsável apenas pela prestação do widget. O nome e38 > o valor são geridos por um objeto `sfFormFieldSchema`, que é o link39 > entre os dados e os widgets.35 >**NOTE** 36 >Um objeto `sfWidgetForm` não sabe nada sobre o seu nome ou o seu valor. 37 >O componente é responsável apenas pela prestação do widget. O nome e 38 >o valor são geridos por um objeto `sfFormFieldSchema`, que é o link 39 >entre os dados e os widgets. 40 40 41 41 ### Por Dentro do sfValidatorBase … … 45 45 pois ele verifica se o valor é válido, dependendo das opções fornecidas. 46 46 47 Internamente, o método `clean()` realiza várias a cções diferentes:47 Internamente, o método `clean()` realiza várias ações diferentes: 48 48 49 49 * Retira os espaços no início e no final do valor de entrada para valores string (se especificado através da opção `trim`) 50 50 * Verifica se o valor é vazio 51 * Chama o método `do clean()` do validador.51 * Chama o método `doClean()` do validador. 52 52 53 53 O método `doClean()` é o método que implementa a lógica principal de validação. 54 Não é boa prática para sobrescrever o método `clean()`. Em vez disso,55 sempre execute qualquer lógica personalizada atravésdo método `doClean()`.54 Não é uma boa prática sobrescrever o método `clean()`. Em vez disso, 55 customize a lógica do método `doClean()`. 56 56 57 57 O validador também pode ser usado como um componente independente para verificar a integridade de entrada. … … 70 70 } 71 71 72 > **NOTE**73 > Quando um formulário está vinculado aos valores de solicitação, o objeto `sfForm` mantém74 > referências aos valores originais (dirty, "sujos") e os valores validados (clean, "limpos").75 > Os valores originais são usados quando o formulário é redesenhado, enquanto76 > os valores "cleaned"são utilizados pela aplicação (por exemplo, para salvar o objeto).72 >**NOTE** 73 >Quando um formulário está vinculado aos valores de solicitação, o objeto `sfForm` mantém 74 >referências aos valores originais (*dirty*, "sujos") e os valores validados (*clean*, "limpos"). 75 >Os valores originais são usados quando o formulário é redesenhado, enquanto 76 >os valores *cleaned* são utilizados pela aplicação (por exemplo, para salvar o objeto). 77 77 78 78 ### O atributo `options` 79 79 80 Tanto o `sfWidgetForm` e `sfValidatorBase` objetostêm uma variedade de opções:81 algumas são opcionais, enquanto outras sãoobrigatórias. Estas opções são definidas80 Tanto o objeto `sfWidgetForm` quanto o `sfValidatorBase` têm uma variedade de opções: 81 algumas opcionais, outras obrigatórias. Estas opções são definidas 82 82 dentro do método `configure()` de cada classe através de: 83 83 … … 86 86 87 87 Estes dois métodos são muito convenientes pois garantem que os valores de dependência 88 são corretamente passadospara o validador ou o widget.89 90 Construi rum Widget e Validator Simples88 são passados corretamente para o validador ou o widget. 89 90 Construindo um Widget e Validator Simples 91 91 -------------------------------------- 92 92 93 93 Esta seção irá explicar como criar um widget simples. Este elemento particular 94 será chamado de "Trilean widget". O widget exibirá uma caixa de seleção com três opções:94 será chamado de "Trilean" widget. O widget exibirá uma caixa de seleção com três opções: 95 95 `Não`, `Sim` e `Nulo`. 96 96 … … 121 121 } 122 122 123 $options [] = $this->renderContentTag (123 $options [] = $this->renderContentTag( 124 124 'option', 125 125 self::escapeOnce($option), … … 128 128 } 129 129 130 return $ this-> renderContentTag(130 return $this->renderContentTag( 131 131 'select', 132 132 "\n". implode ("\n", $options). "\n", … … 136 136 } 137 137 138 O método `configure()` define a lista de valores d eopção através da opção `choices`.138 O método `configure()` define a lista de valores da opção através da opção `choices`. 139 139 Este array pode ser redefinido (ou seja, para alterar o rótulo associado a cada valor). 140 140 Não há limite para o número de opções que um widget pode definir. 141 A classe Widget base, no entanto, declara algumas opções padrão, que funcionam de-facto141 A classe base widget, no entanto, declara algumas opções padrões, que funcionam *de-facto* 142 142 como opções reservadas: 143 143 … … 209 209 210 210 **NOTE** 211 > Se `isEmpty()` retorna true, o método `doClean()` método nunca será chamado.211 >Se `isEmpty()` retorna true, o método `doClean()` método nunca será chamado. 212 212 213 213 Embora este widget seja bastante simples, ele apresenta algumas características de base importantes … … 220 220 Nesta seção, vamos construir um widget complexo. Novos métodos serão 221 221 introduzidos e o widget terá alguma interação JavaScript também. 222 O widget será chamado de "GMAW": " Google Map Address Widget".222 O widget será chamado de "GMAW": "*Google Map Address Widget*". 223 223 224 224 O que queremos alcançar? O widget deve fornecer uma maneira fácil para o … … 355 355 $value['latitude'] = isset($value['latitude']) ? $value['latitude'] : ''; $ value [ 'latitude']:''; 356 356 357 / / Define o widget address357 // Define o widget address 358 358 $address = new sfWidgetFormInputText(array(), $this->getOption('address.options')); 359 359 $template_vars['{input.search}'] = $address->render($name.'[address]', $value['address']); … … 364 364 $template_vars['{input.latitude}'] = $hidden->render($name.'[latitude]', $value['latitude']); 365 365 366 / / Mesclarmodelos e variáveis366 // Mescla modelos e variáveis 367 367 return strtr( 368 368 $this->getOption('template.html').$this->getOption('template.javascript'), … … 388 388 tratar adequadamente os diferentes elementos. 389 389 390 O método `rende ()` também instancia dois widgets internos: um widget `sfWidgetFormInputText`,390 O método `render()` também instancia dois widgets internos: um widget `sfWidgetFormInputText`, 391 391 que é usado para processar o campo `address` e um widget `sfWidgetFormInputHidden`, 392 392 que é usado para processar os campos ocultos. … … 433 433 434 434 * `init()`: o método em que todas as variáveis são inicializadas e eventos 435 inputs435 para diferentes inputs 436 436 437 437 * `lookupCallback()`: um método *estático* usado pelo método de geocoder … … 444 444 445 445 Por favor, consulte a documentação do Google map para obter mais detalhes sobre a funcionalidade 446 do Google Maps [API](http://code.google.com/apis/maps/).446 do Google maps [API](http://code.google.com/apis/maps/). 447 447 448 448 ### Validador `sfValidatorGMapAddress` … … 453 453 os diferentes valores: `latitude`, `longitude` e `address`. A variável `$value` 454 454 deve ser um array, mas como não se deve confiar na entrada do usuário, o validador 455 verifica a presença de todas as chaves para que os validadores internos passam valores válidos.455 verifica a presença de todas as chaves para que os validadores internos passam 456 456 valores válidos. 457 457 … … 461 461 protected function doClean($value) 462 462 { 463 if (! if (!is_array($value))463 if (! if(!is_array($value)) 464 464 { 465 465 throw new sfValidatorError($this, 'invalid'); … … 489 489 >Um validador sempre lança uma `excepção` `sfValidatorError` quando um valor não é 490 490 >válido. Por isso, a validação é cercada por um bloco `try/catch`. 491 >Neste validador, o validadorre-lança uma nova excepção `invalid`, que491 >Neste validador, a validação re-lança uma nova excepção `invalid`, que 492 492 >equivale a um erro de validação `invalid` no validador 493 493 >`sfValidatorGMapAddress`. … … 495 495 ### Testando 496 496 497 Por que o teste é importante? O validador é a colaentre a entrada do usuário498 e a aplicação. Se o validador é falho, a aplicação évulnerável.497 Por que o teste é importante? O validador é a ponte entre a entrada do usuário 498 e a aplicação. Se o validador é falho, a aplicação esta vulnerável. 499 499 Felizmente, o symfony vem com o `lime` que é uma biblioteca de testes 500 500 muito fácil de usar. … … 542 542 o validador `sfValidatorGMapAddress` diretamente e testa diferentes valores. 543 543 544 Reflexões Finais544 Considerações Finais 545 545 -------------- 546 546 547 O erro mais comum durante a criação de um widgeté ser focar excessivamente em547 O erro mais comum durante a criação de um *widget* é ser focar excessivamente em 548 548 como as informações serão armazenadas no banco de dados. O framework formulário é 549 549 simplesmente um contêiner de dados e um framework de validação. Portanto, um widget deve 550 550 gerenciar somente a sua informação relacionada. Se os dados forem válidos, em seguida, os diferentes 551 valores limpos poderão então ser utilizad a pelo model ou no controller.551 valores limpos poderão então ser utilizados no modelo ou no controlador. doc/branches/1.4/more-with-symfony/pt/08-Advanced-Doctrine-Usage.markdown
r28027 r28263 1 Uso Avançado do Doctrine 2 ======================= =3 4 * Por Jonathan H. Wage*5 6 Criando um comportamento paraDoctrine7 --------------------------- -----------8 9 Nesta seção vamos demonstrar como você pode escrever um comportamento usando Doctrine 1.2.1 Uso Avançado do Doctrine 2 ======================= 3 4 *Por Jonathan H. Wage* 5 6 Criando um Comportamento no Doctrine 7 --------------------------- 8 9 Nesta seção vamos demonstrar como você pode escrever um comportamento (*behavior*) usando Doctrine 1.2. 10 10 Iremos criar um exemplo que lhe permitirá manter facilmente um contador em cache dos relacionamentos 11 de modo que você não terá que consultar a contagema todo momento.11 de modo que você não terá que consultar o contador a todo momento. 12 12 13 13 A funcionalidade é bastante simples. Para todos os relacionamentos que você deseja manter 14 14 um contador, o comportamento irá adicionar uma coluna ao modelo para armazenar a contagem atual. 15 15 16 ### O Schema17 18 Aqui está o Schemaque você irá utilizar para começar. Mais tarde vamos modificá-lo e adicionar a16 ### O Esquema 17 18 Aqui está o esquema (*schema*) que você irá utilizar para começar. Mais tarde vamos modificá-lo e adicionar a 19 19 definição do `actAs` para o comportamento que estamos prestes a escrever: 20 20 … … 40 40 foreignAlias: Posts 41 41 42 Agora podemos criar tudo para este schema:43 44 $ php symfony doctrine:build all42 Agora podemos criar tudo para este esquema: 43 44 $ php symfony doctrine:build --all 45 45 46 46 ### O Template 47 47 48 Primeiro, precisamos escrever uma classe de template (modelo) `Doctrine_Template` filhaque será48 Primeiro, precisamos escrever uma classe filha de `Doctrine_Template` que será 49 49 responsável por adicionar as colunas ao modelo que irá armazenar a contagem. 50 50 … … 80 80 terão os métodos `setTableDefinition()` e `setUp()` invocados. Da mesma forma que você têm 81 81 na classe `BasePost` em `lib/model/doctrine/base/BasePost.class.php`. Isto 82 lhe permite adicionar coisas para qualquer modelo de modo plug n' play. Isto pode estar83 nas colunas, relacionamentos, ouvintes de eventos , etc82 lhe permite adicionar coisas para qualquer modelo de modo *plug n' play*. Isto pode ser 83 nas colunas, relacionamentos, ouvintes de eventos (*event listeners*), etc. 84 84 85 85 Agora que você entende um pouco sobre o que está acontecendo, vamos fazer o 86 comportamento `CountCache` realmente fazer alguma coisa:86 comportamento `CountCache` fazer alguma coisa de fato: 87 87 88 88 [php] … … 106 106 $columnName = $this->_options['relations'][$relation]['columnName']; 107 107 $relatedTable = $this->_table->getRelation($relation)->getTable(); 108 $ this-> _OPTIONS [ 'Relações'] [$ relation] [ 'className'] = $ RelatedTable-> GetOption ( 'nome');108 $this->_options['relations'][$relation]['className'] = $relatedTable->getOption('name'); 109 109 $relatedTable->setColumn($columnName, 'integer', null, array('default' => 0)); 110 110 } … … 112 112 113 113 114 O código acima irá a gora adicionar colunas para manter a contagem do modelo relacionados.114 O código acima irá adicionar colunas para manter a contagem do modelo relacionado. 115 115 Portanto, no nosso caso, estamos adicionando o comportamento ao modelo `Post` para o relacionamento 116 com `Thread` Nós queremos manter o número de posts qualquer instancia de `Thread`117 tem em uma coluna chamada `num_posts`. Então agora modifique o YAML schemapara116 com `Thread`. Nós queremos manter o número de posts que qualquer instância de `Thread` 117 tem em uma coluna chamada `num_posts`. portanto modifique o esquema YAML para 118 118 definir as opções adicionais para o comportamento: 119 119 … … 130 130 # ... 131 131 132 Agora , o modelo `Thread` temuma coluna `num_posts`que irá manter-se atualizada132 Agora o modelo `Thread` possui uma coluna `num_posts`que irá manter-se atualizada 133 133 com o número de posts que cada thread tem. 134 134 135 ### O Ouvinte de Eventos 136 137 O próximo passo para a construção do comportamento é escrever um ouvinte de evento registrador135 ### O Ouvinte de Eventos (*Event Listener*) 136 137 O próximo passo para a construção do comportamento é escrever um registrador de ouvintes de evento (*record event listener*) 138 138 que será responsável por manter a contagem atualizada quando inserirmos novos registros, 139 excluir um registro ou executarDQL de exclusão de registros em lote:139 excluirmos um registro ou executarmos DQL de exclusão de registros em lote: 140 140 141 141 [php] … … 152 152 } 153 153 154 Antes de prosseguirmos, precisamos definir a `classe` CountCacheListenerque155 extende `Doctrine_Record_Listener`. El eaceita uma variedade de opções que são simplesmente156 transmitidos ao ouvinte a partir do modelo:154 Antes de prosseguirmos, precisamos definir a classe `CountCacheListener` que 155 extende `Doctrine_Record_Listener`. Ela aceita uma variedade de opções que são simplesmente 156 repassadas ao ouvinte a partir do modelo: 157 157 158 158 [php] … … 196 196 ->createQuery() 197 197 ->Update() 198 ->set($options['columnName'], $options['columnName'].' +1 )198 ->set($options['columnName'], $options['columnName'].' +1') 199 199 ->where($relation['local'].' = ?', $invoker->$relation['foreign']) 200 200 ->execute(); … … 204 204 205 205 O código acima irá incrementar a contagem em um para todas os relacionamentos configurados 206 mediante a emissão de uma instrução DQL UPDATE quando um novo objeto como é inserido abaixo:206 mediante a emissão de uma instrução DQL UPDATE quando um novo objeto como é inserido, conforme o exemplo abaixo: 207 207 208 208 [php] 209 209 $post = new Post(); 210 $ post->thread_id = 1;210 $post->thread_id = 1; 211 211 $post->body = 'corpo da mensagem'; 212 212 $post->save(); 213 213 214 O `Thread` com o `id` `1` terá a coluna `num_posts` coluna incrementadoem `1`.214 O `Thread` com o `id` `1` terá a coluna `num_posts` incrementada em `1`. 215 215 216 216 Agora que o contador está sendo incrementado quando novos objetos são inseridos, nós … … 242 242 243 243 O método `postDelete()` acima é quase idêntico ao `postInsert`. A 244 única diferença é que nós diminuiremos a coluna `num_posts` em 1ao invés de244 única diferença é que nós diminuiremos a coluna `num_posts` em `1` ao invés de 245 245 incrementá-lo. Ele manipularia o seguinte código se fôssemos remover o registro `$post` 246 246 salvo previamente: … … 283 283 } 284 284 285 O código acima clona a instrução `DQL DELETE` e transformá- loem um `SELECT` que285 O código acima clona a instrução `DQL DELETE` e transformá-a em em um `SELECT` que 286 286 nos permite recuperar os `ID`s que serão excluídos, para que possamos atualizar o contador 287 287 desses registros que foram excluídos. 288 288 289 Agora, temos o seguinte cenário cuidando de que o contador será decrementado290 se tivéssemos de fazero seguinte:289 Agora, temos o seguinte cenário tratado e os contadores serão decrementados 290 se fizéssemos o seguinte: 291 291 292 292 [php] … … 307 307 ->execute(); 308 308 309 >**NOT A**309 >**NOTE** 310 310 >Para que o método `preDqlDelete()` seja invocado você deve habilitar 311 311 >um atributo. Os retornos DQL estão desligados por padrão devido a eles terem um custo … … 341 341 342 342 Agora tudo está criado e a massa de dados está carregada, por isso vamos executar um teste para ver 343 Se os contadores foram mantidas atualizados:343 se os contadores foram mantidas atualizados: 344 344 345 345 $ php symfony doctrine:dql "FROM Thread t, t.Posts p" … … 397 397 ->execute(); 398 398 399 Agora nós excluimos tod os as mensagens relatadas e o valor de `num_posts` deverá ser zero:399 Agora nós excluimos todas as mensagens relacionadas e o valor de `num_posts` deverá ser zero: 400 400 401 401 $ php symfony doctrine:dql "FROM Thread t, t.Posts p" … … 406 406 doctrine - Posts: { } 407 407 408 E é isso! Espero que este artigo seja útil tanto no sentido de que você aprend a409 algo sobre os comportamentos e os comportamentos em si também sejam úteis à você!408 E é isso! Espero que este artigo seja útil tanto no sentido de que você aprendeu 409 algo sobre os comportamentos e que os comportamentos em si também sejam úteis à você! 410 410 411 411 Usando o Cache de Resultados do Doctrine … … 413 413 414 414 Em aplicações web de tráfego pesado é comum necessitar de um cache de informações 415 para salvar recursos de CPU. Com a última versão do Doctrine 1.2 fizemos um monte de415 para poupar recursos de CPU. Com a última versão do Doctrine 1.2 fizemos um monte de 416 416 melhorias no cache de conjunto de resultados que lhe dará muito mais controle sobre 417 a remoção de entradas no cache a partir dos controladore de cache. Anteriormente não era possível417 a remoção de entradas no cache a partir dos controladores de cache. Anteriormente não era possível 418 418 especificar a chave de cache usado para armazenar a entrada no cache, então você não podia realmente 419 identificar a entrada de cache a fim de excluí-l o.419 identificar a entrada de cache a fim de excluí-la. 420 420 421 421 Nesta seção, mostraremos um exemplo simples de como você pode utilizar o cacheamento de conjunto de resultados 422 422 para cachear todas as consultas relacionadas a seu usuário, bem como o uso de eventos para se certificar 423 de que eles sejam devidamente apurados quando alguns dado for alterado.423 de que eles sejam devidamente limpos quando alguns dadoa forem alterados. 424 424 425 425 ### Nosso Schema … … 461 461 } 462 462 463 Mais tarde no artigo você vai precisar adicionar algum código para esta classe para fazer a anotação da mesma.463 Mais tarde, no artigo, você vai precisar adicionar algum código a esta classe para fazer a anotação da mesma. 464 464 465 465 ### Configurando o Cache de Resultado … … 492 492 ### Consultas de exemplo 493 493 494 Agora imagine que em sua aplicação você tem um grande número de consultas re alcionionaao usuário494 Agora imagine que em sua aplicação você tem um grande número de consultas relacionadas ao usuário 495 495 e quer apurá-los sempre que algum dado do usuário for alterado. 496 496 … … 508 508 $q->useResultCache(true, 3600, 'users_index'); 509 509 510 >**NOT A**510 >**NOTE** 511 511 >Observe o terceiro argumento. Esta é a chave que será usada para armazenar a entrada 512 512 > cacheada para os resultados no controlador de cache. Isso nos permite identificar facilmente 513 513 >esta consulta e excluí-la do controlador de cache. 514 514 515 Agora, quando executarmos a consulta qu iráirá consultar o banco de dados para buscar os resultados e armazená-515 Agora, quando executarmos a consulta que irá consultar o banco de dados para buscar os resultados e armazená- 516 516 los no controlador de cache na chave chamada `users_index` e todas as requisições posteriores 517 517 obterão as informações do controlador de cache em vez de pedir ao banco de dados: … … 520 520 $users = $q->execute(); 521 521 522 >**NOT A**522 >**NOTE** 523 523 >Isso não somente economiza o processamento no servidor de banco de dados, ele também ignora 524 > o processo inteiro de hidratação que é como o Doctrine armazena os dados hidratado . Isso significa524 > o processo inteiro de hidratação que é como o Doctrine armazena os dados hidratados. Isso significa 525 525 > que irá também aliviar um pouco o processamento do seu servidor web. 526 526 … … 549 549 -lo em um evento. 550 550 551 >** Dica**551 >**TIP** 552 552 > Para ter acesso à instância do controlador de cache de resultado você pode recuperá-lo a partir da 553 553 > instância da classe `Doctrine_Manager`. … … 582 582 passado; 583 583 584 * `deleteByReg ex($regex)`: Exclui as entradas de cache que correspondam à584 * `deleteByRegularExpression($regex)`: Exclui as entradas de cache que correspondam à 585 585 expressão regular passada; 586 586 … … 611 611 612 612 Agora, se fôssemos atualizar um usuário ou inserir um novo ele iria limpar o cache para 613 todas as consultas rela rionadas ao usuário:613 todas as consultas relacionadas ao usuário: 614 614 615 615 [php] 616 616 $user = new User(); 617 $user->username = 'j orge';617 $user->username = 'jwage'; 618 618 user->password = 'mudeme'; 619 619 $user->save(); … … 625 625 usar esses recursos para implementar um cacheamento refinado em suas consultas Doctrine. 626 626 627 Criando H ydratador Doctrine627 Criando Hidratador Doctrine 628 628 --------------------------- 629 629 630 630 Uma das principais características do Doctrine é a capacidade de transformar um objeto `Doctrine_Query` 631 em várias estruturas de conjunto de resultado . Este é o trabalho dos hidratadores do631 em várias estruturas de conjunto de resultados. Este é o trabalho dos hidratadores do 632 632 Doctrine e até a versão 1.2, o hidratadores eram todos codificados rigidamente e não 633 633 eram abertos aos desenvolvedores para serem personalizados. Agora que isto mudou, é possível … … 658 658 User: 659 659 user1: 660 username: j orge660 username: jwage 661 661 password: mudeme 662 662 is_active: 1 663 663 user2: 664 username: jo rjao664 username: jonwage 665 665 password: mudeme 666 666 is_active: 0 … … 670 670 $ php symfony doctrine:build --all --and-load 671 671 672 ### Escrevendo o H ydratador672 ### Escrevendo o Hidratador 673 673 674 674 Para escrever um hidratador tudo o que precisamos fazer é escrever uma nova classe que se estende `Doctrine_Hydrator_Abstract` … … 752 752 Array 753 753 ( 754 [j orge] => 1755 [jo rjao] => 0754 [jwage] => 1 755 [jonwage] => 0 756 756 ) 757 757 758 758 Bem, é isso! Simplesmente lindo não? Espero que isso seja útil a você e como resultado 759 a comunidade terá alguns novos e impressivos hidratadores contribuídos.759 a comunidade terá contribuições de alguns novos e expressivos hidratadores.