Blog movido

11 \11UTC fevereiro, 2010

Esse blog foi movido para: http://webmaster.diegoquinteiro.com

Getting Real (Caindo na Real): É melhor remediar que prevenir!

2 \02UTC julho, 2008

As mensagens que o livro Getting Real quer passar são das mais incomuns. Alguns dos conceitos defendidos pelo livro dos gurus da 37signals, criadores do Ruby on Rails, parecem contariar tudo o que aprendemos:

  • Caia na real!
  • Faça menos que sua concorrência e seja ágil para mudar de rumo.
  • Jogue fora sua bola de cristal: não perca tempo hoje resolvendo problemas de amanhã – você já tem problemas suficientes hoje para se preocupar.
  • Seu projeto funcionando deve ser sua melhor e possivelmente única documentação. Não crie documentos que não viram realidade.
  • Reuniões devem ser evitadas. Quando inevitáveis, coloque o alarme do celular para 30 minutos: quando tocar, acabou a reunião.
  • Lembre-se: erros ocorrerão. Não se preocupe: é um sistema para web e não uma cirurgia de cérebro.

É uma abordagem um tanto chocante que pode deixar alguns contrariados, mas que certamente acrescentará muito a todos.

O livro está disponível gratuitamente na íntegra, traduzido para o português como “Caindo na Real”.

Leia, é grátis!

Mapeamento Objeto-Relacional (ORM)

1 \01UTC julho, 2008

Com o lançamento do PHP5, as esperanças de utilizar as técnicas da programação orientada à objetos (POO) no desenvolvimento de sistemas web cresceu para os adeptos da linguagem . A versão anterior do PHP já possuia recursos de orientação à objetos, mas estes eram muito limitados e alguns problemas chegavam a desencorajar seu uso. O PHP5 veio com um novo motor, o Zend Engine 2, que corrigiu esses problemas e suporta a maioria dos recursos esperados de uma linguagem orientada à objetos, como interfaces, métodos abstratos, herança, polimorfismo, dentre outros.

Apesar das linguagens modernas possuirem recursos da POO, os sistemas de gerenciamento de bases de dados (SGBD) são quase todos baseadas no modelo relacional, onde temos tabelas ao invés de objetos. E na maioria dos casos o que queremos é justamente guardar nossos objetos na base dados. Guardar instâncias de objetos em registros de tabelas é o objetivo das técnicas de mapeamento objeto-relacional (em inglês object-relational mapping, ORM).

Existem muitas diferenças entre os modelos relacional e orientado à objeto e a mais incômoda delas para quem desenvolve sistemas web é que tabelas não suportam herança.

Considere o seguinte exemplo: temos três classes, sendo uma delas abstrata e as outras duas concretas e extendendo a primeira. Mais palpável ainda: considere uma classe abstrata Pessoa e as classes concretas Fornecedor e Cliente que são filhas da classe Pessoa.

Como guardar essas informações no banco de dados?

Uma tabela por classe contreta

A solução mais imediata seria criar 2 tabelas, da seguinte forma:

Essa solução mais simples ignora completamente a hierarquia de classes e faz uma tabela por classe concreta sem relacionamentos.

  • Prós:
    • Todos os dados de um objeto estão em uma só tabela, facilitando a manipulação.
    • Buscas serão executadas de forma rápida.
    • Não há campos nulos.
  • Contras
    • Dificulta alterações na classe pai, pois os campos estão duplicados (ou triplicados, quadruplicados…).
    • Pode ser um grande problema se houver alguma classe com referência para a classe pai (veja próximo exemplo).

Em alguns casos talvez essa seja uma boa solução. Não é o caso se houver uma referência para a classe pai, como por exemplo:

UML - Referência à classe pai

Nesse caso a nossa primeira solução falha, pois não existe uma forma de relacionar Mensagem com Pessoa sem explicitar se é um Fornecedor ou um Cliente. Poderíamos claro, usar a chave estrangeira de todas as subclasses, mas isso seria um pesadelo no caso de muitos relacionamentos e/ou muitas subclasses. Precisamos de uma solução melhor.

Uma tabela por árvore de classes

Podemos utilizar 1 só tabela para todas as subclasses de Pessoa, solucionando o problema anterior:

Nesta solução temos 1 tabela para cada árvore de classes. O campo tipo irá determinar se o registro é um Cliente ou Fornecedor. O campo endereço ficará nulo no caso dos clientes e o campo telefone ficará nulo no caso dos fornecedores.

  • Prós:
    • Todas as informações de uma instância estão na mesma tabela, o que facilita a manipulação.
    • Permite referências à classe pai.
  • Contras:
    • Muitos campos nulos – pode ser muito deselegante para grandes árvores de classes.
    • Muitos registros na mesma tabela podem retardar as busca. Mesmo que indexemos o campo tipo teremos problemas, já que só é utilizado um índice por busca.
    • Registros muito grandes podem retardar as buscas.
    • Campo tipo extra.
    • Não permite referência específica às subclasses.

Note que essa solução falha se precisarmos ter uma referência para a subclasse. Se as mensagens, por exemplo, pudessem ser trocadas apenas entre clientes, não haveria como fazer essa restrição no banco de dados. E se nossa árvore de classes for grande a tabela se tornará um monstro com dezenas de campos nulos em cada registro. Precisamos ainda de uma solução melhor.

Uma tabela por classe

Podemos mapear cada classe para uma tabela, fazendo a representação da hereança por um relacionamento 1 para n:

Assim temos 1 tabela por classe, relacionando as classes de forma a mater a árvore e usando o campo tipo para indicar a subclasse. Na hora de buscar um registro, checa-se esse campo para saber qual é a classe e, logo, qual outra tabela consultar.

  • Prós:
    • Mantém as hierarquia das classes.
    • Não contém campos nulos.
    • Não quebra o encapsulamento de implementação da classe pai.
    • Permite fazer referências tanto a classe pai quanto às subclasses.
    • Registros pequenos, com poucos campos.
  • Contras:
    • Os atributos de um objeto estão espalhados em mais de uma tabela, tornando as buscas mais complexas.
    • As buscas podem ficar mais lentas pelo uso excessivo de joins.
    • Campo tipo extra.

Essa solução é mais completa, podendo ser utilizada em todas as situações. Além disso ela tem uma boa relação custo/benefício até para situações onde as outras soluções também são possíveis.

Conclusão

Recomendo utilizar a solução de uma tabela por classe sempre. O motivo é simples: mesmo que as outras soluções sejam aplicáveis em algumas situações, essas situações podem mudar na próxima alteração, forçando a implementar uma tabela por classe . Além disso, a perda de desempenho pode ser bastante minimizada pela utilização correta de índices.

Bordas arredondadas no CSS com uma forcinha do PHP

29 \29UTC junho, 2008

Bordas arredondadas parecem nunca sair de moda. Para a infelicidade dos webdesigners, adicionar mais de uma imagem de fundo a um elemento e a propriedade “border-radius” são exclusividades da versão 3 do CSS, que ainda não é suportada pelo navegador da Microsoft e por isso seu uso está fora de cogitação.

Mas nem tudo está perdido: uma ótima técnica que utiliza CSS2, descrita no artigo “Even More Rounded Corners”, permite mostrar bordas arredondadas utilizando apenas uma imagem PNG com a caixa inteira e uma porção de declarações “background-position” para fatiar virtualmente a imagem. Como todas as soluções compatíveis com o IE, ela necessita de marcação extra no HTML para funcionar. Na verdade, ela é bem compacta, vejam:

<div class="dialog">
<div class="content">
<div class="t"></div>
<!-- Seu contéudo vai aqui --></div>
<div class="b">
<div></div>
</div>
</div>

Mesmo compacta ela ainda causa, em menor intensidade, os mesmos já conhecidos problemas das marcações extras:

  • Dificuldade de manutenção: é necessário reproduzir a estrutura cada vez que quisermos uma caixa arredondada.
  • Perda de legibilidade: seu conteúdo ficará escondido num mar de DIVs inúteis.
  • Quebra de encapsulamento: tags com propósito unicamente visual (DIVs vazias!) tornam o código menos semântico e intuitivo.

Não sendo possível diminuir mais a quantidade de porcaria marcação inútil servida para o usuário, podemos ao menos reduzir algumas linhas do nosso código fonte no servidor, utilizando o PHP.

A solução aqui apresentada permitirá escrever no fonte:

<div class="dialog">
<!-- Seu conteúdo vai aqui --></div>

E ter como resultado o código “sujo” da técnica do “Even More Rounded Corners” na saída. O truque é fazer com que o PHP injete as divs necessárias e sirva para o usuário a marcação extra sem sujar o fonte, melhorando legibilidade, facilitando a manutenção e evitando em certo nível a quebra de encapsulamento. Para executar a mágica vamos usar o controle de buffer de saída do PHP e a extensão DOM, para manipular o HTML.

Primeiro, vamos precisar adicionar o seguinte código no início da página, fazendo com que ele execute antes do script emitir qualquer saída:

<?
// Captura a saída em um buffer.
ob_start();
?>

Isso garantirá que toda a saída do PHP seja guardada em um buffer, ao invés de ser enviada diretamente ao user agent.

O próximo e último passo é pegar toda a saída e injetar a marcação extra. Isso deve ser feito, é claro, no fim do script, depois de qualquer saída ter sido gerada e, conseqüentemente, guardada no buffer:

<?
// Lê o buffer de saída para uma variável string.
$saida = ob_get_contents();

ob_end_clean();

// Defina aqui a classe a ser considerada
// para caixas arredondadas.
$classe = "dialog";

// Carrega o documento na classe DOM.
$dom = new DomDocument("1.0");

// Detecta a codificação da página.
if (
  mb_detect_encoding(
  $saida . 'a' , 'UTF-8, ISO-8859-1' )
  ==
  "UTF-8"
  )
{
  $saida = utf8_decode($saida);
}
$dom->loadHTML($saida);

// Seleciona todas as DIVs
$divs = $dom->getElementsByTagName("div");

foreach ($divs as $div) {

  // Filtra as DIVs da classe selecionada.
  if (strstr($div->getAttribute("class"), $classe)) {

    // Cria os nós adicionais
    $content_div = $dom->createElement("div");
    $content_div->setAttribute("class", "content");

    $t_div = $dom->createElement("div");
    $t_div->setAttribute("class", "t");

    $b_div = $dom->createElement("div");
    $b_div->setAttribute("class", "b");

    $extra_div = $dom->createElement("div");

    // Cria a árvore correta
    $b_div->appendChild($extra_div);
    $content_div->appendChild($t_div);

    // Importa o conteúdo do div a ser arredondado
    // para dentro de 'content'.
    while ($div->hasChildNodes()) {
      $content_div->appendChild(
      $div->removeChild($div->firstChild)
      );
    }

    // Adiciona tudo de volta à div.
    $div->appendChild($content_div);
    $div->appendChild($b_div);
  }
}

$resultado = $dom->saveHTML();

echo $resultado;

?>

É claro que você não irá copiar e colar esse texto em todas as suas páginas. Você pode usar dois includes (um no começo do documento e outro ao final), e ainda, se você usa algum sistema de templates, fazer esses includes apenas no arquivo do template.

Podemos adaptar essa técnica para várias outras situações onde necessitamos de marcação extra. Podemos ainda adaptá-la facilmente para rodar no lado do cliente, uma vez que as funções DOM do PHP e do javascript são praticamente idênticas. O único porém deste último método é que não podemos garantir que o user agent suporte e esteja com o javascript habilitado e é então sujeitos a falhas, motivo pelo qual escolhi o método server-side.

Agora ninguém mais tem desculpa para fazer cara feia quando tiver que implementar aquele design todo arredondado!

CSS Hacks ou comentários condicionais?

29 \29UTC junho, 2008

Meu xará Diego Eis escreveu um interessante artigo já há alguns meses em seu famoso blog do site Tableless, o qual recomendava o não uso de comentários condicionais e defendia o uso de CSS Hacks.

O argumento central do texto é que o uso de comentários condicionais duplicam o trabalho do desenvolvedor, pois é criado um arquivo CSS para os navegadores mais compatíveis com as especificações da W3C e outro(s) para os navegadores Internet Explorer. Dada a opção de usar dois arquivos, a tendência é criar uma folha de estilos muito diferente ou até mesmo independente para o navegador da Microsoft.

Mas ao dar uma passada de olho nas estatísticas de uso de navegadores deste mês notamos que cerca de 98% dos usuário utilizam Internet Explorer 6, Internet Explorer 7, Firefox, Opera ou Safari. Menos de 1% utilizam o Internet Explorer 5, conhecido pelo sua implementação problemática do CSS Box Model. Isto significa que se usarmos o Doctype Switch para deixar o IE6 em Strict Mode, atenderemos mais de 98% dos visitantes sem nos valermos do famigerado Box Model Hack! Sendo assim, não precisamos mais definir a altura/largura de cada elemento duas vezes, enxugando de forma decisiva o arquivo CSS exclusivo para os IEs.

Podemos em alguns casos eliminar completamente esse arquivo e ao mesmo tempo não usar nenhum hack, nivelando por baixo os recursos usados e tolerando algumas pequenas diferenças de renderização. Esse é melhor dos mundos, mas em layouts mais rígidos ou complexos e no uso de algumas técnicas mais sofisticadas isso infelizmente não é uma opção, e voltamos a ter que decidir entre hacks e comentários condicionais. Levando em conta o que já foi dito, faço uma avaliação dos prós e contras de cada solução:

Comentarios condicionais:

  • Prós:
    • Mantém o CSS original inalterado e válido.
    • Permite servir CSS customizado ou proprietário de forma segura e suportada pela Microsoft.
    • Pode-se criar um arquivo diferente para cada versão do IE.
    • Feitos para serem usados!
  • Contras:
    • Arquivo separado pode ser difícil de manter.
      • Não é trivial notar onde existe ou não uma correção para o IE ao ler o arquivo original.
      • Por vezes também não é imediata a compreensão do funcionamento da correção no arquivo customizado sem ler a definição no arquivo original.
    • Uma requisição HTTP a mais.
    • Adições no HTML que apesar de comentadas são grandes.
    • Horrível de implementar em definições do atributo style da tag.

CSS Hacks:

  • Prós:
    • Tudo no mesmo arquivo, é fácil ver onde as correções acontecem e ao que se aplicam.
    • Mais compacto, pode ser aplicado facilmente no atributo style.
    • Pode ser usado em definições do atributo style do javascript.
    • Em alguns casos são necessários para outros navegadores que não o Internet Explorer.
  • Contras:
    • Alterações no CSS original, possivelmente comprometendo a validação.
    • Método não suportado, pode criar problemas a cada atualização que a Microsoft liberar.
    • Hacks diferenciais (que afetam apenas uma versão) são deselegantes e deus sabe quanto deselegantes eles podem se tornar nas próximas versões do IE, ou mesmo se alguém será capaz de desenvolvê-los.
    • Baseados em bugs!

Atentando para cada um destes detalhes, faço minhas recomendações. Seguem passo a passo:

  1. Use o Doctype Switch para ativar o Strict Mode no IE6.
  2. Ignore o IE5 e anteriores.
  3. Procure diminuir ao máximo as diferenças entre as definições CSS dos navegadores. Tente eliminá-las completamente, utilizando um CSS Reset e simplificando o layout de forma que as pequenas diferenças de renderização não atrapalhem muito. Isso é fácil de fazer para o IE7, mas pode ser bastante difícil para o IE6, então não complique muito seu código para atingir esse objetivo e considere o próximo passo.
  4. Caso sobrem diferenças, separe-as em outro arquivo (use um nome sugestivo, ‘iefix.css’) utilizando os comentários condicionais. Não crie mais de um arquivo, trabalhe bastante no passo anterior para não precisar de um específico para o IE7.
  5. Utilize comentários no CSS original para indicar onde existem correções no arquivo ‘iefix.css’.
  6. Não utilize o atributo style. Se precisar alterar o estilo por javascript, utilize classes.
  7. Se notar que seu ‘iefix.css’ estiver crescendo muito, considere voltar ao passo 3. Se não houver mais onde enxugar e o tamanho do arquivo ainda for um problema, considere utilizar os Hacks, mas não utilize uma técnica híbrida: utilize CSS Hacks ou comentários condicionais – misturar os dois é a receita certa para dores de cabeça.

Espero ter ajudado (a colocar mais lenha na fogueira desta disussão)!


Seguir

Obtenha todo post novo entregue na sua caixa de entrada.