Usando a prototype – passo 7 – lendo dados em formato JSON

Em mais um texto de série sobre o uso da prototype, vou mostrar como fazer para requisitar, via AJAX, dados em formato JSON e exibí-lo na tela.

JSON
Javascript Object Notation é um dos formatos de troca de dados mais utilizados atualmente. Tem como principais vantagens ser mais leve do que outros (como XML) e seguir o modelo de dados do javascript, por isso para aplicações AJAX é altamente recomendado.

O exemplo é simples, e consiste de uma tabela num banco de dados (utilizei MySQL), uma página que lista uma série de estados e dá ao usuário a opção de buscar as cidades desse estado com o seu respectivo DDD.
Segue, abaixo, o modelo da tabela com alguns poucos dados (obviamente, esse modelo nem de longe é o ideal, mas para efeitos de estudo, se aplica, uma vez que o foco é o JSON):

CREATE TABLE IF NOT EXISTS `cidades` (
  `estado` varchar(2) character set latin1 NOT NULL,
  `cidade` varchar(128) character set latin1 NOT NULL,
  `DDD` int(2) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=DYNAMIC;
--
-- Extraindo dados da tabela `cidades`
--
INSERT INTO `cidades` (`estado`, `cidade`, `DDD`) VALUES
(´sp´, ´Sao Paulo´, 11),
(´sp´, ´Santos´, 13),
(´sp´, ´Campinas´, 12),
(´rj´, ´Rio de Janeiro´, 21),
(´rj´, ´Buzios´, 23);

 
Então, criei uma página, em PHP, que recebe por POST o id do estado e retorna a lista de cidades e seus respectivos DDDs. Para mostrar no formato JSON, primeiro fazemos a consulta (mysql_query()), então criamos um array ($rows) e, percorrendo linha a linha do resultado (mysql_fetch_assoc()), jogamos cada entrada no array. Então, usamos a função nativa do PHP json_encode()

 

 

<?php
mysql_connect("localhost", "root", "******") or die(mysql_error());
mysql_select_db("exemplos") or die(mysql_error());
$sql = "SELECT cidade, DDD FROM cidades WHERE estado=´" . $_POST["estado"] . "´";
$result = mysql_query($sql);
$rows = array(); //cria array
//intera nos resultados e adiciona item a item no array
while($row = mysql_fetch_assoc($result)) {
$rows[] = $row;
}
echo json_encode($rows); //codifica para formato JSON
?>

Feito isso, vamos criar a página principal. Nela, criamos um formulário com a action apontando para a página recém criada e montamos um select com a lista dos estados (repito que esse não é o modelo adequado, deveria haver uma tabela com a lista de estados e a tabela com a lista de cidades usar essa como chave estrangeira, mas ter feito dessa maneira não atrapalha esse exemplo).
Ai começa o uso da prototype: no onload da página, dizemos que sempre que o select sofrer o evento onchange (ou seja, alguém selecionar um item da lista), vamos fazer a requisição usando o método Request do objeto Form da prototype (nesse post eu explico como fazer isso com detalhes).
No retorno da requisição, usamos o método evalJSON() da classe Array da prototype. A partir daí, o trabalho é manipulação de dados via javascript: usando o .each() do objeto Array, percorremos a lista e podemos acessar pelo nome da coluna da tabela os itens de cada registro, criando elementos de lista li e inserindo numa lista! Aí mais uma ótima vantagem do JSON: você pode acessar os dados de uma forma bem abstrata, usando os mesmos nomes dados na base de dados como se fossem atributos de um objeto.

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Prototype com PHP - usando json</title>
<script src="http://www.prototypejs.org/assets/2008/1/25/prototype-1.6.0.2.js"></script>
<script type="application/javascript">
    //no onload da página, cria listener para o select dos estados
    Event.observe(window , "load", function() {
        Event.observe($("estados"), "change", function() {
            //chama método para listar as cidades a partir do estado selecionado
            $("buscaCidades").request({ 
                onComplete: function(transport){
                    //decodifica JSON
                   
                    var list = $("cidades");
                    //limpa a lista
                    list.innerHTML = "";
                    //percorre lista de cidades com seu DDD usando o iterator each da classe Array da prototype
                    transport.responseText.evalJSON(true).each(function(city) {
                        var li = new Element("li").update(city.cidade + " (" + city.DDD + ")");
                        list.insert(li);
                    });
                }
            });
           
        });
       
    });
   
</script>
</head>
<body>
<h1>Teste para trazer lista de cidades com o seu DDD a partir de um estado</h1>
<form id="buscaCidades" action="cidadeddd_json.php" method="POST">
    <label for="estados">estado:</label>
    <select name="estado" id="estados">
    <option value="-1">selecione um estado...</option>
    <?php
    //conecta ao banco e retorna a lista de estados a partir da tabela de cidades
    mysql_connect("localhost", "root", "******") or die(mysql_error());
    mysql_select_db("exemplos") or die(mysql_error());
   
    $sql = "SELECT estado FROM cidades GROUP by estado";
   
    $result = mysql_query($sql);
    while($row = mysql_fetch_array( $result )) {
    ?>
    <option value="<?php echo $row["estado"];?>"><?php echo $row["estado"]; ?></option>
    <?php
    }
    ?>
    </select>
</form>
<h2>Lista de cidades e seu DDD</h2>
<ul id="cidades">
</ul>
</body>
</html>
 

Setando o encoding no envio de e-mail por PHP

Quando se usa a função mail() do PHP, erros nos caracteres especiais – por exemplo, caracteres acentuados – podem aparecer no cliente – ou o webmail ou o software – que visualiza a mensagem.

A maneira mais simples de não passar por esse problema é usando o charset UTF-8. Esse charset é um dos que abrange a maioria dos caracteres usados.

A aplicação é simples: devse setar o charset UTF-8 no quarto parâmetro da função, que são os headers enviados para ela.

 <?php
    $recipient = "[email protected]"; //recipient
    $html = "<html><body>Teste</body></html>";

    $subject = "assunto";
    $header = "Content-type: text/html; charset=UTF-8 ";
    mail($recipient, $subject, $html, $header) or die("<p>Erro ao enviar a sua mensagem. Tente novamente, por favor.</p>"); //mail command 🙂
    echo("<p>Sua mensagem foi enviada com sucesso.</p>");
?>

Lumine – um exemplo prático de uso

Nesse post eu falei sobre o Lumine, um framework em PHP para persistência de dados (nos moldes do Hibernate para o Java e o DB_DataObjects do PEAR).
Resolvi dar uma para em uns projetos e realmente experimentar em algo que eu precisava, e me dediquei a abandonar as querys SQL. E funcionou.
Vou, abaixo, mostrar como fazer para persistir os dados postados por um formulário numa tabela de uma base (no caso, MySQL).

Vamos supor que você tenha uma tabela no banco chamada messages com as seguintes colunas:

id – tinyint, chave-primária e autoincrement
name – varchar, 64
email – varchar, 64
msg – varchar, 512
publishDate – date

Como seguinte script, cria-se a tabela:

CREATE TABLE `messages` (
  `id` tinyint(4) NOT NULL auto_increment,
  `name` varchar(64) NOT NULL,
  `email` varchar(64) NOT NULL,
  `msg` varchar(512) NOT NULL,
  `parentId` tinyint(4) NOT NULL,
  `publishDate` date NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

O html que contém o formulário (no caso, um formulário em que um usuário posta o nome, e-mail e uma mensagem) é bem simples e já abordei essa técnica usando a biblioteca prototype (sinta-se livre pra usar qualquer outro framework, ou outra técnica; não influencia no resultado final) em outros posts(aqui e aqui):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Upload de mensagem</title>
<script src="http://www.prototypejs.org/assets/2008/1/25/prototype-1.6.0.2.js"></script>
<script type="text/javascript">
    function upload() {
        $("message").update("<img src=´loader.gif´> aguarde, enviando mensagem...");
        $("form_upload").request({
          onComplete: function(transport){
            if(transport.responseText=="")
                $("form_upload").reset();
                $("message").update("Sua mensagem foi enviada com sucesso... obrigado!");
            }
               
        });
        return false;
    }
</script>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="box">
<h1>Envio de mensagem</h1>
<form action="saveMsg.php" method="post" enctype="application/x-www-form-urlencoded" id="form_upload" onsubmit="return upload();">
    <label for="name">nome:</label><input type="text" id="name" name="name" class="field" /><br /><br />
    <label for="email">e-mail:</label><input type="text" id="email" name="email" class="field" /><br /><br />
    <label for="msg">mensagem:</label><textarea id="msg" name="msg"></textarea><br />
    <input type="submit" value="enviar" class="submitButton" />
    <span id="message"></span>
</form>
<div id="msgs_area"></div>
</div>
</body>
</html>

Agora, o passo mais importante: modelar (exemplo: http://www.hufersil.com.br/documentacao/instanciando) uma classe seguindo a estrutura da sua tabela. No arquivo Message.php eu criei a classe, extendendo a classe do Lumine, mapeando os atributos relativos à tabela.

<?php
class Message extends Lumine_Base{
    protected $_tablename = ´messages´;
    protected $_package   = ´entidades´;
    protected function _initialize() {
        $this->_addField(´id´, ´id´, ´tinyint´, 4, array(´primary´ => true, ´notnull´ => true, ´autoincrement´ => true));
        $this->_addField(´name´, ´name´, ´varchar´, 64, array());
        $this->_addField(´email´, ´email´, ´varchar´, 64, array());
        $this->_addField(´publishDate´, ´publishDate´, ´date´,null , array());
        $this->_addField(´msg´, ´msg´, ´varchar´, 512, array());
        $this->_addField(´parentId´, ´parentId´, ´tinyint´, 4, array(´primary´ => false, ´notnull´ => true, ´autoincrement´ => false));
    }
}
?>

Então, o passo final é criar o arquivo que recebe o post do formulário (no caso, saveMsg.php).
Nesse arquivo, o que fazemos: chamamos a classe Lumine.php (que vem no pacote de instalação do Lumine), configuramos a aplicação (http://www.hufersil.com.br/documentacao/lumine_conf), depois atribuímos os valores recebidos por POST a cada parâmetro enviado pelo formulário (e pegamos a dataa atual para salvar no banco) e, por final, chamamos o método insert() que faz salvar os dados na tabela do banco.
Segue o código:

<?php
require "../lumine/Lumine.php";
require "lumine_config.php";
$cfg = new Lumine_Configuration( $lumineConfig );
require "Message.php";
//cria classe Message
$msg = new Message;
//decodifica os pareâmetros enviados, para evitar problemas com acentuaçãao
foreach ($_POST as $key => $valor ){
   $_POST[$key] = utf8_decode($valor);
}
$msg->name = $_POST["name"];
$msg->email = $_POST["email"];
$msg->msg = $_POST["msg"];
$msg->publishDate=date(DATE_RFC822);
$msg->insert();
?>

Passei batido por alguns detalhes aqui relativos à configuração, mas a documentação oficial é muito boa – e o próprio ato de configurar é algo bem simples para quem está na área de desenvolvimento.
E, olhando assim por cima, parece que há muito trabalho para configurar tudo isso para fazer apenas uma inserção no banco; mas, imaginem só configurar uma vez só, e depois acabar com todas querys de inserts, updates, selects, etc… Muito bom, aprovado!

 

Site oficial: http://www.hufersil.com.br/documentacao/apresentacao

Usando a Prototype – passo 4 – upload de arquivo sem post na tela

Segue abaixo um exemplo de como fazer upload de um arquivo, através de um formulário, sem precisar dar post na página.
Como funciona?
Um form normal de upload de arquivo que tem no onsubmit chamada a função javascript. Essa, por sua vez, cria um iframe em tempo de execução, muda o target do form para esse iframe e faz o upload do form.
O arquivo de destino (nesse caso usei um .php, mas independe de qual linguagem para gerenciar esse upload você quer usar) tem no onload uma chamada à função javascript do parent dele que faz informar na tela que o upload foi efetuado. Trocandando em miúdos, o upload é feito da mesma forma que sempre foi feito, e quando a página é carregada no iframe – é o momento em que o upload do arquivo foi feito e a página processada -, por javascript se faz atualizar a página pai.

Segue abaixo o código das duas páginas, comentado (usei a biblioteca Prototype para ajudar no javascript):

index.htm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Exemplo de upload</title>
<script src="http://www.prototypejs.org/assets/2008/1/25/prototype-1.6.0.2.js"></script>
<script type="text/javascript">
    function upload() {
        //cria um iframe, escondido
        var iframe = new Element("iframe", {name:"target", id:"iframe_upload"});
        //insere na página o iframe
        document.body.insert(iframe);
        //muda o target do form, apontando para o iframe
        $("form_upload").target = iframe.name;
        //mostra a mensagem
        $("message").update("aguarde, enviando arquivo...");
    }
   
    function closeIframe() {
        //atualiza a mensagem
        $("message").update("arquivo enviado com sucesso");
        //apaga o iframe
        $("iframe_upload").remove();
    }
</script>
<style type="text/css">
    iframe {display:none;}
</style>
</head>
<body>
<h1>Exemplo de upload</h1>
<form action="upload.php" method="post" enctype="multipart/form-data" id="form_upload" onsubmit="return upload();">
    <input type="file" name="arquivo" id="arquivo" />
    <input type="submit" value="enviar" />
    <span id="message"></span>
</form>
</body>
</html>

 

upload.php

<?php
//faz todo o processamento necessário no PHP para persistir o arquivo
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Upload - OK</title>
</head>
<!-- chama função javascript da página pai -->
<body onload="parent.closeIframe();">
</body>
</html>

Basicamente, isso é o que a maioria dos sites que têm formulários de upload estão fazendo; é o que chamam, por aí, de upload 2.0. Não sei se concordo muito com o termo usado, mas faz o mesmo efeito.
Vale ressaltar que muitas bibliotecas javascript fazem isso, inclusive com plugins bem maduros. Mas, de qualquer modo, vale a pena saber como funcionam esses plugins, entender qual a dinâmica e o conceito.
Em breve mostro outra forma de fazer upload com barra de progresso – tanto com uma linguagem server-side quanto usando Flash (e Action Script 3).

 

Lumine – mapeamento de banco de dados para PHP

Quando entrei no meu atual emprego, quase 4 anos atrás, uma cosia que eu não conhecia mesmo era esse lance de frameworks.
Talvez pela mistura de ser novato em programação web (conhecia bem html/css e um pouco de php) e ter trabalhado com programação c/c++, onde os programadores prezam muito o fato de se conhecer como as coisas funcionam – e não só fazer funcionar.

No fundo, eu concordo: não acho o ideal já partir de cara para o uso de frameworks, sem nem saber como as coisas são feitas na unha. Até porque, quando aparece algum problema no desenvolvimento, na maior parte das vezes temos que mudar algo no framework, ou fazer um work-around indo lá no meio do código – ou seja, fazer o trabalho sujo de catar milho e escrever código.

Um dos frameworks que mais me chamou atenção foi o Hibernate. É um framework para aplicações java, para persistêcnia de dados. Basicamente, você persiste objetos (sim, objetos, aqueles, do conceito da Orientação a Objetos) em bancos de dados sem ter que se preocupar em como isso vai funcionar; ao invés de ter que ficar escrevendo querys SQL de consulta, inserção, deleção, etc… você chama apenas um método específico que os objetos herdam, e o framework faz todo o trabalho.
Qual a vantagem? Bancos de dados SQL possuem uma estrutura totalmente diferente da forma como os dados existem na orientação a objetos. Fazer código para manipular os dados é custoso – tanto em termos de tempo quanto de trabalho. Quando o framework se propõe a fazer isso, o ganho de produtividade é gigantesco. Sem falar que, com isso, mudar de um banco de dados para o outro é bem mais simples, pois normalmente frameworks desse tipo possuem tal suporte.

Depois de ter conhecido esse framework, fiquei um tempo pensando se não tinha nada parecido em PHP (que é a linguagem server-side para web que mais conheço). Foi quando me deparei com o Lumine. Ele surgiu depois que um programador PHP conheceu também o Hibernate e sentiu necessidade de fazer o mesmo para PHP.
*não conheço o autor desse framework, mas já deixo aqui meus parabéns pra ele
A configuração dele é muito fácil: você escolhe qual banco de dados vai usar (por enquanto pode usar MySQL e PostGree) e define as strings de conexão (login, senha, etc…).
Depois, faz o mapeamento das classes com relação às tabelas do banco. Ou seja, você específica que uma classe (por exemplo Pessoa) estará relacionada a uma tabela do bando (chamada tbl_pessoa) e que os atributos (por exemplo nome, idade, sexo) estarão relacionados a colunas específicas da tabela.
Feito isso, você pode usar os métodos específicos para manipulação dos dados, como inserir uma nova Pessoa:

// recupera o arquivo que faz a configuração de lumine
// descrita anteriormente em "inicializando a configuração"
require_once ´configuracao.php´;
// Importa a classe pessoa
Util::Import(´entidades.Pessoa´);
// instancia a classe pessoa
$pessoa = new Pessoa;
// atribui propriedades
$pesso->nome = ´Hugo´;
$pessoa->idade = 23;
$pessoa->cpf = ´12345678912´;
// persiste o objeto no banco, usando insert
$pessoa->insert();

 

Ou recuperar do banco:

// instancia um novo objeto
$pessoa = new Pessoa;
// recupera o objeto com código 1 pela primeira chave primária da classe
$total = $pessoa->get( 1 );

 

Fica muito mais fácil pois você deixa de ter que misturar as querys SQL no meio do seu código PHP; o desacoplamento é muito maior, e a legibilidade é bem melhor: faz muito mais sentido você ver no código Pessoa->insert() do que toda aquela linguiçona SQL…
Minha sugestão? Quando tiver um tempo, baixe esse framework e experimente. Explore. Há uma curva de aprendizado, claro, e no começo a produtividade cairá um pouco. Mas, depois, a agilidade que ele te dá valerá a pena 🙂