PDO – classe para acesso a diversos tipos de banco de dados

Quando comecei a programar em PHP, aprendi como fazer requisições a banco de dados MySQL usando as funções nativas do PHP (mysql_connect(), mysql_query(), etc…), as chamadas MySQL Functions.

Muito se passou, o PHP estava na versão 3, e hoje em dia está na 5 (quando sai a 6, mesmo?!?!). Muitas coisas mudaram, e o PHP possui agora características de Orientação a Objetos muito boas. E com isso foi possível criar uma interface simples e leve para acesso a banco de dados, chamada de PHP Data Object, ou PDO.
O que o PDO faz é ser um intermediador para que vocé possa acessar vários tipos de bases de dados (MySQL, PostGree, Firebird, SQL Server, Oracle, etc…) com a mesma sintaxe, alterando apenas o driver usado na conexão. Ou seja, não é ele quem faz a conversa com a base de dados; ele procura quem é que vai conversar com aquele tipo de base de dados.

Um exemplo prático de insert em uma tabela de um banco MySQL:

<?php
$fullName = $_POST["fullName"];
$childrensName = $_POST["childrensName"];
$age = $_POST["age"];
$cpf = $_POST["cpf"];
$hobbies = $_POST["fullName"];

$db = new PDO("mysql:host=localhost;dbname=mydb", admin", "root");
$stmt = $db->prepare("INSERT INTO user (fullName, childrensName, age, cpf, hobbies) VALUES ("$fullName","$childrensName","$age","$cpf","$hobbies")");
$stmt->execute();
?>

Se fosse necessário usar outro banco, por exemplo um Firebird, seria necesseario alterar apenas a linha que faz a instanciação do objeto PDO, usando o driver adequado.

Esse texto não visa ser um tutorial de como usar, mas sim uma apresentação dessa classe muito útil e, certamente, elegante!

http://br.php.net/manual/en/book.pdo.php

Exibindo arquivos de um diretório em PHP

Todas as vezes em que precisei listar arquivos de um diretório – em PHP usava as funções opendir() e readdir(). Há muito tempo não precisava fazer isso, e hoje me deparei com essa necessidade.
Fui procurar a documentação e encontrei uma outra função que nunca tinha visto, a glob(). Só é necessário passar o caminho fisíco do diretório e ele retorna um array de entradas com arquivos e diretórios.

O uso é simples:

<!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>Listando arquivos</title>
</head>
<body>
<ul>
<?php
    //define o caminho do diretório
    $dir = "/Applications/MAMP/htdocs/exemplos/files/";
    //listar arquivos
    $files = glob($dir."/*") or die("Erro ao acessar " . $dir);
    //permorre a lista
    foreach($files as $file) {
        if (!is_dir($file)){
?>
    <li><a href="<?php echo $file;?>" title="<?php echo $file;?>"><?php echo $file;?></a></li>
<?php
        }
    }
?>
</ul>
</body>
</html>

Construindo uma classe PHP para acessar a API do BlogBlogs

Postei nesse último final-de-semana uma classe em PHP que fiz para acessar a API do BlogBlogs de uma forma fácil.
A API do BlogBlogs retorna através da arquitetura REST arquivos XML que representam informações do perfil de um usuário do BlogBlogs e de blogs cadastrados no sistema.

Download da classe aqui!

Primeiramente, criei duas classes, que abstraem esses dados em forma de objetos. Uma classe é a BlogBlogsBlog, um bean para a entidade blog, e outra a BlogBlogsUser, que por sua vez é um bean para o usuário do sistema. Cada classe recebe no construtor os atributos.

/**
* Dados do usuário do BlogBlogs
*/
class BlogBlogsUser {
    function __construct($username, $firstname, $lastname, $fullname, $thumbnailpicture, $profilepage, $blogs, $favorites, $fans) {
        $this->userName = $username;
        $this->firstName = $firstname;
        $this->lastName = $lastname;
        $this->fullName = $fullname;
        $this->thumbnailPicutre = $thumbnailpicture;
        $this->profilePage = $profilepage;
        $this->blogsCount = $blogs;
        $this->favoritesCount = $favorites;
        $this->fansCount = $fans;
        $this->blogs = array();
    }
   
}

/**
* Dados de um blog do BlogBlogs
*/
class BlogBlogsBlog {
    function __construct($name, $url, $rssurl, $blogpage, $lastupdate, $inboundblogs, $inboundlinks, $rank, $lang) {
        $this->name = $name;
        $this->url = $url;
        $this->rssUrl = $rssurl;
        $this->blogPage = $blogpage;
        $this->lastUpdate = $lastupdate;
        $this->inboundBlogs = $inboundblogs;
        $this->inboundLinks = $inboundlinks;
        $this->rank = $rank;
        $this->lang = $lang;
    }
}

Com isso, podia continuar a implementação. Criei a classe BlogBlogsService. Guardei no atributo apiUrl a url da API, e no construtor da classe recebo o valor da chave da API do BlogBlogs (semelhante ao processo de se usar a API do Google Maps, por exemplo).
Então criei alguns métodos, e os que são úteis para o desenvolvedor:

  • setUserName(): seta o username do usuário pelo qual faremos a busca no sistema do BlogBlogs. O método automaticamente busca todos os blogs e os dados do perfil do usuário
  • getBlogByUrl(): faz a busca por um blog através da sua url

Assim, temos implementadas todas as buscas possíveis pela API.

class BlogBlogsService {
    private $apiUrl = "http://api.blogblogs.com.br/api/rest/";
    private $userKey;
    private $doc;
   
    /**
    * construtor: recebe a chave da API do BlogBlogs
    * para gerar, acesse http://blogblogs.com.br/dev/chave
    */
    function __construct($k) {
            $this->userKey = $k;
            $this->user = null;
    }
   
    /**
    * recebe o userName no blogblogs e faz a consulta
    */
    function setUserName($userName) {
        $path = $this->apiUrl . "userinfo?key=" . $this->userKey . "&username=" . $userName;
        $this->doc = new DOMDocument();
        $this->doc->load($path);
        //verifica erros: caso tenha um nó com namespace error
        if($this->doc->getElementsByTagName("error")->length==1) {
            $this->errorMsg = $this->doc->getElementsByTagName("error")->item(0)->nodeValue;
            throw new Exception($this->errorMsg);
            return false;
        }
       
   
        if($this->doc->getElementsByTagName("result")->length!=1)
            return;
        //dados do usuário
        $userset = $this->doc->getElementsByTagName("result")->item(0);
        $blogBlogsUser = new BlogBlogsUser($userset->getElementsByTagName("username")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("firstname")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("lastname")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("fullname")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("thumbnailpicture")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("profilepage")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("blogs")->item(0)->nodeValue,
                                              $userset->getElementsByTagName("favorites")->item(0)->nodeValue,
                                           $userset->getElementsByTagName("fans")->item(0)->nodeValue);
        //dados de cada blog
        foreach($this->doc->getElementsByTagName("weblog") as $blog) {
            $blog = $this->createBlogWithXMLNode($blog);
            //$blogBlogsUser->addBlog($blog);
            $this->addBlogToUser($blogBlogsUser, $blog);
        }
        $this->user = $blogBlogsUser;
        return true;
    }
   
    /**
    * recupera um blog por uma url
    */
    function getBlogByUrl($url) {
        $path = $this->apiUrl . "bloginfo?key=" . $this->userKey . "&url=" . $url;
        $this->doc = new DOMDocument();
        $this->doc->load($path);
        if($this->doc->getElementsByTagName("weblog")->length!=1)
            return;
        $blog = $this->doc->getElementsByTagName("weblog")->item(0);
        $myBlog = $this->createBlogWithXMLNode($blog);
        return $myBlog;
   
    }
   
    function createBlogWithXMLNode($node) {
        $blog = new BlogBlogsBlog($node->getElementsByTagName("name")->item(0)->nodeValue,
                                  $node->getElementsByTagName("url")->item(0)->nodeValue,
                                  $node->getElementsByTagName("rssurl")->item(0)->nodeValue,
                                  $node->getElementsByTagName("blogpage")->item(0)->nodeValue,
                                  $node->getElementsByTagName("lastupdate")->item(0)->nodeValue,
                                  $node->getElementsByTagName("inboundblogs")->item(0)->nodeValue,
                                  $node->getElementsByTagName("inboundlinks")->item(0)->nodeValue,
                                  $node->getElementsByTagName("rank")->item(0)->nodeValue,
                                  $node->getElementsByTagName("lang")->item(0)->nodeValue);
   
        return $blog;
    }
   
    function addBlogToUser($user, $blog) {
        array_push($user->blogs, $blog);
    }
}

Vamos ver um passo-a-passo de como ver um atributo de um usuário:

$minha_chave = “XXXXXXXXXXXXXXX”;
$bbService = new BlogBlogsService($minha_chave);
$bbService->setUserName("ChrisB");
echo $bbService->user->fullName;

Se quisermos listar os blogs dele, exibindo as urls dos mesmos, acessamos a lista de BlogBlogsBlog que o BlogBlogsUser possue:

foreach($bbService->user->blogs as $blog) {
$blog->url . “<br />”;
}

Ainda podemos fazer a busca diretamente po um blog, pela sua url:

$bbService->getBlogByUrl(”http://www.chrisb.com.br/blog/”)
//para exibir o nome do blog
$bbService->getBlogByUrl(”http://www.chrisb.com.br/blog/”)->name;

Creio que os exemplos, mesmo simples, mostram a facilidade de uso. []s!

Lendo um RSS e exibindo-o na tela – por PHP

Estou voltando á programação em PHP depois de muitos meses sem fazer muitas coisas. Vou mostrar aqui como ler um RSS (2.0) e gerar um html a partir dele, usando orientação a objetos. Para ajudar na leitura, vale a pena comentar que eu usei o termo Feed para o arquivo XML lido e FeedEntry cada entrada (num RSS, abstraímos isso normalmente para uma notícia ou matéria).

Criei três arquivos:

  • uma classe que segue o conceito de JavaBean para armazenar os dados de cada FeedEntry
  • uma classe que funcionaria como um service: lê o XML e cria a partir dele uma lista de FeedEntries
  • um arquivo PHP que instancia a classe de serviço e gera adequadamente o código html

 O código da classe FeedEntry.php

 

<?php
class FeedEntry {
    private $title;
    private $description;
    private $pubDate;
    private $link;
   
    function __construct($title, $description, $pubDate, $link) {
        $this->title = $title;
        $this->description = $description;
        $this->pubDate = date("d-M-Y", strtotime($pubDate));
        $this->link = $link;
    }
   
    public function getTitle() {
        return $this->title;
    }
   
    public function getDescription() {
        return $this->description;
    }
   
    public function getPubDate() {
        return $this->pubDate;
    }
   
    public function getLink() {
        return $this->link;
    }
}
?>

 

Eis o código da classe de serviço (FeedService.php), devidamente comentado:

 

<?php
require("FeedEntry.php");
class FeedService {
    private $file;
    private $xml;
    private $doc;
    private $feedEntries = array();
   
    /* construtor: recebe a URL do feed e instancia objeto DOMDocument */
    function __construct($feedUrl) {
        $this->file = $feedUrl;
        $this->doc = new DOMDocument();
    }
   
    /**
    * carrega o arquivo XML do feed
    *
    */
    private function readXML() {       
        $this->doc->load($this->file);
    }
   
    /**
    * lê os nós
    */
    private function readNodes() {
       
        /**
        * percorre o objeto do tipo DOMDocument através do método getElementsByTagName()
        * cria um objeto do tipo FeedEntry e adiciona no array
        *
        * cada feed é declarado no RSS com uma tag <item> e tem:
        * uma tag <title> para o título do feed
        * uma tag <descritpion> para a descrição do feed
        * uma tag <pubDate> para a data de publicação do feed
        * uma tag <link> para o link do feed
        */
        $dataset = $this->doc->getElementsByTagName("item");
        foreach($dataset as $feed) {
            $feedEntry = new FeedEntry($feed->getElementsByTagName("title")->item(0)->nodeValue,
                                       $feed->getElementsByTagName("description")->item(0)->nodeValue,
                                       $feed->getElementsByTagName("pubDate")->item(0)->nodeValue,
                                       $feed->getElementsByTagName("link")->item(0)->nodeValue);
            array_push($this->feedEntries, $feedEntry);
        }
    }
   
    /**
    * chama métodos que lê o XML, percorre os nós transformando-os em objetos FeedEntry e retorna a lista
    * desses objetos
    */
    public function consume() {
        $this->readXML();
        $this->readNodes();
        return $this->feedEntries;
    }
}
?>

 

E, finalizando, o código da página PHP (chamei de consume.php) que vai consumir esse RSS:

 

<!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>Exemplo - consumo de um RSS</title>
</head>
<body>
<h1>Exemplo de uso da classe FeedService</h1>
<dl>
<?php
    //inclui Service que faz a manipulação de Feed
    require("FeedService.php");
    //instancia objeto do tipo
    $feedService = new FeedService("http://feeds.feedburner.com/AndAfter");
    $feedEntries = $feedService->consume(); //método do service retorna um array de objetos FeedEntry
    foreach($feedEntries as $feedEntry) { //percorre a lista de feeds, retornando a cada iteração objetos do tipo FeedEntry
?>
    <dt><a href="<?php echo $feedEntry->getLink(); ?>"><?php echo $feedEntry->getTitle() . " (" . $feedEntry->getPubDate() . " )"; ?></a></dt>
    <dd><?php echo $feedEntry->getDescription(); ?></dd>
<?php
    }
?>
</dl>
</body>
</html>
 

 

Tentei separar um pouco as canadas, mas isso nem de perto chega no conceito de MVC. Mas é um começo, já criando um bean e uma classe específica para acessar dados – note que não há mistura no FeedService de marcação html, ou seja, essa classe pode ser utilizada normalmente em qualquer outro projeto.

Para facilitar, coloquei os 3 arquivos para baixar: http://www.chrisb.com.br/files/consumeFeed.zip
É só descompactar no mesmo diretório e acessar, que funcionará.

Velocity – linguagem de templates

Quando comecei a trabalhar na empresa em que estou até hoje, 4 anos atrás, fui "jogado" no mundo de aplicações java para web. Antes, só tinha mexido de verdade com PHP. E de cara comecei a mexer com o que, hoje em dia, é muito comum: templates.

Existem inúmeras opções de frameworks que usam templates, para as mais variadas linguagens (inclusive foi lançado um, em PHP, que parece ser bem bacana, o Spaghetti).
Em java, muito se usa JSP. Mas nem de longe são os mais bacanas: se você quer implementar uma aplicação MVC mesmo, deve usar o JSTL ou Velocity.

E Velocity é com o que eu trabalho atualmente. Ela é ótima para uma aplicação de grande porte, que usa MVC: você escreve seu XHTML normalmente e, depois, utiliza os objetos que a camada de negócios te dá. Ou seja, você coloca apenas lógica de interface na interface. E a lógica de negócios fica na camada de negócios. E não tem como misturar uma coisa com a outra (ok, até tem, mas vai dar tanto trabalho e é tão mais difícil de fazer isso quando comparado com JSP, que desestimula qualquer POG…).

Vamos falar um pouco, então, dele:




Velocity

Conceito

É um engine baseado em java para a construção de templates. Um template velocity é um documento que permite a escrita de html com a inserção de lógica usando objetos do Java na sua inteligência, aplicando o conceito de MVC para separar a camada de negócios da camada de apresentação.

Tipos

Sendo baseado em Java, o código Velocity tem os mesmos tipos de objetos do Java, e podem ser usados todos os métodos dos objetos Java. Objetos são sempre chamados através do identificador $
Por exemplo:
$nome ou $quantidade
Para definir um valor a um objeto, é usada a diretiva #set(
objeto = valor)
Não é necessário definir o tipo do objeto. O engine do Velocity se encarrega de definir a qual tipo ele pertence.
Por exemplo: #set($nome = "Eduardo") ou #set($quantidade = 5)

Arrays

Para criar um array – um objeto que contêm uma lista de outros objetos, há duas formas:
#set($meu_array = ["a", "b", "c", "d", "e"]) ##instancia-se o objeto e já passa o valor a ele
ou

#set($meu_array = []) ##instancia um objeto e cria uma lista vazia
##adiciona item a item os objetos da lista, usando o método add() da classe Array
#set($foo = $meu_array.add("a"))
#set($foo = $meu_array.add("b"))
#set($foo = $meu_array.add("c"))
#set($foo = $meu_array.add("d"))
#set($foo = $meu_array.add("e"))
##como o método add() tem um valor de retorno, jogamos o valor do método em uma variável qualquer, $foo, para que esse valor não seja printado na tela

Tomada de decisão

Há um tipo de estrutura de decisão no Velocity, que é definida pela seguinte sintaxe:
#if(condição)

#elseif(condição)

#else

#end

Nesse caso, apenas a diretiva #if(condição) e #end são obrigatórios; as demais ficam por conta da necessidade da implementação. A lógica é a mesma de qualquer estrutura de outras linguagens. Segue um exemplo completo:
#if($c<5)
##só entra quando $c for menor do que cinco
#elseif($c==5)
##só entra quando $c for igual a c
#else
##entre nos demais casos
#end

Laço de repetição

Há um tipo de estrutura para laço de peteição no Velocity – ou seja, como percorrer um Array (lista), que é definida pela seguinte sintaxe:
#foreach($iter in $lista)

#end

Usando aquele Array criado no item Arrays, segue um exemplo completo da utilização:
#set($meu_array = ["a", "b", "c", "d", "e"])
#foreach($letra in $meu_array)
$letra ##printa na tela a letra
#end

É possível também criar uma repetição de n vezes. Por exemplo, no código abaixo serão feitas 3 iterações (de 1 até 3):
#foreach($c in [1..3])
$c
#end

Macro
Uma macro pode ter seu conceito simplificado para
função carregada em memória. Deve ser utilizada quando um trecho de código é repetido várias vezes em diversos locais do seu código. Por ser carregada em memória e não interpretada em tempo de execução, a performance é maior.
A sintaxe para criação e posterior acesso a ela segue o seguinte padrão:
#macro(meu_nome $param1 $param2 .. $paramn)
##lógica da macro
#end
##chama a macro
#meu_nome($p1 $p2 .. $pn)

Vale ressaltar que n é a quantidade de parâmetros que a macro recebe. Se ela não receber nenhum, é chamada a macro com os parênteses em branco.

Referência: User Guide