Diogo Besson :: tecnologia

Trabalhando com estruturas bizarras em bancos de dados alucinados

Trabalhando com estruturas bizarras em bancos de dados alucinados

Trabalhando com estruturas bizarras em bancos de dados alucinados é um péssimo título. O post fala sobre como estruturar melhor seu banco de dados zuado.

PREFÁCIO

Devo esse update ao colega Giolvani de Matos, autor de um blog sobre tecnologia no wordpress.com, que levantou um problema comum e polêmico na área de sistemas que eu chamo de “despadronização de banco de dados”.

Como o senhor Julio “Jota” Cesar comentou em seu artigo “Não chame de qualquer coisa”, é necessário que exista uma certa compreensão do que é humanamente inteligível durante a arquitetura de um banco de dados e o que pode ser especulado como uma piada interna do grupo de desenvolvedores ao que se refere à nomenclatura nas estruturas do banco.

Isso acontece, pois temos pessoas trabalhando em um sistema que podem não ser as mesmas pessoas que irão fazer a manutenção deste mesmo sistema. Garanto a você que essas pessoas irão agradecer imensamente ao conseguir compreender com facilidade o que se passa nas entranhas do seu código legado devido a uma padronização bem feita.

O contrário ocorre quando é você quem assume a manutenção de um produto que apresenta um banco de dados (ou bando de dados…) com uma taxonomia alienígena bolada por pessoas completamente alucinadas, que provavelmente ou não sabiam o que estavam fazendo ou simplesmente adorariam tirar um sarro da sua cara ao ver sua calvície recentemente adquirida durante um projeto de software.

Para solucionar esse problema comum encontrado em diversos projetos de sistemas, também é necessário atentar para a modelagem e normalização dos dados, caso estejamos utilizando um banco de dados relacional. Meu amigo João Rubira, que é especialista DBA, poderia falar mais sobre isso em outra ocasião mais específica, porém posso adiantar que não se trata de uma tarefa tão incomum e difícil. Creio que até possa ser rotulada como básica e fundamental na tarefa de desenvolvimento.

Outros bancos, não relacionais, como o CouchDB ou o IBM Lotus Notes (Domino), por exemplo, trabalham de forma diferente, orientam-se por documentos ou utilizam uma outra lógica, e isso não se aplica aqui, como explicou nosso camarada Luciano Ramalho, em seu blog semi-estruturado.

O caso aqui estudado é o reaproveitamento de estruturas prontas e antigas de um sistema legado para a criação de uma interface web. O produto possui mais de 10 anos de idade e seu banco de dados não seguiu um padrão muito convencional.

INTRODUÇÃO

O cliente precisava de um menu na web que chamasse os produtos por categorias e subcategorias, como por exemplo:

– INFORMÁTICA

— Notebook

— Mochilas

— Mouse

— Monitores

— LCD

— Plasma

– ELETRONICOS

— Televisores

O problema é que o banco de dados só possuía código e descrição da categoria, tornando a coisa um pouco “bidimensional” para o desenvolvedor. Caso ele quisesse criar uma subcategoria deveria proceder através da execução da seguinte GAMBIARRA:

Código |    Produto

01               | INFORMÁTICA

01.01        | Notebook

01.01.01 | Mochilas

01.01.02 | Mouse

01.02        | Monitor

01.02.01 | LCD

01.02.02 | Plasma

02               | ELETRONICOS

02.01        | Televisores

A ANÁLISE DA COISA

Provavelmente a arquitetura servia para o sistema legado, que há 10 anos funcionava sem maiores problemas. Provavelmente, em seu projeto inicial, ele não previa subcategorias para os produtos: ou eles eram de INFORMÁTICA, ou eram ELETRONICOS, ou eram de PAPELARIA, ou eram MÓVEIS, ou eram de ALIMENTAÇÃO, e por aí vai…

Muitas vezes o tratamento de requisitos deve levar em conta a escalabilidade do software. Será que ele vai ser usado por mais pessoas do que previmos? Ele vai comportar um aumento do estoque? Poderia sobreviver ao bug do milênio? Precisaremos acrescentar novas funcionalidades em um futuro bem próximo?

São perguntas difíceis de elaborar até mesmo se levarmos em conta a utilização de um profissional especializado, um engenheiro de requisitos ou um analista de negócios durante a elicitação, mas que devem ser abordadas com seriedade, pois neste momento, uma coisa simples que deveria ser a montagem de um menu para web, torna-se uma dor de cabeça para os desenvolvedores (principalmente para os coitados dos programadores que sempre tem de resolver esse tipo de barreira).

Lembrando sempre que dores de cabeça podem significar atrasos de cronograma e aumento de custo na elaboração do software. Prejuízo para a equipe, com toda certeza!

DESENVOLVIMENTO

O X da questão aqui, e creio que vocês já esperavam por isso, é como resolver esse maldito menu, já que o problema existe e não pode ser eliminado com facilidade.

Eu disse “eliminado”, pois ocorreram sugestões de refazer o banco, de criarmos campos pai e campos filhos, etc… Porém, todas elas foram recusadas devido ao prazo apertado (pra ontem!) de elaboração da solução.

Antes que todo mundo comece a xingar, devo dizer que a solução de reestruturação do banco, com um DBA atuante, é a que melhor resolveria a situação, mas que de vez em quando, PRECISAMOS fazer uma nova gambiarra para corrigir a antiga (vide artigo porreiro sobre POG da Desciclopédia).

LABORATÓRIO

Bom, vamos ao laboratório:

Primeiramente, crie no seu MYSQL a seguinte estrutura:

[sourcecode language=”sql”]

— Banco de Dados: `loja`

— ——————————————————–

— Estrutura da tabela `categorias`

CREATE TABLE IF NOT EXISTS `categorias` (

`codigo` varchar(255) COLLATE latin1_general_ci NOT NULL,

`descricao` varchar(255) COLLATE latin1_general_ci NOT NULL,

PRIMARY KEY (`codigo`)

) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;

[/sourcecode]

Agora, adicione os dados de teste:

[sourcecode language=”sql”]

— Extraindo dados da tabela `categorias`

INSERT INTO `categorias` (`codigo`, `descricao`) VALUES

('01', 'INFORMATICA'),

('01.01', 'Notebook'),

('01.01.01', 'Mochilas'),

('01.01.02', 'Mouse'),

('01.02', 'Monitor'),

('01.02.01', 'LCD'),

('01.02.02', 'Plasma'),

('02', 'ELETRONICOS'),

('02.01', 'Televisores');

[/sourcecode]

O que fizemos aqui foi, dentro de um banco de dados chamado ‘loja’, adicionar a tabela ‘categorias’, com os campos ‘codigo’ e ‘descricao’, exatamente como no banco legado.

Fácil, né? Agora vem a pancada!!!

PRIMEIRO PASSO: vamos conectar no banco pra saber se os dados estão lá.

[sourcecode language=”php”]

$con = mysql_connect("localhost", "usuario_do_banco", "senha_do_banco") or die ("não conecta!");

mysql_select_db("loja", $con) or die ("não seleciona!");

$sql01 = "Select codigo, descricao from categorias where 1;";

$result01 = @mysql_query($sql01, $con);

$linhas01 = @mysql_num_rows($result01);

if($linhas01==0){

echo "nenhum registro encontrado!";

exit();

}

[/sourcecode]

Ok! Ele não imprimiu nenhuma mensagem de erro.

SEGUNDO PASSO: Vamos separar o que é categoria do que é subcategoria e depois estes do que são categorias terciárias, ou, neste caso, subsubcategorias.

[sourcecode language=”php”]

for($x=0; $x<$linhas01; $x++){

$codigo_da_categoria = mysql_result($result01, $x, 'codigo');

$descricao_da_categoria = mysql_result($result01, $x, 'descricao');

$array_categoria = explode(".",$codigo_da_categoria);

$nivel_categoria = count($array_categoria);

if($nivel_categoria==1){

$categorias[]= array('id' => $array_categoria[0], 'descricao' =>  $descricao_da_categoria);

} else if($nivel_categoria==2){

$subcategorias[] = array ('id' => $array_categoria[1], 'descricao' => $descricao_da_categoria, 'categoria_pai_id' => $array_categoria[0]);

} else if($nivel_categoria==3){

$subsubcategorias[] = array ('id' => $array_categoria[2], 'descricao' => $descricao_da_categoria, 'subcategoria_pai_id' => $array_categoria[1], 'categoria_pai_id' => $array_categoria[0]);

}

}

[/sourcecode]

Agora nós temos arrays de categorias, de subcategorias e de subsubcategorias, com seus nós “pai” e “avô” bem definidos.

TERCEIRO PASSO: Cabeçalhos. Para verificar se tudo está correto, podemos imprimir os arrays na tela e conferir.

[sourcecode language=”php”]

echo "<pre>";

echo "CATEGORIAS

";

print_r($categorias);

echo "SUB-CATEGORIAS

";

print_r($subcategorias);

echo "SUB-SUB-CATEGORIAS

";

print_r($subsubcategorias);

echo "</pre>";

[/sourcecode]

Beleza!!!! Agora o QUARTO PASSO: Vamos montar o menuzinho, através de listas de HTML.

[sourcecode language=”php”]

<?php

$numero_de_categorias = count($categorias);

$numero_de_subcategorias = count($subcategorias);

$numero_de_subsubcategorias = count($subsubcategorias);

for($x=0; $x<$numero_de_categorias; $x++){

?>

<li><?php echo $categorias[$x]['descricao']; ?>

<ul>

<?php

for($y=0; $y<$numero_de_subcategorias; $y++){

if($subcategorias[$y]['categoria_pai_id']==$categorias[$x]['id']){

?>

<li><?php echo $subcategorias[$y]['descricao']; ?>

<ul>

<?php

for($z=0; $z<$numero_de_subsubcategorias; $z++){

if($subsubcategorias[$z]['categoria_pai_id']==$categorias[$x]['id'] AND $subsubcategorias[$z]['subcategoria_pai_id']==$subcategorias[$y]['id']){

?>

<li><?php echo $subsubcategorias[$z]['descricao']; ?></li>

<?php

}

}

?>

</ul>

</li>

<?php

}

}

?>

</ul>

</li>

<?php

}

?>

[/sourcecode]

O RESULTADO

Ficaria mais ou menos assim:

blog-20100810

Com um pouco de conhecimento em CSS e links você consegue transformar essa lista em um menu bem funcional.

CONCLUSÃO

Maravilha, né?

Creio que assim podemos aprender melhor não só a organizar as idéias de um banco de dados completamente zuado para essa finalidade, como também temos a oportunidade de aprender a trabalhar com arrays um pouco complexos e que não são utilizados dessa maneira com muita freqüência.

Segue abaixo o código fonte inteiro da tripa de sistema:

[sourcecode language=”php”]

<?php

$con = mysql_connect("localhost", "usuario_do_banco", "senha_do_banco") or die ("não conecta!");

mysql_select_db("loja", $con) or die ("não seleciona!");

$sql01 = "Select codigo, descricao from categorias where 1;";

$result01 = @mysql_query($sql01, $con);

$linhas01 = @mysql_num_rows($result01);

if($linhas01==0){

echo "nenhum registro encontrado!";

exit();

}

for($x=0; $x<$linhas01; $x++){

$codigo_da_categoria = mysql_result($result01, $x, 'codigo');

$descricao_da_categoria = mysql_result($result01, $x, 'descricao');

$array_categoria = explode(".",$codigo_da_categoria);

$nivel_categoria = count($array_categoria);

if($nivel_categoria==1){

$categorias[]= array('id' => $array_categoria[0], 'descricao' =>  $descricao_da_categoria);

} else if($nivel_categoria==2){

$subcategorias[] = array ('id' => $array_categoria[1], 'descricao' => $descricao_da_categoria, 'categoria_pai_id' => $array_categoria[0]);

} else if($nivel_categoria==3){

$subsubcategorias[] = array ('id' => $array_categoria[2], 'descricao' => $descricao_da_categoria, 'subcategoria_pai_id' => $array_categoria[1], 'categoria_pai_id' => $array_categoria[0]);

}

}

echo "<pre>";

echo "CATEGORIAS

";

print_r($categorias);

echo "SUB-CATEGORIAS

";

print_r($subcategorias);

echo "SUB-SUB-CATEGORIAS

";

print_r($subsubcategorias);

echo "</pre>";

?>

<hr />

<?php

$numero_de_categorias = count($categorias);

$numero_de_subcategorias = count($subcategorias);

$numero_de_subsubcategorias = count($subsubcategorias);

for($x=0; $x<$numero_de_categorias; $x++){

?>

<li><?php echo $categorias[$x]['descricao']; ?>

<ul>

<?php

for($y=0; $y<$numero_de_subcategorias; $y++){

if($subcategorias[$y]['categoria_pai_id']==$categorias[$x]['id']){

?>

<li><?php echo $subcategorias[$y]['descricao']; ?>

<ul>

<?php

for($z=0; $z<$numero_de_subsubcategorias; $z++){

if($subsubcategorias[$z]['categoria_pai_id']==$categorias[$x]['id'] AND $subsubcategorias[$z]['subcategoria_pai_id']==$subcategorias[$y]['id']){

?>

<li><?php echo $subsubcategorias[$z]['descricao']; ?></li>

<?php

}

}

?>

</ul>

</li>

<?php

}

}

?>

</ul>

</li>

<?php

}

?>

[/sourcecode]

A padronização dos nomes, atenção à escalabilidade, normalização em bancos de dados e etc vai de encontro às boas práticas do desenvolvimento de produtos de software e todas as dicas devem ser seguidas se você quiser que seu sistema tenha um mínimo de qualidade. Gambiarra muitas vezes (a maior parte) só leva um sistema a apresentar problemas de manutenção em um futuro próximo.

Vinícius Quaiato descreveu situações muito bacanas de Clean Code em seu blog “Tecnologia e algo mais” e eu acho que também vale a pena começar a correr atrás disso.

Gostaria de aproveitar para agradecer a todos os visitantes que ajudaram o post Mpdf, PHP e PDF a tornar-se o mais visitado do web tecno log, com mais de 1100 visualizações desde o dia 28/12/2009.

Um grande abraço aos visitantes!

1 Comment

  1. Giolvani

    Cara, ficou show o post…

    Como vc disse, foi bom pra gente aprender a estruturar um banco e também a usar o array de uma forma diferente…

    Parabéns bicho, flw

Leave a Comment

O seu endereço de e-mail não será publicado.