Na verdade, esse tipo de filtro muitas vezes é feito bem desse jeito que você tem em mente. Mas não exatamente como você demonstrou em código.
Primeiro de tudo, óbvio, você deve sanitizar, filtrar e, de repente, até validar os dados recebidos. Confiar no usuário é como ir pra guerra sem capacete.
Depois, você tem duas opções:
- Montar um array com cada coluna e seu respectivo valor, um a um e, depois de todas as opções consideradas, montar a query concatenando N cláusulas
AND
:$clauses = array();
if( ! empty( $data['tipoAnunciante'] ) && $data['tipoAnunciante'] != 'none' ) {
$clauses[] = sprintf( '`tipoAnunciante` = "%s"', $data['tipoAnunciante'] );
}
if( ! empty( $data['tipoImovel'] ) && $data['tipoImovel'] != 'none' ) {
$clauses[] = sprintf( '`tipoImovel` = "%s"', $data['tipoImovel'] );
}
// ...
- Fazer da forma que o @Luizz sugeriu, num loop. Apesar de que ele usou uma das funções mysql_* que é uma extensão em desuso e não deve ser utilizada.
Ambas as abordagens tem seus prós e contras.
A primeira porque a query não estaria devidamente tratada, preocupação qe não se precisa ter com prepared statements, seja da PDO, MySQLi…
A segunda porque além de não ser muito legível numa olhada rápida, mesmo que seja usado uma extensão/biblioteca com suporte a prepared statements pode vincular o argumento com o tipo errado ou, pior, requere muitas condições dentro do loop.
O mais adequado (ou pelo menos o menos pior) nesse caso, e já considerando prepared statement seria algo assim:
$columns = array();
if( ! empty( $data['tipoAnunciante'] ) && $data['tipoAnunciante'] != 'none' ) {
$columns[] = $data['tipoAnunciante'];
}
if( ! empty( $data['tipoImovel'] ) && $data['tipoImovel'] != 'none' ) {
$columns[] = $data['tipoImovel'];
}
if( ! empty( $data['quartos'] ) && $data['quartos'] != 'none' ) {
$columns[] = $data['quartos'];
}
$query = 'SELECT * FROM `tabela`';
if( count( $columns ) > 0 ) {
$query .= sprintf( ' WHERE `%s` = ?', implode( '` = ? AND `', $columns ) );
}
Primeiro nós criamos um array com todas as entradas válidas, isto é, aquelas escolhidas pelo usuário e diferentes de none (nenhum, em inglês, opção pessoal minha para o valor default).
O pulo do gato está na concatenação final pois além dela só ocorrer se ao menos um filtro tiver sido escolhido, ainda tira proveito de todo verdadeiro poder de implode()
implode() não serve unicamente pra transformar um array numa string, veja:
sprintf( ‘ WHERE `%s` = ?’, implode( ‘` = ? AND `’, $columns ) );
O termo em negrito adiciona a cláusula WHERE propriamente dita ao final da parte imutável da query.
O termo em itálico (não muito visível) é o placeholder de sprintf(). Eu poderia usar várias concatenações mas dessa forma é mais legível.
Ele será substituído pela string resultante de implode(). O detalhe, se você observar bem, é que ese placeholder está envolto por backticks (caractere da crase – `)
Na string da query resultante:
‘SELECT * FROM `tabela` WHERE `imobiliaria` = ? AND `casa` = ?’
Ele forma o primeiro e o último backtick, à esquerda de imobiliaria
e à direita de casa
:
O grande truque está mesmo é no implode():
implode( ‘` = ? AND `’, $columns )
Ele vai unir todas as peças, os elementos do array, por:
- Outro backtick;
- O sinal de igualdade;
- Uma interrogação (para o placeholder do prepared statement).
- A cláusula AND
- Outro backtick.
O primeiro backtick vai fechar o backtick usado no sprintf() e o segundo backtick vai abrir o segundo definido nessa função.
Como internamente implode() trabalha mais ou menos num loop, isso vai se repetir para cada entrada do array. Mas caso tenhamos três ou mais condições (ao invés das duas demonstradas) seu comportamento vai mudar sem você perceber:
- Na primeira “iteração” o primeiro backtick vai fechar àquele aberto em sprintf() conforme dito acima e o segundo vai abrir um novo para o segundo elemento.
- Na segunda “iteração” o primeiro backtick vai fechar àquele aberto pela iteração anterior e abrir um novo para o terceiro e último elemento.
- Na terceira “iteração” o primeiro backtick vai fechar àquele aberto pela iteração anterior e o segundo vai abrir àquele aberto no sprintf() e a string final está pronta para ser usada.
Mas e quanto ao tratamento dos valores?
Pelo menos com a PDO (não sei quanto MySQLi e outras) a menos que você REALMENTE precise da diferença que PDOStatement::bindParam() e PDOStatement::bindValue() têm entre si, você pode simplesmente passar um array para PDOStatement::execute() que a própria PDO se encarregará se or argumento for ou precisar ser uma string, um inteiro, um float…
E como as interrogações ~serão adicionadas na exata mesma quantidade de itens no array de valores escolhidos, você não terá problemas com excesso ou falta de interrogações no seu prepared statement