segunda-feira, 30 de julho de 2012

Paginação de registros PHP Mysql MVC

Olá, tomando como base o post que mostrei um esquema simples p/ implementar MVC em programação PHP, estendendo essa explicação mostrarei neste post como fazer paginação de registros.
(vale a pena conferir o post sobre autenticação, para restringir o acesso a esses boletos {ou qualquer outra informação}) 


Quando isso é utilizado? ao se criar uma view(página,tabela ou tela) que mostrará uma grande quantidade de dados.
Imagine carregar 2.000 linhas de uma consulta e mostrar em uma página ? Além da página ficar grande e pesada(em kbs), está sujeito o usuário ter que usar o recurso "Localizar" do navegador para encontrar o que procura.


P/ evitar isso (que é muito feio), o que sugiro é uma técnica bastante utilizada de dividir a consulta em blocos ou páginas. Exemplo os 2.000 ficariam :
Registros por página : 100 --> Nº de páginas : 20
Registros por página : 50 --> Nº de páginas : 40


Além do mecanismo de paginação, pode-se implementar filtros para restringir os resultados. Logo, precisamos manter os mesmos filtros durante a navegação dos registros. A busca do Google é um bom exemplo.

O exemplo apresentado será com uma tabela p/ armazenar boletos bancários. Não vou entrar nos detalhes da geração do boleto.Basta saber o que o boleto foi gerado usando BoletoPhp , que na minha opinião é uma das maneiras mais simples e completas p/ gerar boletos com PHP.


Este exemplo ainda utiliza o variáveis tipo $_GET e mantém os valores na URL.
Esse não é o melhor jeito.A melhor maneira de fazer é com formulários usando $_POST, e os links postarem os valores necessários, e um ajax trocar o conteúdo da tabela de dados dentro de uma tag <tbody>, o que mostrarei num posso próximo post, pois tive q fazer isso p/ uma base ORACLE.

Então o que precisamos? (além do que já foi citado nos dois posts anteriores)
  • Banco de Dados
    -Tabela p/ armazenar os boletos: boleto
  • MVC
    -Model-> Boleto.php
    -View-> /pages/*(seção pública que monta a tela),/app/view/Boleto/{index,add}.php
    -Controller-> BoletoControler.php, PostController.php (para tratar os formulários postados que não sejam de consulta)
  • DAO
    -BoletoDAO.php
  • Arquivos de função, costumo chamar de lib ou /lib
    -Conexao.php
    -Utils.php
  • EXTRA: /ajax/clientes.php, /app/model/Cliente.php /app/controller/ClienteController, /app/dao/ClienteDAO.php

Estrutura de arquivos : 
Figura 1 - Estrutura de arquivos no Projeto do Netbeans


Diagrama do Banco
Figura 2 - Diagrama do Banco, a tabela boleto_seq serve apenas p/ controlar o campo nosso_num dentro da aplicação.


Codigo da tabela do Mysql :
CREATE  TABLE IF NOT EXISTS `mydb`.`boleto` (
  `id` INT(11) NOT NULL AUTO_INCREMENT ,
  `nosso_num` VARCHAR(16) NOT NULL ,
  `data_vencimento` DATE NOT NULL ,
  `data_documento` DATETIME NOT NULL ,
  `valor` DECIMAL(10,2) NOT NULL ,
  `nota_fiscal` VARCHAR(30) NULL DEFAULT NULL ,
  `discriminacao` TEXT NOT NULL ,
  `id_cliente` INT(11) NOT NULL ,
  `sacado` VARCHAR(225) NULL DEFAULT NULL ,
  `linha_digitavel` VARCHAR(54) NULL DEFAULT NULL ,
  `pago` TINYINT(1) NULL DEFAULT '0' , -- ao ser criado não pode ter sido pago, certo (0_o)?
  PRIMARY KEY (`id`) ,
  INDEX `boleto_cliente_FK` (`id_cliente` ASC) ,
  CONSTRAINT `boleto_cliente_FK`
    FOREIGN KEY (`id_cliente` )
    REFERENCES `mydb`.`cliente` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci

-- table cliente 
CREATE  TABLE IF NOT EXISTS `mydb`.`cliente` (
  `id` INT(11) NOT NULL AUTO_INCREMENT ,
  `nome` VARCHAR(255) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `email` VARCHAR(130) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `ddd` VARCHAR(3) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `fone` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `ramal` VARCHAR(6) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `endereco` VARCHAR(255) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `numero` VARCHAR(7) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `complemento` VARCHAR(30) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `bairro` VARCHAR(150) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `cidade_id` INT(11) NOT NULL ,
  `estado_id` INT(11) NOT NULL ,
  `cep` VARCHAR(9) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `tipo_end` ENUM('Res','Com','Cor') CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL DEFAULT 'Res' ,
  `tipo_fone` ENUM('Res','Com','Cel') CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT 'Res' ,
  `tipo_cliente` ENUM('pf','pj') CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `cnpj` VARCHAR(20) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `cpf` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `inscr_estadual` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `senha` VARCHAR(8) NULL DEFAULT NULL ,
  `ativo` TINYINT(1) NOT NULL DEFAULT '1' ,
  `contato` VARCHAR(150) NULL DEFAULT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `cidade_cliente_FK` (`cidade_id` ASC) ,
  INDEX `estado_cliente_FK` (`estado_id` ASC) ,
  CONSTRAINT `cidade_cliente_FK`
    FOREIGN KEY (`cidade_id` )
    REFERENCES `mydb`.`cidade` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `estado_cliente_FK`
    FOREIGN KEY (`estado_id` )
    REFERENCES `mydb`.`estado` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci


Veja a tela como ficará após o login :
Lista dos boletos com paginação e pesquisa

Explicação das seções da página

Vamos aos arquivos. obs: as datas ficam no formato brasileiro dia/mes/ano
Modelos
<?php
//@arquivo /app/model/Boleto.php
class Boleto {

    var $id;
    var $nossoNum;
    var $dataVencimento;
    var $dataDocumento;
    var $valor;
    var $notaFiscal;
    var $discriminacao;
    var $idCliente;
    var $linhaDigitavel;
    var $sacado;
    var $pago;

    public function Boleto() { // contrutor
        $this->pago=false; //ao ser criado ele não pode ter sido pago, certo ?(0_o)
        $this->nossoNum=NULL;
        $this->valor=NULL;
        $this->dataDocumento=NULL;
        $this->dataVencimento=NULL;
        $this->idCliente=NULL;
        
    }

    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }

    public function getNossoNum() {
        return $this->nossoNum;
    }

    public function setNossoNum($nossoNum) {
        $this->nossoNum = $nossoNum;
    }

    public function getDataVencimento() {
        return $this->dataVencimento;
    }

    public function setDataVencimento($dataVencimento) {
        $this->dataVencimento = $dataVencimento;
    }

    public function getDataDocumento() {
        return $this->dataDocumento;
    }

    public function setDataDocumento($dataDocumento) {
        $this->dataDocumento = $dataDocumento;
    }

    public function getValor() {
        return $this->valor;
    }

    public function setValor($valor) {
        $this->valor = $valor;
    }

    public function getNotaFiscal() {
        return $this->notaFiscal;
    }

    public function setNotaFiscal($notaFiscal) {
        $this->notaFiscal = $notaFiscal;
    }

    public function getDiscriminacao() {
        return $this->discriminacao;
    }

    public function setDiscriminacao($discriminacao) {
        $this->discriminacao = $discriminacao;
    }

    public function getIdCliente() {
        return $this->idCliente;
    }

    public function setIdCliente($idCliente) {
        $this->idCliente = $idCliente;
    }

    public function getLinhaDigitavel() {
        return $this->linhaDigitavel;
    }

    public function setLinhaDigitavel($linhaDigitavel) {
        $this->linhaDigitavel = $linhaDigitavel;
    }
    public function getSacado() {
        return $this->sacado;
    }

    public function setSacado($sacado) {
        $this->sacado = $sacado;
    }

    public function isPago() {
        return $this->pago;
    }

    public function setPago($pago) {
        $this->pago = $pago;
    }


}

?>
Controller
<?php
//@arquivo /app/controller/BoletoController.php
// observe que o controler nao faz include do conexao. quem faz isso é o DAO
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Boleto.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/dao/BoletoDAO.php');

class BoletoController {

    // funcao p/ gerar o proximo nosso numero que é sequencia e de controle da empresa
    public function getNextNossoNumero() {
        $boleto = new BoletoDAO();
        $numRetorno = $boleto->getNextNossoNum();
        return $numRetorno;
    }
    // veja como passar um TIPO de objeto p/ uma função. isso sim é moderno :)
    public function salvarBoleto(Boleto $Boleto) {
        $boletoC = new BoletoDAO();
        $salvarBoleto = $boletoC->salvarBoleto($Boleto);
        return $salvarBoleto;
    }

    public function listarBoletos() {
        $boletoD = new BoletoDAO();
        $boletoArr = $boletoD->getBoletos();
        return $boletoArr; // retorna um array de Objetos Boletos
    }

    // aqui que é feita a divisão dos registros em blocos.
    // Observe que passaremos um objeto Boleto para utilizar na busca
    public function getDadosPaginados($primeiroRegistro, $numPorPagina, Boleto $buscaBoleto) {
        $boletoD = new BoletoDAO();
        $boletoArr = $boletoD->getClientesPaginados($primeiroRegistro, $numPorPagina, $buscaBoleto);
        return $boletoArr; // retorna um array de Objetos Boletos
    }
    
    // essa funcao é usada pegar o total de registros p/ fazer a paginacao.
    // é o mesmo objeto,  passado p/ a função getDadosPaginados
    public function getTotalBoletos(Boleto $buscaBoleto) {
        $boletoD = new BoletoDAO();
        $total = $boletoD->contaTotalBoletos($buscaBoleto);
        return $total;
    }

}
?>
DAO
<?php
//@arquivo /app/dao/BoletoDAO.php
require_once($_SERVER['DOCUMENT_ROOT'] . '/lib/Conexao.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Boleto.php');

class BoletoDAO {

    public function BoletoDAO() { }
    
    // essa funcao sera utilizada qdo for criado um novo boleto
    public function getNextNossoNum() {
        $numRetorno = 1000001; // nº inicial
        $sqlQuery = "select max(valor) + 1 as valor from boleto_seq ";
        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeQuery($sqlQuery);
        if ($row = mysql_fetch_array($rs)) { // parece estranho. mas só preciso de 1 registro 
            $numRetorno = $row['valor'];
        }
        $conexao->desconecta();
        return $numRetorno;
    }

    public function salvarBoleto(Boleto $Boleto) {

        $sqlQueryInsert = "INSERT INTO `mydb`.`boleto`
            (
            `nosso_num`,
            `data_vencimento`,
            `data_documento`,
            `valor`,
            `nota_fiscal`,
            `discriminacao`,
            `id_cliente`,
            `sacado`,
            `linha_digitavel`)
            VALUES
            (
            '" . $Boleto->getNossoNum() . "',
            STR_TO_DATE('" . $Boleto->getDataVencimento() . "','%d/%m/%Y') ,
            STR_TO_DATE('" . $Boleto->getDataDocumento() . "','%d/%m/%Y') ,
            " . $Boleto->getValor() . ",
            '" . $Boleto->getNotaFiscal() . "',
            '" . $Boleto->getDiscriminacao() . "',
            " . $Boleto->getIdCliente() . ",
            '" . $Boleto->getSacado() . "',
            '" . $Boleto->getLinhaDigitavel() . "'
            );";
//        die($sqlQueryInsert);
        $conexao = new Conexao();
        $conexao->conecta();
        $rsInsert = $conexao->executeUpdate($sqlQueryInsert);


        // se inseriu, precisa incrementar  a tabela que controla os NossoNumº
        // poderia fazer uma trigger, mas nao vem ao caso
        if ($rsInsert) {  

            $sqlQuery = "select max(valor) + 1 as valor from boleto_seq ";
            $rs = $conexao->executeQuery($sqlQuery);

            while ($row = mysql_fetch_array($rs)) {
                $numRetorno = $row['valor'];
            }

            $sqlIncrement = "INSERT into boleto_seq (valor) values($numRetorno) ";
//            die($sqlIncrement);
            $rsInsert2 = $conexao->executeUpdate($sqlIncrement);
        }
        // apos realizar 2 inserts e uma consulta, pode fechar a conexao
        $conexao->desconecta();
        return true;
    }
    //lista simples dos boletos
    public function getBoletos() {
//        $sqlQuery = "Select * from boleto"; // apenas p/ ilustrar , nunca use SELECT * 
        $sqlQuery = "SELECT
            `boleto`.`id`,
            `boleto`.`nosso_num`,
            `boleto`.`data_vencimento`,
            `boleto`.`data_documento`,
            `boleto`.`valor`,
            `boleto`.`nota_fiscal`,
            `boleto`.`discriminacao`,
            `boleto`.`id_cliente`,
            `boleto`.`sacado`,
            `boleto`.`linha_digitavel`,
            `boleto`.`pago`
            FROM `mydb`.`boleto`"; // 
        $conexao = new Conexao();
        $conexao->conecta();
        $boletoArr = array(); // esse é o Array de boletos
        $cnt = 0; // criei p/ controlar o indice, poderia usar array_push($boletoArr, $boleto)
        $rsInsert = $conexao->executeQuery($sqlQuery);

        while ($row = mysql_fetch_array($rsInsert)) {
            $boleto = new Boleto();
            $boleto->setId($row['id']);
            $boleto->setNossoNum($row['nosso_num']);
            $boleto->setDataVencimento($row['data_vencimento']);
            $boleto->setDataDocumento($row['data_documento']);
            $boleto->setValor($row['valor']);
            $boleto->setNotaFiscal($row['nota_fiscal']);
            $boleto->setDiscriminacao($row['discriminacao']);
            $boleto->setIdCliente($row['id_cliente']);
            $boleto->setSacado($row['sacado']);
            $boleto->setLinhaDigitavel($row['linha_digitavel']);
            $boleto->setPago($row['pago']);

            //array_push($boletoArr, $boleto); // se eu nao estivesse usando um indice
            $boletoArr[$cnt] = $boleto;// armazena um objeto Boleto no array
            $cnt++;
        }

        $conexao->desconecta();
        return $boletoArr; // retorna o array de boletos
    }
    /*
     * Em vez de ficar verificando se ja existe WHERE na consulta
     * inicio as duas consultas com o parametro : "WHERE id is not null" que sempre
     * será verdadeiro, ae sim acrescento os filtros usando AND e como o filtro das duas 
     * consultas será o mesmo criei a funcao montaFiltroPaginacao que recebe um boleto
     * como parametro, e retorna o SQL complementar as consultas base
     */
    
    // essa funcao é usada pegar o total de registros p/ fazer a paginacao.
    public function contaTotalBoletos(Boleto $boleto) {
        $conexao = new Conexao();
        $conexao->conecta();
        $sqlQuery = "SELECT COUNT(*) as total FROM mydb.boleto B WHERE id is not null ";
        $sqlQuery .= $this->montaFiltroPaginacao($boleto);

        $rs = $conexao->executeQuery($sqlQuery);
        $total = 0;
        while ($row = mysql_fetch_array($rs)) {
            $total = $row['total'];
        }

        $conexao->desconecta();
        return $total;
        
    }
    // essa funcao é usada p/ fazer a paginacao.
    public function getClientesPaginados($primeiroRegistro, $numPorPagina, Boleto $boleto) {
        $sqlQuery = "Select B.*, 
            DATE_FORMAT(B.data_documento,'%d/%m/%Y') as data_documento,  
            DATE_FORMAT(B.data_vencimento,'%d/%m/%Y') as data_vencimento   
            from boleto B WHERE id is not null ";
        $sqlQuery .= $this->montaFiltroPaginacao($boleto);
        
        $sqlQuery.= " LIMIT " . $primeiroRegistro . "," . $numPorPagina; // esse é segredo da divisao
        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeQuery($sqlQuery);

        $boletoArr = array();
        $cnt = 0;
        while ($row = mysql_fetch_array($rs)) {
            $B = new Boleto();
            $B->setId($row['id']);
            $B->setNossoNum($row['nosso_num']);
            $B->setDataVencimento($row['data_vencimento']);
            $B->setDataDocumento($row['data_documento']);
            $B->setValor($row['valor']);
            $B->setNotaFiscal($row['nota_fiscal']);
            $B->setDiscriminacao($row['discriminacao']);
            $B->setIdCliente($row['id_cliente']);
            $B->setSacado($row['sacado']);
            $B->setLinhaDigitavel($row['linha_digitavel']);
            $B->setPago($row['pago']);

            $boletoArr[$cnt] = $B;
            $cnt++;
            
        }
        $conexao->desconecta();
        return $boletoArr; //retorna o array de boletos de acordo com a pagina que sera controlada na view
    }

    //funcao utilizada para aplicar o filtro na pesquisa
    public function montaFiltroPaginacao(Boleto $boleto){
        $sqlQuery="";
        
        if ($boleto->getNossoNum() != NULL) {
            $sqlQuery.=" AND  B.nosso_num  like '%" . $boleto->getNossoNum() . "%' ";
        }

        if ($boleto->getValor() != NULL) {
            $sqlQuery.=" AND  B.valor =" . $boleto->getValor() . " ";
        }
        
        if ($boleto->getDataDocumento() != NULL) {
            $sqlQuery.=" AND  B.data_documento  = STR_TO_DATE('" . $boleto->getDataDocumento() . "','%d/%m/%Y') ";
        }

        if ($boleto->getDataVencimento() != NULL) {
            $sqlQuery.=" AND  B.data_vencimento  = STR_TO_DATE('" . $boleto->getDataVencimento() . "','%d/%m/%Y') ";
        }

        if ($boleto->getIdCliente() != NULL) {
            $sqlQuery.=" AND  B.id_cliente  = " . $boleto->getIdCliente() . " ";
        }
        
        if ($boleto->isPago()) {
            $sqlQuery.=" AND  B.pago  = 1 ";
        }
        
        
        return $sqlQuery;
    }
    
    
}

?>
VIEW
Assumindo que você já viu os 2 posts anteriores, e o cliente já está logado, com a estrutura acima explicada, precisamos apenas definir na view
o nº de Registros por página, a página em que está, e objeto Boleto que será mantido na busca durante a paginacao.
Lembre-se que essa página privado.php já fez os includes necessários de todas as classes que utilizaremos abaixo.
codigo necessário para fazer a paginação: obs: não vou incluir a parte da pesquisa para focar na paginação e no final eu mostro o arquivo inteiro

<?
//@arquivo=/app/view/Boleto/index.php
/* arquivo q ja estao sendo incluidos
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Boleto.php'); 
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/controller/BoletoController.php');
*/
session_start();
error_reporting('E_ALL');
// define registros por pagina
$numPorPagina = 5;
//base p/ gerar os links
$basePage = $_SESSION['user']['view'] . "?section=boletos&action=index";

// acrescenta o parametro pagina no link, o valor sera gerado ao carregar
$basePage .="&pagina=";

if (isset($_GET['pagina'])) {
    $pagina = $_GET['pagina'];
} else {
    $pagina = 1;
}
//trava palhaçadas, como tentar passar numeros negativos na url
if ($pagina <= 0) {
    header("Location:" . $basePage . "1");
}

// 1º registro do Bloco
$primeiroRegistro = ($pagina * $numPorPagina) - $numPorPagina;

// observe o objeto boletoBusca usado nas duas funcoes 
$BoletoC = new BoletoController();
$cache = $BoletoC->getDadosPaginados($primeiroRegistro, $numPorPagina, $boletoBusca); // carrega os registros do array 
$totalRegistros = $BoletoC->getTotalBoletos($boletoBusca); // pega o total de boletos 
$totalPaginas = ceil($totalRegistros / $numPorPagina); // ceil arredonda p/ maior , exemplo 1.65 sao 2 paginas

$anterior = $pagina - 1;
$proximo = $pagina + 1;

//trava palhaçadas , tipo colocar um nº maior do que o total de paginas
if ($pagina > $totalPaginas) {
    header("Location:" . $basePage . $totalPaginas);
}
?>

<!-- SECAO DADOS-->
    <table width="85%" border="0" cellspacing="2" cellpadding="0">
        <tr>
            <th>ID</th>
            <th>SACADO</th>
            <th>NOSSO NUM</th>
            <th>DATA DOC</th>
            <th>DATA VENC</th>
            <th>VALOR</th>
            <th>a&ccedil;&otilde;es</th>
        </tr>
        <? if (count($cache) > 0) { 
            for ($i = 0; $i < count($cache); $i++) {
                $B = new Boleto();
                $B = $cache[$i];
                $idBoleto = $B->getId();
                ?>
                <tr>
                    <td><? echo $idBoleto; ?></td>
                    <td><? echo $B->getSacado(); ?></td>
                    <td><? echo $B->getNossoNum(); ?></td>
                    <td><? echo $B->getDataDocumento(); ?></td>
                    <td><? echo $B->getDataVencimento(); ?></td>
                    <td><? echo $B->getValor(); ?></td>
                    <td class="actions">
                        <a href="<? echo $_SESSION['user']['view']; ?>?section=boletos&action=view&id=<? echo $idBoleto; ?>"><img src="/images/search-menor.png"></a>| 
                    </td>
                </tr>
        <? } ?>
            <tr> 
                <td colspan="7"><hr width="100%" size="1" /></td>
            </tr>
            <tr>
                <td colspan="7"><? echo $primeiro = ($primeiroRegistro <= 0) ? "1" : $primeiroRegistro; ?> até <?
                echo $ultimo = ($totalRegistros <= ($primeiroRegistro + $numPorPagina) ? $totalRegistros : ($primeiroRegistro + $numPorPagina));
                ?> de <? echo $totalRegistros; ?></td>
            </tr>
        </table>
    
        <!-- SECAO BARRA DE NAVEGACAO -->
        <div class="paging">

            <? if ($pagina <= 1) { ?>
                <span class="prev disabled">&lt; anterior</span>
            <? } else { ?>
                <span class="prev"><a href=" <? echo $basePage . $anterior; ?>">&lt;anterior</a></span>
            <? } ?>
            <?
            for ($z = 1; $z <= $totalPaginas; $z++) {
                if ($z != $pagina) {
                    ?> <a href="<? echo $basePage . $z; ?>"><? echo $z; ?></a>
                <? } else { ?>
                    <? echo $z; ?>
                <? } ?>

            <? } ?>
            <? if ($pagina == $totalPaginas) { ?>
                <span class="next disabled">pr&oacute;xima &gt;</span>
            <? } else { ?>
                <span class="next"><a href="<? echo $basePage . $proximo; ?>">pr&oacute;xima &gt;</a></span>
    <? } ?>
        </div>
        <!-- FIM SECAO BARRA DE NAVEGACAO -->
    </div>
<? } else {// se  a consulta retornar 0 registros  ?>
    <tr> 
        <td colspan="5">Boleto não econtrado.</td>
    </tr>
    </table>
    </div>
<? } ?>
    <!-- FIM SECAO DADOS-->

Simples hein ? agora vamos adicionar a parte php da carga do boletoBusca e a parte   html da busca em cima da seção DADOS.
PHP
<?
// apenas usado p/ realizar a busca, caso seja utilizada
$boletoBusca = new Boleto();
if ((isset($_GET['nosso_num'])) && ($_GET['nosso_num'] != "")) {
    $boletoBusca->setNossoNum($_GET['nosso_num']);
    $basePage.="&nosso_num=" . $_GET['nosso_num'];
}
if ((isset($_GET['valor'])) && ($_GET['valor'] != "")) {
    $boletoBusca->setValor($_GET['valor']);
    $basePage.="&valor=" . $_GET['valor'];
}
if ((isset($_GET['data_doc'])) && ($_GET['data_doc'] != "")) {
    $boletoBusca->setDataDocumento($_GET['data_doc']);
    $basePage.="&data_doc=" . $_GET['data_doc'];
}
if ((isset($_GET['data_venc'])) && ($_GET['data_venc'] != "")) {
    $boletoBusca->setDataVencimento($_GET['data_venc']);
    $basePage.="&data_venc=" . $_GET['data_venc'];
}

if ((isset($_GET['clienteId'])) && ($_GET['clienteId'] != 'todos')) {
    $boletoBusca->setIdCliente($_GET['clienteId']);
    $basePage.="&clienteId=" . $_GET['clienteId'];
}
if ((isset($_GET['pago']))) {
    $boletoBusca->setPago(1);
    $basePage.="&pago=1";
}
?>
HTML, observe que os campos já vem preenchidos com os parâmetros da busca
<!-- SECAO BUSCA  -->
<div id="busca">
<fieldset>
<form id="formBusca" name="formBusca" method="GET" action="<? echo $_SESSION['user']['view']; ?>">
<table style="width: auto;">
    <tr>
        <th colspan="2">Localizar Boleto:<th>
    </tr>
    <tr>
        <td>Nosso Nº:</td><td><input type="text" name="nosso_num" id="nossoNum" value="<? echo $boletoBusca->getNossoNum(); ?>"></td>
    </tr> 
    <tr>
        <td>
            Valor:</td><td><input type="text" name="valor" id="valor" value="<? echo $boletoBusca->getValor(); ?>">
        </td>
    </tr> 
    <tr>
        <td>
            Data Doc:</td><td><input type="text" name="data_doc" id="dataDoc" size="16" value="<? echo $boletoBusca->getDataDocumento(); ?>">
        </td>
    </tr> 
    <tr>
        <td>
            Pago:</td><td><input type="checkbox" name="pago" id="pago" <? if ($boletoBusca->isPago()) {
echo "checked=\"checked\"";
} ?> value="<? echo $boletoBusca->isPago(); ?>">
        </td>
    </tr> 
    <tr>
        <td>
            Data Venc:</td><td><input type="text" name="data_venc" id="dataVenc" size="16" value="<? echo $boletoBusca->getDataVencimento(); ?>">
        </td>
    </tr> 
    <tr>
        <td>
            Cliente:</td><td>
            <input name="data[OS][busca_cliente]" maxlength="30" type="text" id="buscaCliente" />
            <span class="actions">
                <input  type="button" value="Buscar Cliente" id="busca2"/>
            </span>
        </td>
    </tr> 
    <tr>
        <td>Cliente Selecionado</td>
        <td>
            <select name="clienteId" id="clienteId">
                <option value="todos">todos</option>
                <?  // essa parte apenas enche o combo com os clientes retornados da consulta
                if ($boletoBusca->getIdCliente() != NULL) {
                    $clienteC = new ClienteController();
                    $clienteC->loadOptions($boletoBusca->getIdCliente());
                }
                ?>
            </select>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <input type="hidden" name="section" id="section" value="boletos">
            <input type="hidden" name="action" id="action" value="index">
            <input type="hidden" name="pagina" id="pagina" value="1">
<!-- ao clicar no botao pesquisar sempre iniciará uma nova pesquisa por isso 
pagina=1 -->
            <div class="submit">
                <input type="submit" value="Buscar">
            </div>
        </td>
    </tr>
</table>
</form>
</fieldset>    
</div>
<!-- FIM SECAO BUSCA-->

E está feita a paginação de registros. Porém existe uma particularidade nessa busca. Não faz sentido nessa busca, pesquisar nomes de clientes que não existam, Então ao preencher o campo busca_cliente ou clicar no botão "buscar cliente" um ajax busca uma lista de clientes e enche o combo com os resultados, realizando o objetivo de filtrar os boletos por Cliente.
Porque isso? imagine se tivesse 3000 clientes o tamanho q essa lista ficaria. ... então é melhor filtrar ;) 


cuidado ao utilizar ajax p/ não expor dados sigilosos. pesquise no google "cross-site scripting ajax" ou "Cross-site request forgery"
parte do ajax 
<?
session_start();
require_once($_SERVER['DOCUMENT_ROOT'] . '/lib/block.php');
/*
  @arquivo = /ajax/clientes.php
  MVC : model
  objeto : Cliente
  tabela : clientes
  obs : responde a requisições ajax e monta as options do combo clientes
 */
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Cliente.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/controller/ClienteController.php');

//jeito inseguro
//
//if (isset($_GET['busca'])) {
//    $busca = $_GET['busca'];
//    $cliente = new ClienteController();
//    if (isset($_GET['incluirOpcao']) && $_GET['incluirOpcao'] == 'todos') {
//        echo "<option value=\"todos\" selected=\"selected\" >todos</option>";
//    }
//    if ($busca != "") {
//        $cliente->buscaOptions($busca);
//    }
//}

//jeito mais seguro
if (isset($_POST['busca'])) {
    $busca = $_POST['busca'];
    $clienteC = new ClienteController();
    $todos = false;
    if (isset($_POST['incluirOpcao']) && $_POST['incluirOpcao'] == 'todos') {
//        echo "<option value=\"todos\" selected=\"selected\" >todos</option>";
        $todos = true;
        $primeiroCliente= new Cliente();
        $primeiroCliente->setId('todos');
        $primeiroCliente->setNome('todos');
        
    }
    if ($busca != "") {
        $arrClientes= $clienteC->getListaClientesAjax($busca);
    }
//    ternary way
//    $increment=$todos==true?array_shift($arrClientes,$primeiroCliente):null;
//    if ($todos){array_shift($arrClientes,$primeiroCliente);}
    $conta=0;
    if ($todos){
        $arrtmp[$conta]=
        (array(
        "id"=>$primeiroCliente->getId(),
        "nome"=>$primeiroCliente->getNome()
        ));
        $conta++;
    }
    foreach ($arrClientes as $cliente) {
     
        $arrtmp[$conta]=
        (array(
        "id"=>$cliente->getId(),
        "nome"=>$cliente->getNome()
        ));
        $conta++;
}
    echo json_encode($arrtmp);
}

// like a beam , but not java
if (isset($_POST['cliente_id'])){
    $clienteId=$_POST['cliente_id'];
    $clienteC = new ClienteController();
    $cliente = new Cliente();
   
    $cliente = $clienteC->loadDados($clienteId);
    echo json_encode(array(
        "id"=>$cliente->getId(),
        "nome"=>$cliente->getNome(),
        "tipo_cliente"=>$cliente->getTipoCliente(),
        "cpf"=>$cliente->getCpf(),
        "cnpj"=>$cliente->getCnpj()
        ));
    
}
?>
//complementar ao ajax :o clienteCOntroller
<?
/*
  @arquivo = /app/controller/ClienteController.php
  MVC : controller
  objeto : Cliente
 */
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Cliente.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/dao/ClienteDAO.php');

Class ClienteController {
public function buscaOptions($busca) {
        $clienteDAO = new ClienteDAO();
        $clientes = $clienteDAO->getClientesBusca($busca);
        $conta = 0;


        foreach ($clientes as $cliente) {
            echo "<option value=\"" . $cliente->getId() . "\" >" . $cliente->getNome() . "</option> \n";
            $conta++;
        }

        if ($conta == 0) {
            echo "<option  value=\"0\" selected=\"selected\" > cliente n&atilde;o encontrado </option> \n";
        }
    }
 public function getListaClientesAjax($busca) {
        $clienteDAO = new ClienteDAO();
        $arrClientes = $clienteDAO->getClientesBusca($busca);
        if (count($arrClientes)==0){
            return NULL;
        }else {
        return $arrClientes;
        }
}?>
e o clienteDAO
<?php

//  @arquivo = /app/dao/ClienteDAO.php
//  MVC : controller
//  objeto : Cliente

require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Cliente.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/lib/Conexao.php');

class ClienteDAO {

    public function ClienteDAO() {
        
    }

    public function getClientesBusca($busca) {
        $arrClientes = array();
        $indice = 0;
        $sqlQuery = "Select id,nome from cliente where nome like '%" . $busca . "%' order by nome";

        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeQuery($sqlQuery);

        while ($row = mysql_fetch_array($rs)) {
            $cliente = new Cliente();
            $cliente->setId($row['id']);
            $cliente->setNome($row['nome']);
            $arrClientes[$indice] = $cliente;
            $indice++;
        }

        $conexao->desconecta();
        return $arrClientes;
    }
}?>



Agora vejamos pagina completa :
<?
session_start();
error_reporting('E_ALL');

require_once($_SERVER['DOCUMENT_ROOT'] . '/app/controller/ClienteController.php');

// define registros por pagina
$numPorPagina = 5;
//base p/ gerar os links
$basePage = $_SESSION['user']['view'] . "?section=boletos&action=index";

// apenas usado p/ realizar a busca, caso seja utilizada
$boletoBusca = new Boleto();
if ((isset($_GET['nosso_num'])) && ($_GET['nosso_num'] != "")) {
    $boletoBusca->setNossoNum($_GET['nosso_num']);
    $basePage.="&nosso_num=" . $_GET['nosso_num'];
}
if ((isset($_GET['valor'])) && ($_GET['valor'] != "")) {
    $boletoBusca->setValor($_GET['valor']);
    $basePage.="&valor=" . $_GET['valor'];
}
if ((isset($_GET['data_doc'])) && ($_GET['data_doc'] != "")) {
    $boletoBusca->setDataDocumento($_GET['data_doc']);
    $basePage.="&data_doc=" . $_GET['data_doc'];
}
if ((isset($_GET['data_venc'])) && ($_GET['data_venc'] != "")) {
    $boletoBusca->setDataVencimento($_GET['data_venc']);
    $basePage.="&data_venc=" . $_GET['data_venc'];
}

if ((isset($_GET['clienteId'])) && ($_GET['clienteId'] != 'todos')) {
    $boletoBusca->setIdCliente($_GET['clienteId']);
    $basePage.="&clienteId=" . $_GET['clienteId'];
}
if ((isset($_GET['pago']))) {
    $boletoBusca->setPago(1);
    $basePage.="&pago=1";
}
// acrescenta o parametro pagina no link, o valor sera gerado ao carregar
$basePage .="&pagina=";



if (isset($_GET['pagina'])) {
    $pagina = $_GET['pagina'];
} else {
    $pagina = 1;
}
//trava palhaçadas, como tentar passar numeros negativos na url
if ($pagina <= 0) {
    header("Location:" . $basePage . "1");
}

// 1º registro do Bloco
$primeiroRegistro = ($pagina * $numPorPagina) - $numPorPagina;

// observe o objeto boletoBusca usado nas duas funcoes 
$BoletoC = new BoletoController();
$cache = $BoletoC->getDadosPaginados($primeiroRegistro, $numPorPagina, $boletoBusca); // carrega os registros do array 
$totalRegistros = $BoletoC->getTotalBoletos($boletoBusca); // pega o total de boletos 
$totalPaginas = ceil($totalRegistros / $numPorPagina); // ceil arredonda p/ maior , exemplo 1.65 sao 2 paginas

$anterior = $pagina - 1;
$proximo = $pagina + 1;

//trava palhaçadas , tipo colocar um nº maior do que o total de paginas
if ($pagina > $totalPaginas) {
    header("Location:" . $basePage . $totalPaginas);
}
?>        

<div class="index">
    <h3>Seção : boletos:</h3> 
<!-- SECAO BUSCA  -->
<div id="busca">
<fieldset>
<form id="formBusca" name="formBusca" method="GET" action="<? echo $_SESSION['user']['view']; ?>">
<table style="width: auto;">
    <tr>
        <th colspan="2">Localizar Boleto:<th>
    </tr>
    <tr>
        <td>Nosso Nº:</td><td><input type="text" name="nosso_num" id="nossoNum" value="<? echo $boletoBusca->getNossoNum(); ?>"></td>
    </tr> 
    <tr>
        <td>
            Valor:</td><td><input type="text" name="valor" id="valor" value="<? echo $boletoBusca->getValor(); ?>">
        </td>
    </tr> 
    <tr>
        <td>
            Data Doc:</td><td><input type="text" name="data_doc" id="dataDoc" size="16" value="<? echo $boletoBusca->getDataDocumento(); ?>">
        </td>
    </tr> 
    <tr>
        <td>
            Pago:</td><td><input type="checkbox" name="pago" id="pago" <? if ($boletoBusca->isPago()) {
echo "checked=\"checked\"";
} ?> value="<? echo $boletoBusca->isPago(); ?>">
        </td>
    </tr> 
    <tr>
        <td>
            Data Venc:</td><td><input type="text" name="data_venc" id="dataVenc" size="16" value="<? echo $boletoBusca->getDataVencimento(); ?>">
        </td>
    </tr> 
    <tr>
        <td>
            Cliente:</td><td>
            <input name="data[OS][busca_cliente]" maxlength="30" type="text" id="buscaCliente" />
            <span class="actions">
                <input  type="button" value="Buscar Cliente" id="busca2"/>
            </span>
        </td>
    </tr> 
    <tr>
        <td>Cliente Selecionado</td>
        <td>
            <select name="clienteId" id="clienteId">

                <option value="todos">todos</option>
                <?  // essa parte apenas enche o combo com os clientes retornados da consulta
                if ($boletoBusca->getIdCliente() != NULL) {
                    $clienteC = new ClienteController();
                    $clienteC->loadOptions($boletoBusca->getIdCliente());
                }
                ?>

            </select>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <input type="hidden" name="section" id="section" value="boletos">
            <input type="hidden" name="action" id="action" value="index">
            <input type="hidden" name="pagina" id="pagina" value="1">
            <div class="submit">
                <input type="submit" value="Buscar">
            </div>
        </td>
    </tr>
</table>
</form>
</fieldset>    
</div>
<!-- FIM SECAO BUSCA-->
    <!-- SECAO DADOS-->
    <table width="85%" border="0" cellspacing="2" cellpadding="0">
        <tr>
            <th>ID</th>
            <th>SACADO</th>
            <th>NOSSO NUM</th>
            <th>DATA DOC</th>
            <th>DATA VENC</th>
            <th>VALOR</th>
            <th>a&ccedil;&otilde;es</th>
        </tr>
        <? if (count($cache) > 0) { 
            for ($i = 0; $i < count($cache); $i++) {
                $B = new Boleto();
                $B = $cache[$i];
                $idBoleto = $B->getId();
                ?>
                <tr>
                    <td><? echo $idBoleto; ?></td>
                    <td><? echo $B->getSacado(); ?></td>
                    <td><? echo $B->getNossoNum(); ?></td>
                    <td><? echo $B->getDataDocumento(); ?></td>
                    <td><? echo $B->getDataVencimento(); ?></td>
                    <td><? echo $B->getValor(); ?></td>
                    <td class="actions">
                        <a href="<? echo $_SESSION['user']['view']; ?>?section=boletos&action=view&id=<? echo $idBoleto; ?>"><img src="/images/search-menor.png"></a>| 
                    </td>
                </tr>
        <? } ?>
            <tr> 
                <td colspan="7"><hr width="100%" size="1" /></td>
            </tr>
            <tr>
                <td colspan="7"><? echo $primeiro = ($primeiroRegistro <= 0) ? "1" : $primeiroRegistro; ?> até <?
                echo $ultimo = ($totalRegistros <= ($primeiroRegistro + $numPorPagina) ? $totalRegistros : ($primeiroRegistro + $numPorPagina));
                ?> de <? echo $totalRegistros; ?></td>
            </tr>
        </table>
    
        <!-- SECAO BARRA DE NAVEGACAO -->
        <div class="paging">

            <? if ($pagina <= 1) { ?>
                <span class="prev disabled">&lt; anterior</span>
            <? } else { ?>
                <span class="prev"><a href=" <? echo $basePage . $anterior; ?>">&lt;anterior</a></span>
            <? } ?>
            <?
            for ($z = 1; $z <= $totalPaginas; $z++) {
                if ($z != $pagina) {
                    ?> <a href="<? echo $basePage . $z; ?>"><? echo $z; ?></a>
                <? } else { ?>
                    <? echo $z; ?>
                <? } ?>

            <? } ?>
            <? if ($pagina == $totalPaginas) { ?>
                <span class="next disabled">pr&oacute;xima &gt;</span>
            <? } else { ?>
                <span class="next"><a href="<? echo $basePage . $proximo; ?>">pr&oacute;xima &gt;</a></span>
    <? } ?>
        </div>
        <!-- FIM SECAO BARRA DE NAVEGACAO -->
    </div>
<? } else {// se  a consulta retornar 0 registros  ?>
    <tr> 
        <td colspan="5">Boleto não econtrado.</td>
    </tr>
    </table>
    </div>
<? } ?>
    <!-- FIM SECAO DADOS-->
    <!-- SECAO NOVO -->
<div class="actions">
    <h3>A&ccedil;&otilde;es</h3>
    <ul>
        <li><a style="width: 80px;"  href="<? echo $_SESSION['user']['view']; ?>?section=boletos&action=add">Novo Boleto</a></li>


    </ul>
</div>
    <!-- FIM SECAO NOVO -->
e no final da pagina
<script type="text/javascript">
    $(document).ready(function() {
        $(function(){  
            $("#dataDoc").mask("99/99/9999");  
            $("#dataVenc").mask("99/99/9999");  
            
        }); 

        $('#buscaCliente').change(function(){
//            JEITO INSEGURO              
//            $('#clienteId').load('/ajax/clientes.php?incluirOpcao=todos&busca='+$('#buscaCliente').val(),function(){
//                $('#clienteId').each(function(){
//                    if (this.value==""){
//                        alert('cliente nao econtrado');
//                    }
//                });
//
//            }) ;

//            JEITO SEGURO
            if ( $('#buscaCliente').val()!='') {
            var dataString='incluirOpcao=todos&busca='+$('#buscaCliente').val();
            $('#clienteId').empty(); // limpa o select
            $.post("/ajax/clientes.php", dataString,
            function(data) {
              var conta=0;
              
              $.each(data,function(){
                $('#clienteId').append("<option value="+this['id']+">"+this['nome']+"</option>");
//                alert(data);
//                alert(this);
                conta++;
              })
                if (conta==1){ // o 1º é o todos
                alert('nenhum cliente econtrado');
            }
            },"json");
            

            }
        
        });
        
    });
</script>



E assim está concluída nossa paginação registros com opções de filtragem/busca. Para complementar vou mostrar os principais trechos das duas paginas que salvam os boletos. Eis o formulário após a geração do boleto.

<br>
<form action="/app/controller/PostController.php" method="post">
<input type="hidden" name="data[boleto][operacao]" value="novo"/>
<input type="hidden" name="data[boleto][id_cliente]" value="<? echo $_POST['id_cliente'];?>"/>
<input type="hidden" name="data[boleto][nossoNum]" value="<? echo $dadosboleto["nosso_numero"]; ?>"/>
<input type="hidden" name="data[boleto][dataVencimento]" value="<? echo $dadosboleto["data_vencimento"] ;?>"/>
<input type="hidden" name="data[boleto][dataDocumento]" value="<? echo $dadosboleto["data_documento"];?>"/>
<input type="hidden" name="data[boleto][valorBoleto]" value="<? echo number_format($dadosboleto["valor_boleto"],2,'.','') ;?>"/>
<input type="hidden" name="data[boleto][sacado]" value="<? echo $dadosboleto["sacado"] ;?>"/>
<input type="hidden" name="data[boleto][nf]" value="<? echo $dadosboleto["nu_doc"] ;?>"/>
<input type="hidden" name="data[boleto][discriminacao]" value="<? echo $dadosboleto["demonstrativo1"] ;?>"/>
<input type="hidden" name="data[boleto][doc_cliente]" value="<? echo $dadosboleto["cnpj_sacado"];?>"/>
<input type="hidden" name="data[boleto][linha_digitavel]" value="<? echo $dadosboleto["linha_digitavel"];?>"/>

<button>Salvar</button>
</form>

e o PostController que recebe e trata os formularios, esta seção abaixo salva o boleto.

<?php
session_start();
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Boleto.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/controller/BoletoController.php');

//echo "<pre>";
//print_r($_POST);
//echo  "</pre>";



if (isset($_POST['data']['boleto']['operacao'])) {
    
    $nossoNum=$_POST['data']['boleto']['nossoNum'];
    $dataVencimento=$_POST['data']['boleto']['dataVencimento'];
    $dataDocumento=$_POST['data']['boleto']['dataDocumento'];
    $valor=$_POST['data']['boleto']['valorBoleto'];
    $notaFiscal=$_POST['data']['boleto']['nf'];
    $discriminacao=$_POST['data']['boleto']['discriminacao'];
    $idCliente =$_POST['data']['boleto']['id_cliente'];
    $sacado = $_POST['data']['boleto']['sacado'];
    $linhaDigitavel = $_POST['data']['boleto']['linha_digitavel'];
    $B= new Boleto();
    $B->setNossoNum($nossoNum);
    $B->setDataVencimento($dataVencimento);
    $B->setDataDocumento($dataDocumento);
    $B->setValor($valor);
    $B->setNotaFiscal($notaFiscal);
    $B->setDiscriminacao($discriminacao);
    $B->setIdCliente($idCliente);
    $B->setSacado($sacado);
    $B->setLinhaDigitavel($linhaDigitavel);
    
    $boletoC = new BoletoController();
    $boletoC->salvarBoleto($B);
    $urlToGo = $_SESSION['user']['view']."?section=boletos&action=index&flash=boleto&resultado=3";
    header("Location: $urlToGo");
    
}


?>

Até a próxima...

terça-feira, 17 de julho de 2012

Autenticação de Usuários PHP-MVC

Olá, tomando como base o post que mostrei um esquema simples p/ implementar MVC em programação PHPmostrarei neste post como fazer autenticação de usuários para acesso restrito.


Hoje em dia é bastante comum querermos ou precisarmos restringir ou personalizar algumas seções do site. 


Uma maneira de implementar isso é utilizando um objeto de Sessão ($_SESSION no caso do PHP) no lado do servidor e autenticar o usuário por meio de um formulário de login e senha.


E para isso é necessário: 
1 -Criar a estrutura no banco de dados  p/ armazenar o usuários;
2- Criar a interface de acesso e o controlador de autenticação


E nas páginas que vc quiser restringir basta fazer o seguinte : 
1 - Criar um arquivo que verifica o estado da sessão e manda p/ a pagina de Login, se já não estiver logado.
2 - Incluir esse arquivo <include,require,require_once> no começo de cada arquivo restrito


O Exemplo : 
De onde veio este exemplo existem clientes e funcionários.Ambos são pessoas.
Assim sendo temos a classe base Pessoa e as classes que estendem Pessoa que são Cliente e Funcionario. já detalhadas aqui 


E por questões de segurança e regras do negócio ambos serão tratados como usuários, onde as views são separadas, mas acessam os mesmos objetos (assim as regras ficam organizadas, evitando códigos duplicados)

Vamos a estrutura do banco :
Observe que temos a tabela cliente e funcionario e usuario é uma view
Figura 1 - estrutura do banco




// funcionario
CREATE  TABLE IF NOT EXISTS `mydb`.`funcionario` (
  `id` INT(11) NOT NULL AUTO_INCREMENT ,
  `nome` VARCHAR(150) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `funcao` VARCHAR(150) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `cpf` VARCHAR(11) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `data_admissao` DATE NOT NULL ,
  `salario` DECIMAL(7,2) NOT NULL ,
  `rg` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `carteira_trab` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `pis` VARCHAR(11) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `data_nascimento` DATE NOT NULL ,
  `senha` VARCHAR(45) NULL DEFAULT NULL ,
  `ativo` TINYINT(1) NOT NULL DEFAULT '1' ,
  `admin` TINYINT(1) NULL DEFAULT '0' ,
  `perfil_acesso` ENUM('Atendente','Gestor','Admin') NULL DEFAULT NULL ,
  `login` VARCHAR(45) NULL DEFAULT NULL ,
  `email` VARCHAR(120) NULL DEFAULT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB
AUTO_INCREMENT = 4
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci

//view 
CREATE OR REPLACE VIEW `usuario` AS 
select `C`.`id` AS `id`,
`C`.`email` AS `login`, -- login do cliente é o email
`C`.`senha` AS `senha`,
'C' AS `tipo_usuario`, -- define C
`C`.`ativo` AS `ativo`,
'Cliente' AS `perfil_acesso` 
from `mydb`.`cliente` `C` 
union 
select `F`.`id` AS `id`,
`F`.`login` AS `login`, -- funcionario possui um campo login
`F`.`senha` AS `senha`,
'F' AS `tipo_usuario`, -- define F
`F`.`ativo` AS `ativo`,
`F`.`perfil_acesso` AS `perfil_acesso` -- le o perfil
 from `mydb`.`funcionario` `F`


Vamos a estrutura dos arquivos : 


Figura 2 - Organização dos arquivos






Vamos aos arquivos:


Models:
/app/model/Funcionario.php
<?
//  @arquivo = /app/model/Funcionario.php
//  MVC : model
//  objeto : Funcionario


require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Pessoa.php');

class Funcionario extends Pessoa{

    var $funcao;
    var $cpf;
    var $dataAdmissao;
    var $salario;
    var $rg;
    var $carteiraTrab;
    var $pis;
    var $dataNascimento;
    var $senha;
    var $ativo;
    var $admin;
    var $perfilAcesso;
    var $login;
    var $email;
    
    public function Funcionario() {
        
    }
    public function getFuncao() {
        return $this->funcao;
    }

    public function setFuncao($funcao) {
        $this->funcao = $funcao;
    }

    public function getCpf() {
        return $this->cpf;
    }

    public function setCpf($cpf) {
        $this->cpf = $cpf;
    }

    public function getDataAdmissao() {
        return $this->dataAdmissao;
    }

    public function setDataAdmissao($dataAdmissao) {
        $this->dataAdmissao = $dataAdmissao;
    }

    public function getSalario() {
        return $this->salario;
    }

    public function setSalario($salario) {
        $this->salario = $salario;
    }

    public function getRg() {
        return $this->rg;
    }

    public function setRg($rg) {
        $this->rg = $rg;
    }

    public function getCarteiraTrab() {
        return $this->carteiraTrab;
    }

    public function setCarteiraTrab($carteiraTrab) {
        $this->carteiraTrab = $carteiraTrab;
    }

    public function getPis() {
        return $this->pis;
    }

    public function setPis($pis) {
        $this->pis = $pis;
    }

    public function getDataNascimento() {
        return $this->dataNascimento;
    }

    public function setDataNascimento($dataNascimento) {
        $this->dataNascimento = $dataNascimento;
    }

    public function getSenha() {
        return $this->senha;
    }

    public function setSenha($senha) {
        $this->senha = $senha;
    }

    public function getAtivo() {
        return $this->ativo;
    }

    public function setAtivo($ativo) {
        $this->ativo = $ativo;
    }

    public function getAdmin() {
        return $this->admin;
    }

    public function setAdmin($admin) {
        $this->admin = $admin;
    }

    public function getPerfilAcesso() {
        return $this->perfilAcesso;
    }

    public function setPerfilAcesso($perfilAcesso) {
        $this->perfilAcesso = $perfilAcesso;
    }

    public function getLogin() {
        return $this->login;
    }

    public function setLogin($login) {
        $this->login = $login;
    }
    public function getEmail() {
        return $this->email;
    }

    public function setEmail($email) {
        $this->email = $email;
    }



}
?>


/app/model/Usuario.php
<?
//  @arquivo = /app/model/Usuario.php
//  MVC : model
//  objeto : Usuario
//  OBS : não existe uma tabela usuario no banco, e sim uma view que é uma junção
//  das tabelas clientes e funcionarios
 

class Usuario {

    var $id;
    var $login;
    var $senha;
    var $tipoUsuario;
    var $ativo;
    var $perfilAcesso;

    public function Usuario() {
        $this->id=0;
//        $this->admin=0;
    }

    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }

    public function getLogin() {
        return $this->login;
    }

    public function setLogin($login) {
        $this->login = $login;
    }

    public function getSenha() {
        return $this->senha;
    }

    public function setSenha($senha) {
        $this->senha = $senha;
    }

    public function getTipoUsuario() {
        return $this->tipoUsuario;
    }

    public function setTipoUsuario($tipoUsuario) {
        $this->tipoUsuario = $tipoUsuario;
    }

    public function getAtivo() {
        return $this->ativo;
    }

    public function setAtivo($ativo) {
        $this->ativo = $ativo;
    }


    public function getPerfilAcesso() {
        return $this->perfilAcesso;
    }

    public function setPerfilAcesso($perfilAcesso) {
        $this->perfilAcesso = $perfilAcesso;
    }

    
}
?>

DAOs:
/app/dao/UsuarioDAO.php
<?
require_once($_SERVER['DOCUMENT_ROOT'] . '/lib/Conexao.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Usuario.php');

class UsuarioDAO {
    public function UsuarioDAO(){}

    public function autenticar($login, $senha) {
        $Usuario = new Usuario();
        $conexao = new Conexao();
 $conexao->conecta();
        $sqlQuery = sprintf("Select id,login,senha,ativo,tipo_usuario,perfil_acesso from usuario where login='%s'
            and senha='%s'",$login,$senha);
//        die ($sqlQuery);
 $rs = $conexao->executeQuery($sqlQuery);
        if ($row=mysql_fetch_array($rs)) {
            $Usuario->setId($row['id']);
            $Usuario->setLogin($row['login']);
            $Usuario->setAtivo($row['ativo']);
            $Usuario->setTipoUsuario($row['tipo_usuario']);
            $Usuario->setPerfilAcesso($row['perfil_acesso']);
        }
        $conexao->desconecta();
        return $Usuario;
    }

    public function load(Usuario $user) {
        $conexao = new Conexao();
 $conexao->conecta();
        $sqlQuery = "Select id,senha,tipo_usuario from usuario where id=".$user->getId();
        $rs = $conexao->executeQuery($sqlQuery);
        if ($row=mysql_fetch_array($rs)) {
            $Usuario = new Usuario();
            $Usuario->setId($row['id']);
            $Usuario->setSenha($row['senha']);
            $Usuario->setTipoUsuario($row['tipo_usuario']);
        }
        $conexao->desconecta();
        return $Usuario;
        
    }

    public function atualizaSenhaCliente(Usuario $user, $novasenha) {
        $conexao = new Conexao();
 $conexao->conecta();
        $sqlQueryUpdate = "UPDATE cliente 
                SET 
                `senha`='".$novasenha."' 
                where id=".$user->getId();
        
        $rs = $conexao->executeUpdate($sqlQueryUpdate);
        $conexao->desconecta();
        
    }

    public function localizaPorEmail($email) {
        $conexao = new Conexao();
 $conexao->conecta();
        $sqlQuery = "Select id,senha,tipo_usuario from usuario where login='".$email."' ";
        $rs = $conexao->executeQuery($sqlQuery);
        $carregado=false;
        if ($row=mysql_fetch_array($rs)) {
            $Usuario = new Usuario();
            $Usuario->setId($row['id']);
            $Usuario->setSenha($row['senha']);
            $Usuario->setTipoUsuario($row['tipo_usuario']);
        
           $carregado=true;
        }
        $conexao->desconecta();
        
        if ($carregado){
            return $Usuario; 
        }
        
        else {
           return null; 
        }
        
    }

    public function localizaPorDoc($tipoDoc, $busca) {
        $conexao = new Conexao();
       $conexao->conecta();
        $carregado=false;
        $sqlQuery = "Select id,senha,tipo_usuario from usuario where $tipoDoc='".$busca."' ";
        $rs = $conexao->executeQuery($sqlQuery);
        if ($row=mysql_fetch_array($rs)) {
            $Usuario = new Usuario();
            $Usuario->setId($row['id']);
            $Usuario->setSenha($row['senha']);
            $Usuario->setTipoUsuario($row['tipo_usuario']);
        
           $carregado=true;
        }
        $conexao->desconecta();
        
        if ($carregado){
            return $Usuario; 
        }
        else {
           return null; 
        } 
        
    }
    
    
}?>


Controllers:
/app/controller/Usuario/autentica.php
<?
//  @arquivo = /app/view/Usuario/autentica.php - recebe as variaveis de login via post
session_start();

require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Usuario.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/controller/UsuarioController.php');


$user = new Usuario();
$userC = new UsuarioController();

if (isset($_POST['data']['Usuario']['login'])) {

    $login = $_POST['data']['Usuario']['login'];
}

if (isset($_POST['data']['Usuario']['senha'])) {

    $senha = $_POST['data']['Usuario']['senha'];
}

$user = $userC->autenticar($login, $senha);

if ($user->getId() == 0) {
    header("Location: /login.php");
}

if (($user->getId() != 0) && ($user->getTipoUsuario() == "C")) {
    $_SESSION['user']['logado'] = true;
    $_SESSION['user']['tipoUsuario'] = $user->getTipoUsuario();
    $_SESSION['user']['login'] = $user->getLogin();
    $_SESSION['user']['idUsuario'] = $user->getId();
    $_SESSION['user']['perfilAcesso'] = $user->getPerfilAcesso();
    $_SESSION['user']['view'] = "/cliente.php";
    header("Location: /pages/cliente.php?section=usuario&action=home");
}

if (($user->getId() != 0) && ($user->getTipoUsuario() == "F")) {
    $_SESSION['user']['logado'] = true;
    $_SESSION['user']['tipoUsuario'] = $user->getTipoUsuario();
    $_SESSION['user']['login'] = $user->getLogin();
    $_SESSION['user']['idUsuario'] = $user->getId();
    $_SESSION['user']['perfilAcesso'] = $user->getPerfilAcesso();


    switch ($_SESSION['user']['perfilAcesso']) {
        case "Admin":
            $_SESSION['user']['view'] = "/pages/privado.php";
            header("Location: ".$_SESSION['user']['view']);
            break;
        case "Gestor":
            $_SESSION['user']['view'] = "/pages/privado.php";
            header("Location: ".$_SESSION['user']['view']);
            break;
        case "Atendente":
//            $_SESSION['user']['view'] = "/atendente.php";
            $_SESSION['user']['view'] = "/pages/privado.php";
            header("Location: ".$_SESSION['user']['view']);
            break;

        default : // se tiver erro no cadastro, ou não tiver essa info
            header("Location: /login.php");
            break;
    }
}?>
/app/controller/UsuarioController.php 
<?
//  @arquivo = /app/model/UsuarioController.php
//  MVC : controller
//  objeto : UsuarioController
 
 
require_once($_SERVER['DOCUMENT_ROOT'].'/lib/Conexao.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/app/model/Usuario.php');
require_once($_SERVER['DOCUMENT_ROOT'].'/app/dao/UsuarioDAO.php');

class UsuarioController {
    
    function autenticar($login,$senha){
        
        $usuarioDAO = new UsuarioDAO();
        $Usuario = $usuarioDAO->autenticar($login,$senha); 
        return $Usuario;
    }

    public function load(Usuario $user) {
        $usuarioDAO = new UsuarioDAO();
        $Usuario = $usuarioDAO->load($user); 
        
        return $Usuario;
    }
    
    
    // DEVE SER USADO APENAS P/ CLIENTES
    public function atualizaSenha(Usuario $user, $novasenha){
        $usuarioDAO = new UsuarioDAO();
        $usuarioDAO->atualizaSenhaCliente($user, $novasenha);
    }
 
    
    public function localizaUsuario($email){
        
        $usuarioDAO = new UsuarioDAO();
        $Usuario = $usuarioDAO->localizaPorEmail($email); 
        return $Usuario;
    }

    public function localizaUsuarioPorDocumento($tipoDoc, $busca) {
        
       $usuarioDAO = new UsuarioDAO();
       $Usuario = $usuarioDAO->localizaPorDoc($tipoDoc, $busca); 
       
       return $Usuario;
    }
    
    
}
?>


Views:
/login.php
<?
session_start();
if (isset($_GET['action']) && $_GET['action'] == 'logout') {
    unset($_SESSION['user']['logado'], $_SESSION['user']['tipoUsuario'], $_SESSION['user']['login'], $_SESSION['user']['idUsuario'], $_SESSION['user']['perfilAcesso'], $_SESSION['user']['view']
    );
}
?>
<fieldset>
<form action="/app/controller/Usuario/autentica.php" method="POST" id="formLogin" name="formLogin">
   <label for="Login">Login:</label>
   <input name="data[Usuario][login]" maxlength="30" type="text" id="login"/>
   <label for="Senha">Senha:</label>
   <input name="data[Usuario][senha]" maxlength="15" type="password" id="senha"/>
   <div class="submit"><input  type="submit" value="@Login"/></div>
</form>
</fieldset>






/lib/Conexao.php --descrita aqui


Agora vc pode optar por 2 alternativas 
1 - Cria o arquivo trava.php p/ ser incluido em todas as paginas 
<?
// arquivo /lib/trava.php
session_start();
if (!isset($_SESSION['user']['logado'] ))  {
   header("Location: /login.php?message=2");
}?>

e em todas paginas :
<? require_once($_SERVER['DOCUMENT_ROOT'] ."/lib/trava.php"); ?> 

2 - ou implementa o seu próprio framework a partir daqui :  

 /lib/Utils.php
<?
//  @arquivo = /lib/Utils.php
//  MVC :  controller
//  objeto :
//  obs : as funções comuns ao projeto

function includeFile($file) {
    try {
        if (file_exists($file)) {
            require_once $file;
        } else {
            //throw new Exception("Arquivo '$file' n&atilde;o encontrado");
            throw new Exception("A operação deste modulo '$file' ainda  n&atilde;o foi implementada.");
        }
    } catch (Exception $ex) {
        echo $ex->getMessage();
    }
}

function includeNewFile($file) {
    try {
  $fileX = $_SERVER['DOCUMENT_ROOT'].$file;
//  die($fileX);
        if (file_exists($fileX)) {
            require_once $fileX;
        } else {
            //throw new Exception("Arquivo '$file' n&atilde;o encontrado");
            throw new Exception("A operação deste modulo '$file' ainda  n&atilde;o foi implementada.");
        }
    } catch (Exception $ex) {
        echo $ex->getMessage();
    }
}
?>


/pages/* ou views


opcoes.php
<?php
error_reporting('E_ALL');

if (!$_SESSION['user']["logado"]) {
 header("Location: /login.php");
}

require_once($_SERVER['DOCUMENT_ROOT'] .'/lib/Utils.php');
if (isset($_GET['section'])){
 $section= $_GET['section'];
 switch($section){
  
  case 'clientes':
   $model='Cliente';
   $controller='ClienteController';    
  break;
  case 'servicos':
   $model='Servico';
   $controller='ServicoController';
  break;
  case 'tarefas':
   $model='Tarefa';
   $controller='TarefaController';
  break;
  case 'usuario':
   $model='Usuario';
   $controller='UsuarioController';
  break;
                case 'funcionarios':
   $model='Funcionario';
   $controller='FuncionarioController';
                        
  break;
                case 'boletos':
   $model='Boleto';
   $controller='BoletoController';
                        
  break;
  
  default:
   $model='empty';
   $controller='empty';
   break;
 }

includeNewFile("/app/controller/".$controller.".php"); 
includeNewFile("/app/model/".$model.".php");

}
$pageAction=''; // pagina de acao
if (isset($_GET['action'])){
 $action= $_GET['action'];
 switch($action){
            /*
  case 'index':
   $pageAction='index.php';
  break;
  case 'add':
   $pageAction='add.php';
  break;
  case 'edit':
   $pageAction='edit.php';
  break;
  case 'remove':
   $pageAction='remove.php';
  break;
  case 'view':
   $pageAction='view.php';
  break;  
  case 'login':
   $pageAction='login.php';
  break;
 */

  default:
   $pageAction=$action.'.php';
  break;
 
            
  }

}
?>


view.php
<?php
if (isset($_GET['section'])){ 
 $section = $_GET['section'];

 if ($section!=''){
  try {
   includeNewFile("/app/view/".$model."/".$pageAction); 
  } 
  
 catch (Exception $err){
  echo $err->getMessage();
 
  }
 }
}
?>


/pages/privado.php essa é a página do funcionário que faz todos os includes
<?
session_start();
error_reporting('E_ALL');

if (!isset($_SESSION['user']['logado'] ))  {
   header("Location: /login.php?message=2");
}
// gestor, admin e atendente tem acesso a essa pagina
if (!isset($_SESSION['user']['perfilAcesso']) || $_SESSION['user']['perfilAcesso']=="Cliente"  )  {
   header("Location: /login.php?message=2");
}
require_once($_SERVER['DOCUMENT_ROOT'] ."/pages/opcoes.php");  
?>
<!doctype html>
<?php require_once($_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_conditionals.php"); ?>
<head>
<?php 
$pageName = "TITULO";
include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_head-meta.php"; 
?>
</head>

<body>
<div id="container" class="box_round_0 box_shadow_0">
  <?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_header.php"; ?>
  <div id="main" class="clearfix">
    <div id="content">
        <? require_once('view.php'); ?>
    </div></div>
<?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_footer.php"; ?>
  </div>

<?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_pos-scripts.php"; ?>
</body>
</html>    


/pages/cliente.php essa é a página do cliente que faz todos os includes obs:esse arquivo não aparece na foto do projeto acima
<?
session_start();
error_reporting('E_ALL');

if (!isset($_SESSION['user']['logado'] ))  {
   header("Location: /login.php?message=2");
}
// gestor, admin e atendente tem acesso a essa pagina
if (!isset($_SESSION['user']['perfilAcesso']) || $_SESSION['user']['perfilAcesso']!="Cliente"  )  {
   header("Location: /login.php?message=2");
}
require_once($_SERVER['DOCUMENT_ROOT'] ."/pages/opcoes.php");  
?>
<!doctype html>
<?php require_once($_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_conditionals.php"); ?>
<head>
<?php 
$pageName = "TITULO";
include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_head-meta.php"; 
?></head>

<body>
<div id="container" class="box_round_0 box_shadow_0">
  <?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_header.php"; ?>
  <div id="main" class="clearfix">
    <div id="content">
        <? require_once('view.php'); ?>
    </div>
 </div>
<?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_footer.php"; ?>
  </div>
<?php include $_SERVER['DOCUMENT_ROOT'] ."/static_includes/inc_pos-scripts.php"; ?>
</body>
</html>

e Pronto. As seções estão separadas.
Agora a base do site na url fica assim após o Login:
http://seudominio/pages/privado.php?section=clientes&action=index e o arquivo /static_includes/inc_header.php pode conter os links p/ as seções

/static_includes/inc_header.php
<?
if (isset($_SESSION['user']['logado'])) {
    $userLogado = $_SESSION['user']['logado'];
} else {
    $userLogado = false;
}
?>
<div>
 <nav>
 <ul class="main-menu">
            <li><a href="/index.php" <?php if ($pageName == 'Home') echo 'class="current"'; ?>>Home</a></li>
                    <? if ($userLogado) { ?>
                 <li><a href="<? echo $_SESSION['user']['view']; ?>?section=boletos&action=index" <?php if ($_GET['section'] == 'boletos') echo 'class="current"'; ?>>Boletos</a></li>
                 <li><a href="<? echo $_SESSION['user']['view']; ?>?section=funcionarios&action=index"<?php if ($_GET['section'] == 'funcionarios') echo 'class="current"'; ?> >Funcionarios</a> </li>
                <li><a href="<? echo $_SESSION['user']['view']; ?>?section=clientes&action=index"<?php if ($_GET['section'] == 'clientes') echo 'class="current"'; ?> >Clientes</a> </li>
                    <? } ?>

                </ul>
</nav>











sexta-feira, 13 de julho de 2012

MVC PHP- PHP OOP


Olá. Neste post pretendo demonstrar um exemplo simples e prático de como usar o PHP orientado a objetos no esquema MVC (Model View Controller).
Observação : esse esquema passa a funcionar muito bem a partir do PHP 5.3

O resultado final será uma página que lista os clientes cadastrados.
(nos próximos posts de PHP posso detalhar as funções CRUD)

Não pretendo discutir os conceitos de MVC, apenas mostrar sua implementação em PHP, destacando a facilidade de construção e manutenção de código, quando as partes (camadas) da aplicação estão separadas em :
M- Modelo de Dados: estrutura dos Objetos (Classes)
V-View: Interface de apresentação dos dados p/ o Usuário (Telas ou páginas)
C- Controle : Regras de negócios (Componentes e Controllers)


Essas camadas devem ser independentes, ou seja : se mudar alguma regra de negócio, esta deve ser implementada na camada de Controle, assim como se a interface for alterada, não existe a a necessidade de se mexer nas regras.


Eu costumo utilizar mais uma "camada" em minhas aplicações conhecida como DAO (Data Access Object) que é a camada responsável pela comunicação com o Banco de Dados. Esta é utilizada pela camada de Controle para carregar os Modelos com os dados e entregar para as Views.


Figura 1- Resumo MVC


Exemplo de organização de código : (Neste exemplo mostrarei com a seção clientes)
Figura 2- Estrutura de pastas e arquivos


--App :
--Controller : cada Objeto tem o seu
, é onde ficam as regras de negócio



--DAO : cada Objeto tem o seu


--Model : quase todas as entidades do DB possuem o seu Objeto e outras informações que sejam compartilhadas em várias seções do site 



View : cada Objeto tem sua pasta com as operações básicas de qualquer CRUD (Create Read Update Delete) e um índice (lista inicial)
C->  add.php
R->  view.php
U->  edit.php
D-> remove.php


--lib : pasta que contém as principais funções compartilhadas  do sistema.




pastas com itens de layout do site :
--css : estilos css
--images : imagens da seção pública
--img : imagens da seção privada
--js : arquivos javascript
--pages : as páginas do site
--static_includes : arquivos que não mudam : meta-tags, cabeçalho, rodapé










Tabelas do Banco de Dados (MYSQL nesse exemplo):
Cliente,Estado,Cidade
Figura 3- Diagrama do DB

-- estado 
CREATE  TABLE IF NOT EXISTS `mydb`.`estado` (
  `id` INT(11) NOT NULL AUTO_INCREMENT ,
  `uf` VARCHAR(2) NULL DEFAULT NULL ,
  `nome` VARCHAR(45) NULL DEFAULT NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB
AUTO_INCREMENT = 28
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci

-- cidade 
CREATE  TABLE IF NOT EXISTS `mydb`.`cidade` (
  `id` INT(11) NOT NULL AUTO_INCREMENT ,
  `estado_id` INT(11) NOT NULL ,
  `uf` VARCHAR(2) NULL DEFAULT NULL ,
  `nome` VARCHAR(120) NULL DEFAULT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `estado_cidade_FK` (`estado_id` ASC) ,
  CONSTRAINT `estado_cidade_FK`
    FOREIGN KEY (`estado_id` )
    REFERENCES `mydb`.`estado` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
AUTO_INCREMENT = 9715
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci


-- cliente

CREATE  TABLE IF NOT EXISTS `mydb`.`cliente` (
  `id` INT(11) NOT NULL AUTO_INCREMENT ,
  `nome` VARCHAR(255) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `email` VARCHAR(130) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `ddd` VARCHAR(3) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `fone` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `ramal` VARCHAR(6) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `endereco` VARCHAR(255) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `numero` VARCHAR(7) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `complemento` VARCHAR(30) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `bairro` VARCHAR(150) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `cidade_id` INT(11) NOT NULL ,
  `estado_id` INT(11) NOT NULL ,
  `cep` VARCHAR(9) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL ,
  `tipo_end` ENUM('Res','Com','Cor') CHARACTER SET 'utf8' COLLATE 'utf8_bin' NOT NULL DEFAULT 'Res' ,
  `tipo_fone` ENUM('Res','Com','Cel') CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT 'Res' ,
  `tipo_cliente` ENUM('pf','pj') CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `cnpj` VARCHAR(20) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `cpf` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `inscr_estadual` VARCHAR(15) CHARACTER SET 'utf8' COLLATE 'utf8_bin' NULL DEFAULT NULL ,
  `senha` VARCHAR(8) NULL DEFAULT NULL ,
  `ativo` TINYINT(1) NOT NULL DEFAULT '1' ,
  `contato` VARCHAR(150) NULL DEFAULT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `cidade_cliente_FK` (`cidade_id` ASC) ,
  INDEX `estado_cliente_FK` (`estado_id` ASC) ,
  CONSTRAINT `cidade_cliente_FK`
    FOREIGN KEY (`cidade_id` )
    REFERENCES `mydb`.`cidade` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `estado_cliente_FK`
    FOREIGN KEY (`estado_id` )
    REFERENCES `mydb`.`estado` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci




Após algumas introduções , vamos ao código : 
De onde veio este exemplo existem clientes e funcionários.Ambos são pessoas.
Assim sendo temos a classe base Pessoa e as classes que estendem Pessoa que são Cliente e Funcionario. 
/app/model/Pessoa.php
//  @arquivo = /app/model/Pessoa.php
//  MVC : model
//  objeto : Pessoa

class Pessoa {

    var $id;
    var $nome;

    //construtor para poder usar o operador new()
    public function Pessoa() {}

    public function getId() {
        return $this->id;
    }

    public function setId($id) {
        $this->id = $id;
    }

    public function getNome() {
        return $this->nome;
    }

    public function setNome($nome) {
        $this->nome = $nome;
    }

}
/app/model/Cliente.php
//  @arquivo = /app/model/Cliente.php
//  MVC : model
//  objeto : Cliente

require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Pessoa.php');
class Cliente extends Pessoa{

    var $email;
    var $ddd;
    var $fone;
    var $ramal;
    var $endereco;
    var $enderecoNum;
    var $enderecoComplemento;
    var $bairro;
    var $cidadeId;
    var $estadoId;
    var $cep;
    var $tipoEndereco;
    var $tipoFone;
    var $tipoCliente;
    var $cnpj;
    var $cpf;
    var $inscrEstadual;
    var $senha;
    //auxiliar
    var $cidadeNome;
    var $estadoNome;

    public function Cliente() {
        $this->id=0;
    }

    //funcoes automagicas pode fazer assim 
    function __set($param, $value) {
        $this->$param = $value;
    }

    function __get($var) {
        return $this->$var;
    }
    // ou fazer todos os getters e setters 
    public function getEmail() {
        return $this->email;
    }

    public function setEmail($email) {
        $this->email = $email;
    }

    public function getDdd() {
        return $this->ddd;
    }

    public function setDdd($ddd) {
        $this->ddd = $ddd;
    }

    public function getFone() {
        return $this->fone;
    }

    public function setFone($fone) {
        $this->fone = $fone;
    }

    public function getRamal() {
        return $this->ramal;
    }

    public function setRamal($ramal) {
        $this->ramal = $ramal;
    }

    public function getEndereco() {
        return $this->endereco;
    }

    public function setEndereco($endereco) {
        $this->endereco = $endereco;
    }

    public function getEnderecoNum() {
        return $this->enderecoNum;
    }

    public function setEnderecoNum($enderecoNum) {
        $this->enderecoNum = $enderecoNum;
    }

    public function getEnderecoComplemento() {
        return $this->enderecoComplemento;
    }

    public function setEnderecoComplemento($enderecoComplemento) {
        $this->enderecoComplemento = $enderecoComplemento;
    }

    public function getBairro() {
        return $this->bairro;
    }

    public function setBairro($bairro) {
        $this->bairro = $bairro;
    }

    public function getCidadeId() {
        return $this->cidadeId;
    }

    public function setCidadeId($cidadeId) {
        $this->cidadeId = $cidadeId;
    }

    public function getEstadoId() {
        return $this->estadoId;
    }

    public function setEstadoId($estadoId) {
        $this->estadoId = $estadoId;
    }

    public function getCep() {
        return $this->cep;
    }

    public function setCep($cep) {
        $this->cep = $cep;
    }

    public function getTipoEndereco() {
        return $this->tipoEndereco;
    }

    public function setTipoEndereco($tipoEndereco) {
        $this->tipoEndereco = $tipoEndereco;
    }

    public function getTipoFone() {
        return $this->tipoFone;
    }

    public function setTipoFone($tipoFone) {
        $this->tipoFone = $tipoFone;
    }

    public function getTipoCliente() {
        return $this->tipoCliente;
    }

    public function setTipoCliente($tipoCliente) {
        $this->tipoCliente = $tipoCliente;
    }

    public function getCnpj() {
        return $this->cnpj;
    }

    public function setCnpj($cnpj) {
        $this->cnpj = $cnpj;
    }

    public function getCpf() {
        return $this->cpf;
    }

    public function setCpf($cpf) {
        $this->cpf = $cpf;
    }

    public function getInscrEstadual() {
        return $this->inscrEstadual;
    }

    public function setInscrEstadual($inscrEstadual) {
        $this->inscrEstadual = $inscrEstadual;
    }

    public function getSenha() {
        return $this->senha;
    }

    public function setSenha($senha) {
        $this->senha = $senha;
    }

    public function getCidadeNome() {
        return $this->cidadeNome;
    }

    public function setCidadeNome($cidadeNome) {
        $this->cidadeNome = $cidadeNome;
    }

    public function getEstadoNome() {
        return $this->estadoNome;
    }

    public function setEstadoNome($estadoNome) {
        $this->estadoNome = $estadoNome;
    }

    
}
/app/controller/ClienteController.php
//  @arquivo = /app/controller/ClienteController.php
//  MVC : controller
//  objeto : Cliente

require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Cliente.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/dao/ClienteDAO.php');

class ClienteController {
    // nao é necessario criar um construtor
    function add(Cliente $cliente) {
        $clienteDAO = new ClienteDAO();
        $clienteDAO->adicionarCliente($cliente);
    }

    function edit(Cliente $cliente) {
        $clienteDAO = new ClienteDAO();
        $clienteDAO->atualizarCliente($cliente);
    }

    function remove($clienteId) {
        $clienteDAO = new ClienteDAO();
        $clienteDAO->removerCliente($clienteId);
    }

    function desativar($clienteId) {
        $clienteDAO = new ClienteDAO();
        $clienteDAO->desativarCliente($clienteId);
    }

    public function loadOptions($idPreSelecionado) { // p/ montar os combos
        $clienteDAO = new ClienteDAO();
        $clientes = $clienteDAO->getClientes();
        $conta = 0;

        foreach ($clientes as $cliente) {

            if ($idPreSelecionado == $cliente->getId()) {
                echo " \n";
            } else {
                echo " \n";
            }
            $conta++;
        }

        if ($conta == 0) {
            echo " \n";
        }
    }

    public function buscaOptions($busca) {
        $clienteDAO = new ClienteDAO();
        $clientes = $clienteDAO->getClientesBusca($busca);
        $conta = 0;


        foreach ($clientes as $cliente) {
            echo " \n";
            $conta++;
        }

        if ($conta == 0) {
            echo " \n";
        }
    }

    public function atualizaPerfil(Cliente $cliente) {
        $clienteDAO = new ClienteDAO();
        $clienteDAO->atualizarPerfil($cliente);
    }

    public function getDadosPaginados($primeiroRegistro, $numPorPagina, $buscaCliente) {

        $clienteDAO = new ClienteDAO();
        $clientes = $clienteDAO->getClientesPaginados($primeiroRegistro, $numPorPagina, $buscaCliente);

        return $clientes;
    }

    public function getTotalClientes($buscaCliente) {
        $clienteDAO = new ClienteDAO();
        $total = $clienteDAO->getTotalClientes($buscaCliente);

        return $total;
    }

    public function loadDados($id) {
        $clienteDAO = new ClienteDAO();
        $cliente = $clienteDAO->load($id);
        return $cliente;
    }

    public function localizarReexecucoes(Cliente $cliente) {

        $clienteDAO = new ClienteDAO();
        $total = $clienteDAO->getTotalReexecucoes($cliente);
        return $total;
    }

    public function getListaClientes() {
        $clienteDAO = new ClienteDAO();
        $clientes = $clienteDAO->getClientes();
        return $clientes;
    }

}
/app/dao/ClienteDAO.php
//  @arquivo = /app/dao/ClienteDAO.php
//  MVC : controller
//  objeto : Cliente

require_once($_SERVER['DOCUMENT_ROOT'] . '/app/model/Cliente.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/lib/Conexao.php');

class ClienteDAO {

    public function ClienteDAO() {
        
    }

    public function adicionarCliente(Cliente $cliente) {
        $sqlQuery = "INSERT INTO `mydb`.`cliente`
                (
                `nome`,
                `email`,
                `ddd`,
                `fone`,
                `ramal`,
                `endereco`,
                `numero`,
                `complemento`,
                `bairro`,
                `cidade_id`,
                `estado_id`,
                `cep`,
                `tipo_end`,
                `tipo_fone`,
                `tipo_cliente`,
                `cnpj`,
                `cpf`,
                `inscr_estadual`,
                `senha`)
                VALUES
                ("
                . "'" . $cliente->getNome() . "',"
                . "'" . $cliente->getEmail() . "',"
                . "'" . $cliente->getDdd() . "',"
                . "'" . $cliente->getFone() . "',"
                . "'" . $cliente->getRamal() . "',"
                . "'" . $cliente->getEndereco() . "',"
                . "'" . $cliente->getEnderecoNum() . "',"
                . "'" . $cliente->getEnderecoComplemento() . "',"
                . "'" . $cliente->getBairro() . "',"
                . $cliente->getCidadeId() . ","
                . $cliente->getEstadoId() . ","
                . "'" . $cliente->getCep() . "',"
                . "'" . $cliente->getTipoEndereco() . "',"
                . "'" . $cliente->getTipoFone() . "',"
                . "'" . $cliente->getTipoCliente() . "',"
                . "'" . $cliente->getCnpj() . "',"
                . "'" . $cliente->getCpf() . "',"
                . "'" . $cliente->getInscrEstadual() . "',"
                . "'" . $cliente->getSenha() . "' "
                . ")";


        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeUpdate($sqlQuery);
        $conexao->desconecta();
    }

    public function atualizarCliente(Cliente $cliente) {
        $sqlQueryUpdate = "UPDATE  `mydb`.`cliente`
                 SET "
                . "`nome`='" . $cliente->getNome() . "',"
                . "`email`='" . $cliente->getEmail() . "',"
                . "`ddd`='" . $cliente->getDdd() . "',"
                . "`fone`='" . $cliente->getFone() . "',"
                . "`ramal`='" . $cliente->getRamal() . "',"
                . "`endereco`='" . $cliente->getEndereco() . "',"
                . "`numero`='" . $cliente->getEnderecoNum() . "',"
                . "`complemento`='" . $cliente->getEnderecoComplemento() . "',"
                . "`bairro`='" . $cliente->getBairro() . "',"
                . "`cidade_id`=" . $cliente->getCidadeId() . ","
                . "`estado_id`=" . $cliente->getEstadoId() . ","
                . "`cep`='" . $cliente->getCep() . "',"
                . "`tipo_end`='" . $cliente->getTipoEndereco() . "',"
                . "`tipo_fone`='" . $cliente->getTipoFone() . "',"
                . "`tipo_cliente`='" . $cliente->getTipoCliente() . "',"
                . "`cnpj`='" . $cliente->getCnpj() . "',"
                . "`cpf`='" . $cliente->getCpf() . "',"
                . "`inscr_estadual`='" . $cliente->getInscrEstadual() . "',"
                . "`senha`='" . $cliente->getSenha() . "' "
                . "WHERE `id`=" . $cliente->getId();
        //echo 'entrei aqui';
        //print_r($cliente);
        // echo $sqlQueryUpdate;
        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeUpdate($sqlQueryUpdate);
        $conexao->desconecta();
    }

    public function removerCliente($clienteId) {
        $sqlQuery = "DELETE FROM `mydb`.`cliente` "
                . "WHERE `id`=" . $clienteId;
        //echo $sqlQuery; 
        $conexao = new Conexao();
        $conexao->conecta();
//        try{
        $rs = $conexao->executeUpdate($sqlQuery);
        $conexao->desconecta();
    }

    public function desativarCliente($clienteId) {
        $sqlQuery = "UPDATE  `mydb`.`cliente` 
  SET ativo = false "
                . "WHERE `id`=" . $clienteId;
        //echo $sqlQuery; 
        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeUpdate($sqlQuery);
        $conexao->desconecta();
    }

    public function getClientes() {

        $arrClientes = array();
        $indice = 0;
        $sqlQuery = "Select id,nome from cliente order by nome";

        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeQuery($sqlQuery);

        while ($row = mysql_fetch_array($rs)) {
            $cliente = new Cliente();
            $cliente->setId($row['id']);
            $cliente->setNome($row['nome']);
            $arrClientes[$indice] = $cliente;
            $indice++;
        }

        $conexao->desconecta();
        return $arrClientes;
    }

    public function getClientesBusca($busca) {
        $arrClientes = array();
        $indice = 0;
        $sqlQuery = "Select id,nome from cliente where nome like '%" . $busca . "%' order by nome";

        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeQuery($sqlQuery);

        while ($row = mysql_fetch_array($rs)) {
            $cliente = new Cliente();
            $cliente->setId($row['id']);
            $cliente->setNome($row['nome']);
            $arrClientes[$indice] = $cliente;
            $indice++;
        }

        $conexao->desconecta();
        return $arrClientes;
    }

    public function atualizarPerfil(Cliente $cliente) {
        $sqlQueryUpdate = "UPDATE  `mydb`.`cliente`
                 SET "
                . "`nome`='" . $cliente->getNome() . "',"
                . "`email`='" . $cliente->getEmail() . "',"
                . "`ddd`='" . $cliente->getDdd() . "',"
                . "`fone`='" . $cliente->getFone() . "',"
                . "`ramal`='" . $cliente->getRamal() . "',"
                . "`tipo_fone`='" . $cliente->getTipoFone() . "',"
//                . "`senha`='" . $cliente->getSenha() . "' "
                . "WHERE `id`=" . $cliente->getId();
        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeUpdate($sqlQueryUpdate);
        $conexao->desconecta();
    }

    public function getClientesPaginados($primeiroRegistro, $numPorPagina, $buscaCliente) {
        $sqlQuery = "Select CL.*,C.nome as cidade,E.nome as estado 
from cliente CL left join estado E on (CL.estado_id = E.id) 
left join  cidade C on (CL.cidade_id=C.id ) ";
        if ($buscaCliente != NULL) {
            $sqlQuery.=" WHERE CL.nome like '%" . $buscaCliente . "%' ";
        }

        $sqlQuery.= " LIMIT " . $primeiroRegistro . "," . $numPorPagina;
        $conexao = new Conexao();
        $conexao->conecta();
        $rs = $conexao->executeQuery($sqlQuery);
        $cache = array();
        while ($row = mysql_fetch_array($rs)) {

            $cliente = new Cliente();
            $cliente->setId($row['id']);
            $cliente->setNome($row['nome']);
            $cliente->setEmail($row['email']);
            $cliente->setDdd($row['ddd']);
            $cliente->setFone($row['fone']);
            $cliente->setRamal($row['ramal']);
            $cliente->setEndereco($row['endereco']);
            $cliente->setEnderecoNum($row['numero']);
            $cliente->setEnderecoComplemento($row['complemento']);
            $cliente->setBairro($row['bairro']);
            $cliente->setCidadeId($row['cidade_id']);
            $cliente->setEstadoId($row['estado_id']);
            $cliente->setTipoEndereco($row['tipo_end']);
            $cliente->setTipoFone($row['tipo_fone']);
            $cliente->setTipoCliente($row['tipo_cliente']);
            $cliente->setCnpj($row['cnpj']);
            $cliente->setCpf($row['cpf']);
            $cliente->setInscrEstadual($row['inscr_estadual']);
            $cliente->setSenha($row['senha']);

            $cliente->setCidadeNome($row['cidade']);
            $cliente->setEstadoNome($row['estado']);
            $line = array(
                'id' => $cliente->getId(),
                'nome' => $cliente->getNome(),
                'email' => $cliente->getEmail()
            );
            array_push($cache, $line);
        }

        $conexao->desconecta();
        return $cache;
    }

    public function getTotalClientes($buscaCliente) {

        $conexao = new Conexao();
        $conexao->conecta();
        $sqlQuery = "SELECT COUNT(*) as total FROM mydb.cliente C ";
        if ($buscaCliente != NULL) {
            $sqlQuery.=" WHERE C.nome like '%" . $buscaCliente . "%' ";
        }
        $rs = $conexao->executeQuery($sqlQuery);
        $total = 0;
        while ($row = mysql_fetch_array($rs)) {
            $total = $row['total'];
        }

        $conexao->desconecta();
    }

    public function load($id) {

        $conexao = new Conexao();
        $conexao->conecta();
        $sqlQuery = "SELECT *  FROM mydb.cliente C where id=" . $id;
        $rs = $conexao->executeQuery($sqlQuery);
        $cliente = new Cliente();
        if ($row = mysql_fetch_array($rs)) {

            $cliente->setId($row['id']);
            $cliente->setNome($row['nome']);
            $cliente->setEmail($row['email']);
            $cliente->setTipoCliente($row['tipo_cliente']);
            $cliente->setCpf($row['cpf']);
            $cliente->setCnpj($row['cnpj']);
            $cliente->setDdd($row['ddd']);
            $cliente->setFone($row['fone']);
            $cliente->setRamal($row['ramal']);
            $cliente->setEndereco($row['endereco']);
            $cliente->setEnderecoNum($row['numero']);
            $cliente->setEnderecoComplemento($row['complemento']);
            $cliente->setBairro($row['bairro']);
            $cliente->setCidadeId($row['cidade_id']);
            $cliente->setEstadoId($row['estado_id']);
            $cliente->setTipoEndereco($row['tipo_end']);
            $cliente->setTipoFone($row['tipo_fone']);
            $cliente->setInscrEstadual($row['inscr_estadual']);
//            $cliente->setSenha($row['senha']);
        }
        $conexao->desconecta();
        return $cliente;
    }

    public function getTotalReexecucoes(Cliente $cliente) {
        $conexao = new Conexao();
        $conexao->conecta();
        $total = 0;
        $sqlQuery = "Select count(*) as total from ordem_servico OS 
LEFT JOIN servicos_ordem_servico SOS on (SOS.ordem_servico_id = OS.id)
where OS.cliente_id=" . $cliente->getId() . "
and SOS.reexecucao=1
and OS.data_entrada >= DATE_SUB(NOW(), INTERVAL 3 MONTH) ";
        $rs = $conexao->executeQuery($sqlQuery);
        while ($row = mysql_fetch_array($rs)) {
            $total = $row['total'];
        }
        $conexao->desconecta();
        return $total;
    }

}
/lib/Conexao.php
<?
/*
  @arquivo = /lib/Conexao.php
  MVC :  controller
  objeto : Conexao
  obs : arquivo em uso na versão corrente
 */

Class Conexao {

    var $host;
    var $user;
    var $pass;
    var $dbname;
    var $data;
    var $db;
    var $saida;
    var $status;
    var $entrada;

    function Conexao() {
        
    }

    public function conecta() {
        $this->status = 0;
        $this->host = "SERVIDOR";
        $this->user = "LOGIN_BANCO";
        $this->pass = "SENHA_BANCO";
        $this->dbname = "NOME_DB";
        $this->db = mysql_connect($this->host, $this->user, $this->pass);

        if (!$this->db) {
//  echo "Erro ao conectar no banco".trigger_error(mysql_error(),E_USER_ERROR);
            echo "Erro ao conectar no banco" . mysql_error();
        } else {
            //echo "Conectado no banco";
            $this->status = 1;
        }
        mysql_select_db($this->dbname, $this->db);
        mysql_set_charset('utf8');
    }

    public function executeQuery($query) {
        if ($this->status == 1) {
            //echo "lista...";
            if ($this->saida = mysql_query($query)) {
                // echo 'Rs loaded';
                return $this->saida;
            } else {
                echo "<pre class=\"error\">";
                echo "SQL ERROR: " . mysql_error();
                echo "SQL : " . $query;
                echo "</pre>";
                desconecta();
            }
        }
    }

    public function executeUpdate($query) {
        if ($this->status == 1) {
            if ($this->entrada = mysql_query($query)) {
                return true;
            } else {
                echo "<pre class=\"error\">";
                echo "SQL ERROR: " . mysql_error();
                echo "</pre>";
                desconecta();
                return false;
            }
        }
    }

    function desconecta() {
        return mysql_close($this->db);
    }

}

?>
Agora com a estrutura completa, basta criar a View que mostrará a lista de clientes /pages/listaClientes.php
//  @arquivo = /pages/listaClientes.php
//  @arquivo = /pages/listaClientes.php
//  MVC :  view
//  objeto : Cliente
//  obs : exemplo para o blog

//se o container já não tiver incluido, inclui
require_once($_SERVER['DOCUMENT_ROOT'] . '/app/controller/ClienteController.php');

$clienteC = new ClienteController();
$cache = $clienteC->getListaClientes(); // carrega os registros do array para a view
$total = count($cache);
?>
<table width="85%" border="0" cellspacing="2" cellpadding="0">
    <tr>
        <th>ID</th>
        <th>NOME</th>
        <th>EMAIL</th>
    </tr>
    <? if ($total > 0) { 
        for ($i = 0; $i < $total; $i++) {
            $idCliente = $cache[$i]->getId();
            $clienteNome = $cache[$i]->getNome();
            ?>
            <tr>
                <td><? echo $idCliente; ?></td>
                <td><? echo $clienteNome; ?></td>
                <td><? echo $cache[$i]->getEmail(); ?></td>
            </tr>
            <?
        }
    }
    ?>
    <tr>
        <th colspan="3">Total: <? echo $total; ?></th>
    </tr>
</table>

Resultado : 
Figura 4- Resultado


e para dar carga nessa tabela troquei o seguinte trecho da linha 11: 
$arrClientes=array();
    for ($x=0;$x<=5;$x++){        
        $cliente = new Cliente();
            $cliente->setId($x);
            $cliente->setNome("Cliente ".$x);
            $cliente->setEmail("cliente".$x."@exemploblog");
            $arrClientes[$x] = $cliente;
    }

$cache = $arrClientes;


Simples né ? Espero ter simplificado a implementação MVC em uma aplicação PHP.


Até a próxima...