(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çõ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">< anterior</span> <? } else { ?> <span class="prev"><a href=" <? echo $basePage . $anterior; ?>"><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óxima ></span> <? } else { ?> <span class="next"><a href="<? echo $basePage . $proximo; ?>">próxima ></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ã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çõ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">< anterior</span> <? } else { ?> <span class="prev"><a href=" <? echo $basePage . $anterior; ?>"><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óxima ></span> <? } else { ?> <span class="next"><a href="<? echo $basePage . $proximo; ?>">próxima ></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çõ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...