Aqui está mais um plugin de minha autoria. Este cumpre muito bem o que se propõem em fazer: gerar CONDITIONS para um método FIND. É verdade, tem um monte de plugins na internet que fazem isso, entretanto, o Filter Results é compatível com o Paginate, o que o torna mais interessante, não é? =]

Filter Results

Basicamente, com o Filter Results você poderá fazer formulários de pesquisa da maneira que você desejar, de forma rápida e padronizada para todo a sua aplicação.

Compatibilidade

Instalação

Download

Antes de iniciar, baixe o Filter Results. Copie os arquivos para a pasta /app/plugins/filter_results ou em outro diretório para plugins do CakePHP.

Se preferir, utilize o git:
git clone -b 1.3 https://github.com/pedroelsner/filter_results.git

Configurações

Vamos adicionar nas variáveis $components e $helpers no app_controller.php:

Parâmetros de configuração:

  • auto->paginate Se você definir como TRUE, o Paginate será configurado automaticamente.
  • auto->explode Se você definir como TRUE, o valor de pesquisa será quebrado pelo explode->character e concatenado pela condição explode->concatenate.

Antes de tudo

Para este e os próximos exemplos, vamos ter como base o seguinte banco de dados:

Beleza? Então vamos começar!

Filtro Simples

Bom, após gerar as telas pelo Bake ou Bake UFT-8 temos o a seguinte action em nosso Controller:

Arquivo /app/controller/users_controller.php

Vamos então colocar um filtro sobre a grid(tabela) para pesquisar um usuário pelo nome(name) e configurar o Paginate da action.

Arquivo /app/controller/users_controller.php

A configuração aqui é bem simples: Criamos um filtro chamado filter1 que utilizará o campo User.name. Este filtro utiliza o operador LIKE e adiciona % antes e depois do conteudo a ser filtrado.

Agora temos apenas que fazer o formulário na View em cima da tabela.

Arquivo /app/view/users/index.ctp

Pronto! Temos um campo que filtra o usuário pelo nome e compatível com o Paginate. ^^

E mais, o Filter Results automaticamente divide o valor de pesquisa para obter um melhor resultado. Por exemplo: se realizarmos um filtro por ‘Pedro Elsner’, a condição será: WHERE ((User.name LIKE '%Pedro%') AND (User.name LIKE '%Elsner%')).

Explode

A opção explode para os operadores LIKE e NOT LIKE estão ativadas por padrão nas configurações do Filter Results. Mas, como você sabe, você pode desativar na declaração dos components no controller. Se você fizer isto, você pode ativar a função explode para um filtro determinado:

Também é possível mudar as opções de quebra para cada um.

Além disso, você também pode usar a função de quebra junto com outro operador (como =). Veja:

Filtro Simples + Regra Composta

Vamos agora fazer mais uma regra dentro do filtro filter1. Queremos que ele filtre pelo nome(name) ou pelo nome do usuário(username).

Alteramos então apenas o nosso Controller:

Arquivo /app/controller/users_controller.php

A regra OR pode ser também AND ou NOT.

NOTA: Se você definir mais de uma condição sem especificar a regra, o plugin entenderá como AND automaticamente.

Filtro Simples + Regra Fixa

Vamos supor agora que o nosso filtro filter1 quando informado deva filtrar pelo nome(name) E somente usuários(username) ativos.

Arquivo /app/controller/users_controller.php

Agregação de Filtros

Automaticamente o Filter Results concatena todos os filtros pela regra AND. Usando o exemplo a seguir, informando ‘Pedro’ no filter1 e ‘elsner’ no filter2 teremos a condição: WHERE (User.name LIKE '%Pedro%') AND (User.usernname LIKE '%elsner%')

NOTA: Podemos concatenar os filtros também pelas regras OR ou NOT.

Vamos alterar nosso exemplo para concatenar os filtros pela regra OR, e, se o filtro1 for informado queremos apenas usuários ativos. Desta vamos obter a condição: WHERE ((User.name LIKE '%Pedro%') AND (User.active = 1)) OR (User.usernname LIKE '%elsner%')

Filtro de Seleção

Vamos mudar nosso filtro. Além de filtrar pelo nome, queremos agora filtrar também pelo grupo do usuário(Group.name) desejado através de um campo de seleção.

Arquivo /app/controller/users_controller.php

Arquivo /app/view/users/index.ctp

Pronto! Use e abuse de quantos filtros desejar.

Para deixar bem claro, veja a imagem abaixo. Nela temos a View de Produtos, onde é possível filtrar por: Cor, Dimensão, Gramatura, Material e Nome.

Filtro Avançado

Em alguns casos queremos algo diferente diferente, por exemplo, desejamos que o usuário possa escolher o campo e o operador para realizar o filtro.

Arquivo /app/controller/users_controller.php

NOTA: Perceba que desta vez criamos o filtro filter1 sem nenhum parâmetro. Isto porque as regras serão selecionadas na View.

Arquivo /app/view/users/index.ctp

Agora, primeiro você pode selecionar o campo (automaticamente o Filter Results lista os campos da tabela), depois o operador e informar o valor desejado para o filtro.

Haverá situações em que você precisará personalizar os operadores para seleção. Por exemplo, vamos deixar somente os campos User.id, User.name e User.username para seleção, e os operadores LIKE e =.

Para isso, mudamos somente a View:

Arquivo /app/view/users/index.ctp

Operadores

O Filter Results possuí operadores pré-definidos, abaixo você encontra todas as opções disponíveis para você utilizar em seus Filtro personalizados.

BETWEEN

Também é possível utilizar o operador BETWEEN para consulta entre valores numéricos ou de data. Configure o campo de filtro desta forma:

Para campos de data, adicione a opção 'convertDate' => true para converte a data informada para o formato YYYY-MM-DD:

Relacionamentos HABTM

Por padrão, nos relacionamentos HABTM o CakePHP realiza várias consultas a parte e depois faz um merge nos resultados em um único array. Você já deve ter percebido isso, mas se não, verifique a janela de debug enquanto você faz find() em uma relação HABTM.

Para filtramos nessas relações, precisamos criar alguns “hacks” para que o CakePHP gere um único select, assim o plugin filtra o resultado com comandos where simples.

Todas as informações e explicações necessárias para criar os “hacks” você encontra neste tutorial: Pesquisando em relações HABTM no CakePHP

Agradecimento

Quero deixar aqui registrado meu muito obrigado ao meu amigo Vinicius Arantes pela colaboração e implantação do o operador BETWEEN ao plugin. =)

Conclusão

O Filter Results faz o que ele precisa fazer. Obviamente, ele esta longe de ser perfeito e já tenho em mente novas funções para uma segunda versão.

É isto ai, espero que este plugin seja útil a você. ^^
Qualquer dúvida é só deixar um comentário,

Até a próxima!

Quer copiar esse post no seu site? Você pode!

Segundo a licença da Creative Commons 3.0 (CC BY SA 3.0) você pode copiar e distribuir esse conteúdo desde que faça menção ao autor original, para isso é só copiar esse código no final do artigo quando for publicá-lo em seu site:

<p>Artigo originalmente publicado em <em>30 de setembro de 2011</em> por <strong><a href="http://pedroelsner.com/" title="Pedro Elsner, Profissional de TI - São Paulo">Pedro Elsner</a></strong>: <a href="http://pedroelsner.com/2011/09/filtrando-resultados-do-paginate-no-cakephp-com-filter-results/" title="Filtrando resultados do Paginate no CakePHP 1.3 com Filter Results">Filtrando resultados do Paginate no CakePHP 1.3 com Filter Results</a></p>
A não menção ao autor original da obra implicará em cópia e/ou distribuição ilegal de propriedade intelectual, o que é crime segundo a Lei n.º 9.610.
  • Muito legal o tutorial e o plugin. Complicou no final mas nada que a prática leve a perfeição.

    Acompanhando já no github o projeto!

  • Legal Gilvan!
    Em qual parte complicou?

  • Fernando Michel

    Olá Pedro, muito bom esse tutorial. Obrigado por contribuir com a comunidade cakephp
    abraço!

  • Vinícius Moraes de Araújo

    Perfeito, muito bom já estou utilizando..

  • Fala Vinícius,
    Optei por criptografar por alguns motivos de segurança.

    O principal é que em alguns caso, o plugin pode passar o nome do campo da tabela como parâmetro e não é bom deixar “escancarada” esta informação, previndo assim um possível SQL Injection na aplicação.

  • John Henrique

    Tentei de várias formas e não consegui fazer uma pesquisa de registros dentro de um determinado período de tempo, tenho a coluna Trabalho.inicio e Trabalho.encerramento daí preciso que o filter faça a consulta parecida com NOW() BETWEEN inicio AND encerramento.

    O filter results dá suporte a isto?

    Falopa!

  • Infelizmente a versão atual do Filter Results não suporta pesquisa por data e/ou intervalos.
    Estou finalizando algumas revisões para a versão 1.1 e vou implementar esta nova funcionabilidade.

    Mando um e-mail para você assim que estiver disponível,

    Abraços!

  • caraca cara muito legal esse plugin melhor ainda esse seu skin de admin, kkk disponibiliza ai tambem …brincadeira
    você ta pensando ja em passar ele pra cake 2.0?

  • Sobre o layout do admin, é uma boa idéia, vou prepara algo para disponibilizar =]

    Sobre o plugin, a versão 2.0 chegará em breve com novas funcionabilidades, assim como o plugin Acl_Caching.

    Abraços ^^

  • André Fuhrman

    Muito bom seu plugin, só não entendi como fazer para desabilitar um filtro, por exemplo, criei uma página onde tenho o Nome e qual Grupo que o usuário pertence como opções de filtro, mas vamos supor que o internauta queira somente preencher o nome, filtrando em todos os grupos… como fazer?
    Parabéns, e obrigado,
    André

  • Beleza André?

    Bom se eu entendi direito, fazemos isto com apenas 2 filtros:
    – Primeiro um filtro simples para o nome do usuário
    – Segundo um filtro de selação para os grupos de usuários

    Simples assim, desta forma temos varias opções:
    – Selecionar todos os usuarios de todos os grupos (deixar tudo em branco)
    – Selecionar um usuario de todos os grupos (preencher apenas o nome)
    – Selecionar um usuário de um grupo especifico (preencher o nome e selecionar um grupo)
    – Selecionar todos os usuario de um grupo especifico (selecionar apenas o grupo)

    Entendeu? O sistema filtra somente os campos preenchidos.

    Abraços! =D

  • André Fuhrman

    Olá Pedro,

    Eu fiz a migração para a versão 2.0, posso te enviar se desejar, parece que agora está funcionando, não sei se tem a ver ou não com a diferença de versão, mas no Component ele inseria de qq forma a condição, tendo o parâmetro sido enviado ou não.  O que fiz foi modificar na linha 778
     
       if (isset($this->_params[sprintf(‘%s.%s’, $this->_options[‘prefix’], $field)]))
       por
       if ($this->_params[sprintf(‘%s.%s’, $this->_options[‘prefix’], $field)] != ”)

    Abraços,
    André
                 

  • André, a versão atual é apenas para Cake 1.3, e nele esta funcionando perfeitamente =]

    Em breve vou disponibilizar a nova e mais incrementada versão do plugin para Cake 2.

    Abraços

  • Olá Pedro, muito bom o artigo. Consegui implementar em uma estrutura teste bakeada do zero, mas quando tentei implementar em uma projeto com ACL e Auth (baseados nos exemplos do seu blog) implementado, o Filter Results não funcionou. Será que existe a possibilidade de ocorrer conflitos entre eles? 

  • Obrigado Wescley.
    Não! Não ha nenhuma relação. =]
    Utilizo-o junto com o meu outro plugin Acl_Caching – ou sem ele – sem nenhum problema.

    Verifique as configurações, pastas, arquivos, declarações e etc…
    Alguma coisa errada ai não esta certa xD

  • Acacio147

    Oi Pedro, gostei muito do plugin, já estou utilizando, muito obrigado e parabéns.
    Duas coisas que tentei e não consegui achar o caminho: ( levando em conta meu pouco conhecimento)
    Primeira: Colocar um nome diferente ou traduzir o nome da tabela como inicial do campo, como na figura do seu exemplo: “Dimensão”.
    Outra coisa, se tenho dois campos de select box, e estão relacionados, se seleciono o primeiro, não consegui popular o segundo já filtrado pelo primeiro, Ex. Seleciono um Estado num primeiro select, gostaria que o segundo só mostrasse as cidades daquele estado.

    Está me ajudando muito, super fácil de usar. Estou usando muito.

    Pessoas generosas é que fazem o verdadeiro progresso, ajudando todos a progredir também.
    Continue assim. Obrigado.
    Acácio

  • Beleza Acacio! =D
    Fico feliz que o plugin esteja sendo útil para você tambem!

    Você pode mudar o label do filtro desta forma:
    $this->FilterForm->input(‘filter’, array(‘label’ => ‘MEU CAMPO’));

    Sobre popular os campos de seleção, você precisa de uma função AJAX para carregar as cidades do estado selecionado e recarregar a lista. No google ha “n” formas de se fazer isso, utilizando jQuery, Mootools ou Prototype (todos em JavaScript)

    Abraços!

  • Acacio147

    Olá Pedro, desculpe mas eu não expliquei direito.
    As opções de input tipo select-box, você coloca na primeira opção o nome do model, era isso que queria alterar, para por exemplo: Selecione o Estado. algo assim, como na figura do seu exemplo, você não usa label.
    Quanto ao popular, já fiz uns testes com uns scripts que uso em outra situação, mas nesse caso ainda não funcionou. Mas vou continuar tentando.
    Obrigado mais uma vez.
    Acácio

  • Acácio,
    No caso da imagem é ‘label’ sim =D.

    O que acontece é que o tema utiliza um componente JQuery que deixa o select-box mais “estriloso” e cheio de frescuras. Uma delas é exibir a ‘label’ no campo quando nenhuma opção está selecionada.

    Procure no google por “formulários personalizados jquery” e você encontrará muitas coisas interessantes. =D

    Valeu!

  • Acacio147

    Pedro, nada como um bom debug, para descobrir as mancadas.
    Consegui fazer o select aninhado funcionar, ficou ótimo.

    Obrigado e sucesso sempre.
    Acácio
     

  • Vinicius

    Olá Pedro..! Acabei de cruzar com mais um plugin seu! já uso o acl_cache.. muito bom!
    Acabei de instalar seu plugin e ele parece muito interessante.. simples de usar..! Parabéns!

    Mas o que o André Fuhrman falou está acontecendo comigo aqui. Cake 1.3.

    Criei dois campos de filtro, um por nome, simples.. e outro com um select, mas a partir desse momento, não consigo filtrar somente pelo select, deixando o nome em branco..

    ele cria as clausulas WHERE de maneira errada:WHERE AND `Usuario`.`grupo_id` = 1 ORDER BY `Usuario`.`id` ASC LIMIT 20 Ele coloca esse AND logo depois do WHERE…Aparentemente ele está considerando os dois campos quando na verdade deveria considerar apenas o selectbox..Alguma ideia do que pode ser?Agradeço a ajuda!Vinicius

  • Vinicius Big

    encontrei a causa do erro…
    mas ainda não achei uma forma de resolve-lo… preciso debugar melhor teu código..!!Quando temos um filtro campo de texto com várias regras… OR por exemplo e um outro filtro com um select, ele dá erro quando filtramos somente pelo select.// Filtro de resultados$this->FilterResults->addFilters( array( ‘filter1’ => array( ‘OR’ => array( // REGRA “OU” ‘Usuario.nome’ => array( ‘operator’ => ‘LIKE’, ‘beforeValue’ => ‘%’, ‘afterValue’ => ‘%’ ), ‘
    Usuario.email’ => array( ‘operator’ => ‘LIKE’, ‘beforeValue’ => ‘%’, ‘afterValue’ => ‘%’ ) ) ), ‘filter2’ => array( ‘
    Usuario.grupo_id’ => array( ‘value’ => $this->FilterResults->merge(‘Grupo’, $this->
    Usuario ->
    Grupo ->find(‘list’)) ) ) ));

    O erro aparentemente é na função _makeConditions()…
    ainda estou procurando..!

    De qualquer forma, para regras simples funciona perfeitamente!

  • Fala Vinicius,
    Faz o seguinte, passa pra mim o código do FilterResults em sua action.

    Abraços!

  • John agora o FilterResults aceita o operador ‘BETWEEN’.
    Abraços!

  • Fala Amigo,
    A versão do Plugin Filter Results para CakePHP 2.x já está disponível =D
    Acesse: http://github.com/pedroelsner/FilterResults2

  • Dsprog

    vlw cara demais vou recomendar aos amigos que estão estudando cake tambem

  • Mord4z

    Olá, gostei muito do plugin, obrigado por compartilhar. Este resolveu alguns problemas que eu estava tendo com um outro plugin que usava.
    No momento estou tentando fazer um botão pra “resetar” os filtros mas ainda não consegui, alguma dica de como fazer? 

  • Beleza Mord4z? =)
    É só fazer o um link para a action atual, sem enviar os parametros NAMED e PASS.
    Abraços!

  • Allan George

    Opa, boa! Agora sim ficou perfeito! 
    Mais uma vez obrigado!

  • Theodoro Granero

    Parabéns pelo trabalho, muito bem documentado e desenvolvido..

    O utilizei com o Cake 1.3 e tive o seguinte erro ao acionar o Helper:

    Warning (2): array_merge() [function.array-merge]: Argument #2 is not an array [APPpluginsfilter_resultsviewshelpersfilter_form.php, line 69]

    Estranhamente o construtor da classe não estava instanciando a variável $settings como array retornando a variável como nula.
    Resolvi o problema validando a variável como array na linha 69
    $this->_options = is_array($settings)? array_merge($this->_options, $settings):$this->_options;

    Aparentemente ninguém teve um erro similar. 
    Achei estranho só ter ocorrido comigo.

    Abraço

  • Allan George

    Pintou uma dúvida aqui: como filtrar via “is not null” ? To quebrando a cabeça aqui!

  • Obrigado Theodoro,
    Atualizei o plugin! =D

  • Francis Rodrigues

    Se um campo for um selectbox com os dados preenchidos na mão não consigo fazer consultar junto com outro campo, por exemplo um nome e um status (preenchido na mão).

  • Francis, você deve popular seu selectbox no Controller (junto ao comando Filter) e não na View. Veja o exemplo do tutorial e altere a função Find pelo seu array.

  • Breno Roosevelt Barros de Jesu

    Pedro, como faço para atribuir um valor inicial a um filtro, valor este que pode ser modificado pelo usuário no fomrulário?