Problema com IDs alfanuméricos no form->input (select) do CakePHP

Para quem já está mexendo com CakePHP a algum tempo, algumas limitações de funcionalidades podem tirar um pouco o sono de quem está muito mal acostumado com a produtividade da framework. Em geral, toda framework tem seus pontos fracos.

A algum tempo, tive um problema curioso: a chave primária de uma tabela teria valores alfanuméricos. Ou seja: valores como “205-A”, “JB02” e outros bizarros seriam possíveis. Regra de negócio definida, modificação do modelo para o nome de chave fora da convenção e tudo corria bem até que populei um select.

🙁 Qual não foi minha surpresa em ver que o CakePHP, além de não marcar o valor correto, em certos casos ainda selecionava mais de um valor no select. #fail total.

Pesquisa na internet, perguntas na lista de discussão oficial do CakePHP e as respostas eram:

  • Coisas que não tinha nada haver com o problema
  • Sugestões pra mudar a estratégia para resolver o problema
  • Gente falando que o CakePHP só trampa com valores numérico (o que de fato acontece :P)

O problema

Então, mexendo no código pra cima e pra baixo (sem conotações sexuais por favor) consegui achar o problema.

No arquivo  cake/libs/views/helpers/form.php (cakephp 1.2.5), na linha 1710:

if ((!$selectedIsEmpty && $selected == $name) || ($selectedIsArray && in_array($name, $selected))) {

Depois de fuçar e testar um pouco, vi que o problema principal na verdade é um problema com o próprio operador == do PHP: ele trata as operações entre strings e numéricos (http://php.net/manual/en/language.operators.comparison.php) de uma forma que gera os seguintes resultados:

  • um valor como 206-1 por exemplo, o helper casava tanto o valor 206-1 (o correto) quanto 206 (string == integer).
  • um valor como JB01 por exemplo, o helper casava tanto o valor JB01 (o correto) quanto 0 (string == integer).

Devido à característica do PHP de resolver qual o tipo da variável em tempo real, ele tenta fazer o casting da variável sempre e acaba gerando resultados não esperados pra gente.

Solução

A solução então foi forçar (type casting) as variáveis para serem tratadas como strings. Assim, evitamos que o PHP transforme nossas variáveis em tipos distintos (no nosso caso string e integer) e consequentemente faça a comparação de valores do jeito que precisamos.

A linha 1710 (no cakephp 1.2.5 ou 1748 no cakephp 1.3-dev) ficou assim:

if ((!$selectedIsEmpty && (string)$selected == (string)$name) || ($selectedIsArray && in_array($name, $selected))) {

Note o type casting sendo feito nas variáveis $selected e $name. 😉

Se alguem conseguir uma solução mais bacana ou encontrou outro bug bizarro do CakePHP, compartilhe! 🙂

Simbora!

Update: Eu tinha reportado esse erro a algum tempo no track de bugs do CakePHP  e hoje vi que já corrigiram isso. Então, quando sair a versão stable do cake 1.3, não precisa mais se preocupar com isso. 😀

  • Pingback: Léo Hackin()

  • Óia que bacana: eu tinha reportado o bug/correção dessa falha anteriormente para o pessoal do Cake (http://code.cakephp.org/tickets/view/167) mas com outra abordagem pro erro. Alterei a estratégia, enviei de novo e não é ele já tinham corrigido? HAHEHEHAUEe Iradasso. 🙂

    Então, quando sair a versão stable do cake 1.3, não precisa mais se preocupar com isso.