Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

João Batista Neto

Uma classe Datagrid totalmente abstrata e reutilizável

Recommended Posts

Apesar da grande quantidade de classes Datagrid que é possível encontrar, inclusive aqui no fórum, eu resolvi escrever uma mesmo assim.

A idéia principal era ter uma classe Datagrid totalmente abstrata, tanto na camada de modelo de dados como na camada de apresentação, por isso

utilizei a classe PDO do PHP para conseguir a abstração com o banco de dados e utilizei XSL para a criação dos temas para a Datagrid, permitindo

assim a reusabilidade total da classe.

 

Para utilizar essa classe é necessário primeiro definir o modelo de dados, que pode ser uma matriz ou então o resultado de uma consulta:

 

1. Utilizando uma matriz:

array(
    array( "Nome" => "João Neto" , "Endereço" => "Rua de teste, 666" , "Telefone" => "00 0000-0000" ),
    array( "Nome" => "Teste"     , "Endereço" => "Rua dos bobos, 0"  , "Telefone" => "11 1111-1111" )
)

Nesse modelo temos três colunas, Nome, Endereço e Telefone, esses serão os títulos da nossa Datagrid...

 

$tabela = new Datagrid();
$tabela->setModel( array(
    array( "Nome" => "João Neto" , "Endereço" => "Rua de teste, 666" , "Telefone" => "00 0000-0000" ),
    array( "Nome" => "Teste"     , "Endereço" => "Rua dos bobos, 0"  , "Telefone" => "11 1111-1111" )
) );

echo $tabela;

O código acima irá produzir:

 

<table>
    <summary />
    <thead>
        <tr>
            <th>Nome</th>
            <th>Endereço</th>
            <th>Telefone</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>João Neto</td>
            <td>Rua de teste, 666</td>
            <td>00 0000-0000</td>
        </tr>
        <tr>
            <td>Teste</td>
            <td>Rua dos bobos, 0</td>
            <td>11 1111-1111</td>
        </tr>
    </tbody>
</table>

Perceba que nossos caracteres especiais, ç e ã foram automaticamente convertidos para ç e ã.

Agora definindo o valor da tag summary:

 

$tabela = new Datagrid();
$tabela->summary = "Lista de Clientes";
$tabela->setModel( array(
    array( "Nome" => "João Neto" , "Endereço" => "Rua de teste, 666" , "Telefone" => "00 0000-0000" ),
    array( "Nome" => "Teste"     , "Endereço" => "Rua dos bobos, 0"  , "Telefone" => "11 1111-1111" )
) );

echo $tabela;

Irá produzir:

 

<table>
    <summary>Lista de clientes</summary>
    <thead>
        <tr>
            <th>Nome</th>
            <th>Endereço</th>
            <th>Telefone</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>João Neto</td>
            <td>Rua de teste, 666</td>
            <td>00 0000-0000</td>
        </tr>
        <tr>
            <td>Teste</td>
            <td>Rua dos bobos, 0</td>
            <td>11 1111-1111</td>
        </tr>
    </tbody>
</table>

Porém, a maioria das vezes os dados vêm de uma consulta no banco de dados, então vamos utilizar a seguinte tabela para popular nosso Datagrid:

 

mysql> CREATE TABLE `clientes` (
    ->   `id_cliente` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    ->   `nome_cliente` varchar(80) NOT NULL,
    ->   `endereco_cliente` varchar(100) NOT NULL,
    ->   `telefone_cliente` varchar(20) DEFAULT '',
    ->   PRIMARY KEY (`id_cliente`)
    -> ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.00 sec)

Agora vamos inserir alguns registros:

 

mysql> insert into `clientes`(`nome_cliente`,`endereco_cliente`,`telefone_cliente`) values
    ->     ('João Neto','Rua de teste, 666','00 0000-0000'),
    ->     ('Teste' , 'Rua dos bobos, 0' , '11 1111-1111');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

Agora que já temos os dados, vamos utilizar a classe PDO para fazer a conexão e recuperar os dados:

 

try {
    $tabela = new Datagrid();
    $tabela->summary = "Lista de clientes";
    $pdo = new PDO( "mysql:host=host;dbname=database" , "user" , "password" );

    if ( ( $res = $pdo->query( "SELECT * FROM `clientes`;" ) ) instanceOf PDOStatement ){
        $tabela->setModel( $res ); //Aqui definimos como modelo de dados o resultado da consulta
    }

    echo $tabela;
} catch ( PDOException $e ){
    printf( "Não foi possível conectar\nErro[ %d ]: %s\n" , $e->getCode() , $e->getMessage() );
}

A saída será:

 

<table>
    <summary>Lista de clientes</summary>
    <thead>
        <tr>
            <th>id_cliente</th>
            <th>nome_cliente</th>
            <th>endereco_cliente</th>
            <th>telefone_cliente</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>1</td>
            <td>João Neto</td>
            <td>Rua de teste, 666</td>
            <td>00 0000-0000</td>
        </tr>
        <tr>
            <td>2</td>
            <td>Teste</td>
            <td>Rua dos bobos, 0</td>
            <td>11 1111-1111</td>
        </tr>
    </tbody>
</table>

Como é possível perceber, nossos cabeçalhos estão utilizando os nomes das colunas da tabela e por isso ficaram "feios", vamos melhorar nossa consulta

dando apelidos para os campos:

 

try {
    $tabela = new Datagrid();
    $tabela->summary = "Lista de clientes";
    $pdo = new PDO( "mysql:host=host;dbname=database" , "user" , "password" );
    $sql = 'SELECT `nome_cliente` "Nome do Cliente", `endereco_cliente` "Endereço", `telefone_cliente` "Telefone" FROM `clientes`;';

    if ( ( $res = $pdo->query( $sql ) ) instanceOf PDOStatement ){
        $tabela->setModel( $res ); //Aqui definimos como modelo de dados o resultado da consulta
    }

    echo $tabela;
} catch ( PDOException $e ){
    printf( "Não foi possível conectar\nErro[ %d ]: %s\n" , $e->getCode() , $e->getMessage() );
}

Agora a saída será:

 

<table>
    <summary>Lista de clientes</summary>
    <thead>
        <tr>
            <th>Nome do Cliente</th>
            <th>Endereço</th>
            <th>Telefone</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>João Neto</td>
            <td>Rua de teste, 666</td>
            <td>00 0000-0000</td>
        </tr>
        <tr>
            <td>Teste</td>
            <td>Rua dos bobos, 0</td>
            <td>11 1111-1111</td>
        </tr>
    </tbody>
</table>

Agora que temos o modelo de dados funcionando, vamos utilizar o outro recurso da classe Datagrid que é definir um tema, o arquivo de tema é

na verdade um XSL que irá transformar o XML gerado pela classe Datagrid:

 

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output
        indent="no"
        omit-xml-declaration="yes"
        method="html" />
    <xsl:template match="/">
        <xsl:element name="table">
            <xsl:attribute name="class">estilo_da_tabela</xsl:attribute>
            <xsl:element name="summary"><xsl:value-of select=".//Datagrid/summary/text()" /></xsl:element>

            <xsl:if test="count(.//Datagrid/headers)">
                <xsl:element name="thead">
                    <xsl:element name="tr">
                        <xsl:attribute name="class">estilo_do_cabecalho</xsl:attribute>

                        <xsl:for-each select=".//Datagrid/headers/header">
                            <xsl:element name="th"><xsl:value-of select="." /></xsl:element>
                        </xsl:for-each>
                    </xsl:element>
                </xsl:element>
            </xsl:if>

            <xsl:if test="count(.//Datagrid/rows)">
                <xsl:element name="tbody">
                    <xsl:for-each select=".//Datagrid/rows/Row">
                        <xsl:element name="tr">
                            <xsl:for-each select="./Column">
                                <xsl:element name="td">
                                    <xsl:attribute name="class">estilo_da_coluna</xsl:attribute>
                                    <xsl:value-of select="." />
                                </xsl:element>
                            </xsl:for-each>
                        </xsl:element>
                    </xsl:for-each>
                </xsl:element>
            </xsl:if>

        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Agora aplicando o tema a nossa Datagrid:

 

try {
    $tabela = new Datagrid();
    $tabela->summary = "Lista de clientes";
    $tabela->theme = "Datagrid.xsl" //O nome do arquivo de tema
    $pdo = new PDO( "mysql:host=host;dbname=database" , "user" , "password" );

    if ( ( $res = $pdo->query( "SELECT * FROM `clientes`;" ) ) instanceOf PDOStatement ){
        $tabela->setModel( $res ); //Aqui definimos como modelo de dados o resultado da consulta
    }

    echo $tabela;
} catch ( PDOException $e ){
    printf( "Não foi possível conectar\nErro[ %d ]: %s\n" , $e->getCode() , $e->getMessage() );
}

Agora nossa saída será:

 

<table class="estilo_da_tabela">
    <summary>Lista de clientes</summary>
    <thead>
        <tr class="estilo_do_cabecalho">
            <th>Nome do Cliente</th>
            <th>Endereço</th>
            <th>Telefone</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td class="estilo_da_coluna">João Neto</td>
            <td class="estilo_da_coluna">Rua de teste, 666</td>
            <td class="estilo_da_coluna">00 0000-0000</td>
        </tr>
        <tr>
            <td class="estilo_da_coluna">Teste</td>
            <td class="estilo_da_coluna">Rua dos bobos, 0</td>
            <td class="estilo_da_coluna">11 1111-1111</td>
        </tr>
    </tbody>
</table>

Porém, como é comum utilizarmos uma página para exibir uma listagem vamos criar um tema para isso:

 

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output
        indent="no"
        doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
        doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
        omit-xml-declaration="no"
        method="html" />

    <xsl:template match="/">
        <html>
            <head>
                <title>Minha listagem de Clientes</title>
            </head>
            <body>
                <h1>Listagem de Clientes</h1>
                <table class='estilo_da_tabela'>
                    <summary><xsl:value-of select=".//Datagrid/summary/text()" /></summary>

                    <xsl:if test="count(.//Datagrid/headers)">
                        <thead>
                            <tr class='estilo_do_cabecalho'>
                                <xsl:for-each select=".//Datagrid/headers/header">
                                    <th><xsl:value-of select="." /></th>
                                </xsl:for-each>
                            </tr>
                        </thead>
                    </xsl:if>

                    <xsl:if test="count(.//Datagrid/rows)">
                        <tbody>
                            <xsl:for-each select=".//Datagrid/rows/Row">
                                <tr>
                                    <xsl:for-each select="./Column">
                                        <td class='estilo_da_coluna'><xsl:value-of select="." /></td>
                                    </xsl:for-each>
                                </tr>
                            </xsl:for-each>
                        </tbody>
                    </xsl:if>
                </table>
            </body>
        </html>
    </xsl:template>
</xsl:stylesheet>

Esse tema irá criar um HTML completo...

 

try {
    $tabela = new Datagrid();
    $tabela->summary = "Lista de clientes";
    $tabela->theme = "Datagrid.xsl" //O nome do arquivo de tema
    $pdo = new PDO( "mysql:host=host;dbname=database" , "user" , "password" );

    if ( ( $res = $pdo->query( "SELECT * FROM `clientes`;" ) ) instanceOf PDOStatement ){
        $tabela->setModel( $res ); //Aqui definimos como modelo de dados o resultado da consulta
    }

    echo $tabela;
} catch ( PDOException $e ){
    printf( "Não foi possível conectar\nErro[ %d ]: %s\n" , $e->getCode() , $e->getMessage() );
}

A saída será:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
    <head>
        <title>Minha listagem de Clientes</title>
    </head>
    <body>
        <h1>Listagem de Clientes</h1>
        <table class="estilo_da_tabela">
            <summary>Lista de clientes</summary>
            <thead>
                <tr class="estilo_do_cabecalho">
                    <th>Nome do Cliente</th>
                    <th>Endereço</th>
                    <th>Telefone</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td class="estilo_da_coluna">João Neto</td>
                    <td class="estilo_da_coluna">Rua de teste, 666</td>
                    <td class="estilo_da_coluna">00 0000-0000</td>
                </tr>
                <tr>
                    <td class="estilo_da_coluna">Teste</td>
                    <td class="estilo_da_coluna">Rua dos bobos, 0</td>
                    <td class="estilo_da_coluna">11 1111-1111</td>
                </tr>
            </tbody>
        </table>
    </body>
</html>

Bom, é isso agora só falta o código da classe:

 

<?php
/**
 * Cria uma tabela com os dados fornecidos, esses dados podem ser uma matriz de linhas e colunas ou então o resultado
 * de uma consulta SQL.
 *
 * @author João Batista Neto <neto.joaobatista@gmail.com>
 * @property string $theme O nome do arquivo XSL que irá desenhar a tabela
 */
class Datagrid implements Countable {
    /**
     * DOMDocument que será usado para construir a Datagrid e para aplicar o tema
     * @var DOMDocument
     */
    private $dom;

    /**
     * Define o tema padrão
     * @var array
     */
    private $default_theme;

    /**
     * Nome do arquivo de tema
     * @var string
     */
    protected $theme;

    /**
     * Número de colunas da Datagrid
     * @var integer
     */
    protected $columns;

    /**
     * Matriz contendo os cabeçalhos da Datagrid
     * @var array
     */
    protected $headers;

    /**
     * Matriz contendo as linhas da Datagrid
     * @var array
     */
    protected $rows;

    /**
     * Define uma explicação para a Datagrid
     * @var string
     */
    public $summary;

    /**
     * Cria uma instância da Datagrid
     */
    public function __construct(){
        $this->headers    = array();
        $this->rows        = array();

        /**
         * Criando o tema padrão, essa matriz contém um arquivo XSL compactado e codificado com
         * base64_encode. Isso é apenas para economizar espaço e para garantir que tenhamos pelo
         * menor um tema definido
         */
        $this->default_theme = array(
            "eNqVkztvwkAQhGv4FdZVUNgblCYgbIpEqVIlREp72Au2dA90t8b43+f8Co8cKKms08zOjj9pl6ujFMEBj",
            "S20itksemABqlRnhdrF7HP9Gj6xVTJeHq1YWKoF2hyRrgZchLILZ4lZTrRfAFRVFVWPkTY7mM3nc/j6eI",
            "O14cputZEsGY/aQF3SvqTxaFSoDBXFTGnmXloWFLrMMMNUcMOp3VSjbUSJlOusWSQFC2CIIpR7wQkDySn",
            "NYwbNkk5CgdKlB4pLjBnxjcBW9Km2lJKbmiWtduCixFBvA+tcqesXAbxw4jtTZNBbgfBIk2lTZQlngacN",
            "xTYgtG461aWiyXlGjjxzJKddH2/dxtLLXt0MYqc6viHyNPdW7tf1359B/96bDPy/2sTAeYOh82+nhxN0o",
            "P4EzejqLrGNzuoLYneZNGnwrqtLjDch30x81qKU6uTz5mT/h+qn6sPqdd5nfa127+GW3NnD5d0n30U9SC4="
        );
    }

    /**
     * Define dinamicamente o valor de uma propriedade
     * @param string $name O nome da propriedade
     * @param mixed $value O novo valor da propriedade
     */
    public function __set( $name , $value ){
        if ( $name == "theme" ){
            if ( file_exists( $value ) ){
                $this->theme = $value;
            } else {
                throw new LogicException( sprintf( "O arquivo de tema %s não foi encontrado." , $value ) );
            }
        }
    }

    /**
     * Recupera dinamicamente uma propriedade
     * @param string $name O nome da propriedade
     * @return mixed O valor da propriedade
     */
    public function __get( $name ){
        $ret = null;

        if ( $name == "theme" ){
            $ret = $this->theme;
        }

        return( $ret );
    }

    /**
     * Converte o objeto em sua representação em string
     * @return string
     */
    public function __toString(){
        return( $this->createDatagrid() );
    }

    /**
     * Adiciona uma nova linha ao fim da Datagrid
     * @param array $row Matriz contendo as colunas que serão adicionadas
     * @return Datagrid
     */
    public function append( $row ){
        if ( ( $type = gettype( $row ) ) === "array" ){
            $columns = count( $row );

            if ( $this->columns == null ) $this->columns = $columns;

            if ( $this->columns == $columns ){
                $this->rows[] = $row;
            } else {
                throw new InvalidArgumentException( sprintf( "Todas as linhas devem ter o mesmo número de colunas, %d era esperado e %d foi dado" , $this->columns , $columns ) );
            }
        } else {
            throw new UnexpectedValueException( sprintf( "O método Datagrid::append esperava um array, %s foi dado." , $type ) );
        }

        return( $this );
    }

    /**
     * Implementa o método count() da interface Countable
     * @see Countable::count()
     * @return integer O número de linhas da Datagrid
     */
    public function count(){
        return( count( $this->rows ) );
    }

    /**
     * Cria a tabela e aplica o tema especificado ou o tema padrão
     * @return string
     */
    private function createDatagrid(){
        $ret = "<Datagrid />";

        if ( count( $this->rows ) ){
            $this->dom = new DOMDocument();
            $datagrid  = $this->dom->createElement( "Datagrid" );
            $summary   = $this->dom->createElement( "summary" );
            $headers   = $this->dom->createElement( "headers" );
            $rows      = $this->dom->createElement( "rows" );

            if ( !empty( $this->summary ) ){
                $text = $this->dom->createTextNode( $this->summary );
                $summary->appendChild( $text );
            }

            $datagrid->appendChild( $summary );
            $datagrid->appendChild( $headers );
            $datagrid->appendChild( $rows );

            /**
             * Criando os cabeçalhos
             */
            foreach ( $this->headers as $header ){
                $cole = $this->dom->createElement( "header" , $header );
                $headers->appendChild( $cole );
            }

            /**
             * Criando as linhas da tabela
             */
            foreach ( $this->rows as $row ){
                $rowe = $this->dom->createElement( "Row" );
                $rows->appendChild( $rowe );

                foreach ( $row as $column ){
                    $cole = $this->dom->createElement( "Column" , $column );
                    $rowe->appendChild( $cole );
                }
            }

            $this->dom->appendChild( $datagrid );

            /**
             * Aplicando o tema
             */
            $xsl = new DOMDocument();

            if ( !empty( $this->theme ) )
                $xsl->load( $this->theme );
            else
                $xsl->loadXML( gzuncompress( base64_decode( implode( "" , $this->default_theme ) ) ) );

            $proc = new XSLTProcessor();
            $proc->registerPHPFunctions();
            $proc->importStyleSheet( $xsl );

            $ret = $proc->transformToDoc( $this->dom )->saveHTML();
        }

        return( $ret );
    }

    /**
     * Define os cabeçalhos para o Datagrid
     * @param array $headers Matriz com os cabeçalhos
     * @return Datagrid
     */
    public function setHeaders( $headers ){
        if ( ( $type = gettype( $headers ) ) === "array" ){
            $columns = count( $headers );

            if ( $this->columns == null ) $this->columns = $columns;

            if ( $this->columns == $columns ){
                $this->headers = $headers;
            } else {
                throw new InvalidArgumentException( sprintf( "Todas as linhas devem ter o mesmo número de colunas, %d era esperado e %d foi dado" , $this->columns , $columns ) );
            }
        } else {
            throw new UnexpectedValueException( sprintf( "O método Datagrid::setHeaders esperava um array, %s foi dado." , $type ) );
        }

        return( $this );
    }

    /**
     * Define o modelo de dados para o Datagrid
     * @param PDOStatement|array $statement O resultado de uma consulta
     * @return Datagrid
     */
    public function setModel( $model ){
        $set_headers = !( count( $this->headers ) > 0 );

        if ( $model instanceOf PDOStatement ){
            while ( ( $row = ( $model->fetch( PDO::FETCH_ASSOC ) ) ) ){
                if ( $set_headers ){
                    $this->setHeaders( array_keys( $row ) );
                    $set_headers = false;
                }

                $this->append( array_values( $row ) );
            }
        } elseif ( ( $type = gettype( $model ) ) == "array" ){
            if ( count( $model ) ){
                $headers = null;

                foreach ( $model as $row ){
                    if ( $set_headers ){
                        $this->setHeaders( array_keys( $row ) );
                        $set_headers = false;
                    }

                    $this->append( array_values( $row ) );
                }
            }
        } else {
            throw new UnexpectedValueException( sprintf( "Datagrid::setModel( \$model ) esperava uma instância de PDOStatement ou uma matriz, %s foi dado." , $type ) );
        }

        return( $this );
    }
}

Qualquer comentário é bem vindo, abraços...

Compartilhar este post


Link para o post
Compartilhar em outros sites

Parabens cara..

 

fico mt bom.. pelo q eu li..

 

mas ae.. tem como você disponibilizar ele pra download em zip ?

 

ae quero baixar ele pra estudar com + calma depois

 

 

valww abraçoo e parabens de novo :D

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.