Skip to content

Instantly share code, notes, and snippets.

@hlegius
Created September 19, 2010 18:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hlegius/586984 to your computer and use it in GitHub Desktop.
Save hlegius/586984 to your computer and use it in GitHub Desktop.
[PHP] Realiza queries SQL com PDO.
<?php
/**
* Criteria para a QueryObject
*
* @author Hélio Costa e Silva <helio@shokstudio.com.br>
*
* @created Novembro, 25 2008
* @revised Dezembro, 26 2008
* @version desenvolvimento / development
*
* [Dezembro, 26 2008]
* + Controle das tabelas manipuladas com addFrom()
* + Controle sobre JOIN's com o grupo: addJoin(), removeJoin() e flushJoins()
*/
final class Criteria {
private $rows = array();
private $filtro = array();
private $start = null;
private $registros = null;
private $order = null;
private $entity = null;
private $from = null;
private $join = array();
/**
* Injeta a entidade na criteria. Serve como base para os dados n�o padr�es
* @param Object $classe
* @return Criteria (referencia)
*/
public function setEntity($classe) {
$this->entity = is_object($classe) ? $classe : null;
return $this;
}
/**
* Adiciona um crit�rio de busca
* @param string $campo (nome do campo da tabela)
* @param string $operacao (=, !=, >, <, >=, <= IS, IS NOT) para LIKE, use $this->addLike()
* @param string $valor (nome do getter na entidade sem o get. getNome() seria 'nome' ou valor real)
* @param bool $valorReal (se TRUE, o $valor � usado como par�metro(valor real) e n�o como atalho da entidade)
* @return Criteria (refer�ncia)
*/
public function add($campo, $operacao, $valor = -1, $valorReal = false) {
if(is_bool($valor) && $valor !== false)
settype($valor, 'bool');
else
$valor = $valor === -1 ? $campo : $valor;
$this->filtro[] = array('field' => $campo, 'oper' => $operacao, 'value' => $valor, 'valorReal' => $valorReal);
return $this;
}
/**
* Adiciona um crit�rio usando o LIKE e suas variantes
* @param string $campo (nome do campo da tabela)
* @param string $operacao (=, !=, >, <, >=, <= IS, IS NOT) para LIKE, use $this->addLike()
* @param array $criterio (geralmente usado para completar a busca. i.e: %)
* @param string $valor (nome do getter na entidade sem o get. getNome() seria 'nome' ou valor real)
* @param bool $valorReal (se TRUE, o $valor � usado como par�metro(valor real) e n�o como atalho da entidade)
* @return Criteria (refer�ncia)
*/
public function addLike($campo, $operacao, array $criterio, $valor = null, $valorReal = false) {
if($operacao != 'LIKE' && $operacao != 'NOT LIKE' && $operacao != "iLIKE") return $this;
foreach($criterio as &$criterio_split) { ## s� aceitamos %. � % ou nada !
if($criterio_split !== '%')
$criterio_split = '';
}
$valor = is_null($valor) ? $campo : $valor;
$this->filtro[] = array('field' => $campo, 'oper' => $operacao, 'criterio' => $criterio, 'value' => $valor, 'valorReal' => $valorReal);
return $this;
}
/**
* Adiciona um operador
* @param string $operator (and, or)
* @return Criteria (refer�ncia)
*/
public function addOperator($operator) {
$this->filtro[] = $operator;
return $this;
}
/**
* Adiciona um novo grupo - coloca abre parent�ses
* @return Criteria (refer�ncia)
*/
public function startParentGroup() {
$this->filtro[] = ' ( ';
return $this;
}
/**
* Finaliza um grupo - coloca fecha parent�ses
* @return Criteria (refer�ncia)
*/
public function endParentGroup() {
$this->filtro[] = ' ) ';
return $this;
}
/**
* Seta o inicio da busca (LIMIT $inicio)
* @param int $inicio
* @return Criteria (refer�ncia)
*/
public function startFrom($inicio) {
settype($inicio,'int');
$this->start = $inicio;
return $this;
}
/**
* Itens a buscar (LIMIT $inicio,$registros)
* @param int $registros
* @return Criteria (Refer�ncia)
*/
public function stopWith($registros) {
settype($registros,'int');
$this->registros = $registros;
return $this;
}
/**
* Adiciona a ordem de resultados
* @param string $field (campo)
* @param string $order (ASC,DESC)
* @return Criteria (refer�ncia)
*/
public function orderBy($field, $order) {
$this->order = $field . ' ' . strtoupper($order);
return $this;
}
/**
* Campo a ser buscado (*, COUNT(1), DISTINCT(), nome,idade,id...)
* @param string $row
* @return Criteria (refer�ncia)
*/
public function addRow($row) {
if(is_array($row)) {
foreach($row as $campo) {
$this->rows[] = $campo;
}
}else{
$this->rows[] = $row;
}
return $this;
}
/**
* Remove os rows j� setados
* @return this
*/
public function flushRow() {
$this->rows = null;
return $this;
}
/**
* Seta as tabelas a serem lidas
* @param string $from
*/
public function addFrom($from) {
$this->from = $from;
return $this;
}
/**
* Adiciona um Join ao criteria
* @param string(LEFT, RIGHT, INNER) $tipo
* @param string(sqltable) $table
* @param string(join clause) $join
* @param mixed $internal_name
* @return this
*/
public function addJoin($tipo, $table, $join, $internal_name = null) {
$internal_name = !is_null($internal_name) ? $internal_name : count($this->join);
$this->join[$internal_name] = strtoupper(str_replace(" JOIN", "",$tipo)) . ' JOIN ' . $table . ' ON ' . $join . ' ';
return $this;
}
/**
* Remove um Join que tenha sido previamente identificado com um internal_name
* @param mixed $internal_name
* @return this
*/
public function removeJoin($internal_name) {
if (key_exists($internal_name, $this->join)) {
(unset)$this->join[$internal_name];
}
return $this;
}
/**
* Reseta todas as Joins anteriormente setadas.
* @return this
*/
public function flushJoins() {
$this->join = array();
return $this;
}
/**
* Verifica se h� algum filtro setado
* @return boolean
*/
public function hasFiltros() {
if(count($this->filtro) == 0)
return false;
else
return true;
}
/**
* Reseta os filtros
* @return void
*/
public function cleanup() {
$this->rows = array();
$this->filtro = array();
$this->start = null;
$this->registros = null;
$this->order = null;
$this->entity = null;
$this->from = null;
$this->join = array();
}
public function getFrom() { return $this->from; }
public function getRows() { return $this->rows; }
public function getJoins() { return $this->join; }
public function getFiltro() { return $this->filtro; }
public function getStart() { return $this->start; }
public function getRegistros() { return $this->registros; }
public function getOrder() { return $this->order; }
public function getEntity() { return $this->entity; }
public function dump() {
var_dump($this->filtro);
}
}
<?php
/**
* Classe que implementa a QueryObject
* Funciona apenas com MySQL
* basicamente funciona com SELECTs, e DELETEs
*
* @author Hélio Costa e Silva <helio@shokstudio.com.br>
*
* @created Novembro, 25 2008
* @revised Janeiro, 13 2009
* @version desenvolvimento / development
*
* [Janeiro, 13 2009]
* + Arrumado BUG do 'auto-and' mode
* + Adicionado controle contra 'and'/'or' solitário no final da Query
*
* [Dezembro, 26 2008]
* + Possibilidade de setar as tabelas com criteria->addFrom()
* + Manipulação de JOIN's
*
* @internal Exemplos no final do arquivo comentado !
*/
/**
* @todo criar classes especializadas que irão extender uma base.
* Servirá para criar UPDATEs, INSERTs com maior precisão
* Criar JOIN's (agora temos JOIN's \o/ [Dez, 26 08], HAVING, GROUP BY e outros
*/
final class QueryObject {
/**
* Recebe a Criteria
* @var Criteria
*/
private $criteria = null;
/**
* Rows a serem pegos
* @var string
*/
private $rows = null;
/**
* Tabelas a serem pesquisadas
* @var string
*/
private $from = null;
/**
* A clausula where
* @var string
*/
private $condition = null;
/**
* Ordenação e limites
* @var string
*/
private $limit = null;
/**
* Elementos do prepared-statement (PDO)
* @var array
*/
private $bind = null;
/**
* Construtor da classe. Recebe a Criteria
* @param Criteria $criteria
*/
public function __construct(Criteria $criteria) {
$this->criteria = $criteria;
$this->getInstruction(); ## roda a Query
}
/**
* Monta a estrutura de critérios
* @return string
*/
private function getInstruction() {
$this->rowsInstruction(); ## setando os campos a serem buscados
$this->joinsInstruction(); ## montando os possíveis joins
$criterios = $this->criteria->getFiltro();
if (count($criterios) == 0) {
$this->condition = $this->trim($this->condition . $this->limitInstruction());
return ;
}
$this->condition .= ' WHERE ';
$otherFlags = false;
foreach ($criterios as $indice => $criterio) {
if (!is_array($criterio)) { ## usado em AND, OR, (, )...
$this->condition .= ' ' . $criterio . ' ';
$otherFlags = trim($criterio) != ')' ? true : false; ## controla o "auto 'and' condition"
continue;
}
if ($indice != 0 && $otherFlags === false) { ## auto 'and' condition mode
$this->condition .= is_array($criterio) ? ' and ' : '';
$otherFlags = false;
}
if (is_null($criterio['value']) && ($criterio['oper'] == 'IS NOT' || $criterio['oper'] == "IS")) {
$this->condition .= $criterio['field'] . '' . (($criterio['oper'] == 'IS NOT') ? ' IS NOT' : ' IS') . ' NULL ';
continue; ## IS NOT NULL, IS NULL
}
if (isset($criterio['criterio']) && is_array($criterio['criterio'])) { # LIKE, NOT LIKE...
$this->condition .= $criterio['field'] . ' ' . $criterio['oper'] . " " . '?' . "";
$this->_bindValue($criterio['criterio'][0] . $criterio['value'] . $criterio['criterio'][1], $criterio['valorReal']);
continue;
}
if (is_string($criterio['value']) || is_object($criterio['value']) || is_null($criterio['value'])) { # normal foo=, !=, > ? (foo>?)
$this->condition .= $criterio['field'] . '' . $criterio['oper'] . '?';
$this->_bindValue($criterio['value'], $criterio['valorReal']);
continue;
}
if (is_int($criterio['value'])) { ## idade > 19
$this->condition .= $criterio['field'] . '' . $criterio['oper'] . '' . $criterio['value'] . ' ';
continue;
}
if (is_bool($criterio['value'])) { ## selected = TRUE
$criterio['value'] = $criterio['value'] === true ? 'TRUE' : 'FALSE';
$this->condition .= $criterio['field'] . '' . $criterio['oper'] . '' . $criterio['value'] . ' ';
continue;
}
}
if (substr($this->trim($this->condition), -3) == 'and' ||
substr($this->trim($this->condition), -2) == 'or') { ## retirando o 'and'/'or' do final (usado em 'and' ou 'or' forçados via addOperator()
$this->condition = substr($this->trim($this->condition), 0, -3);
}
$this->condition = $this->trim($this->condition . $this->limitInstruction());
}
/**
* Monta o bindValue procurando o seu get$Value na entidade da critéria
* @param string $value
*/
private function _bindValue($value, $valorReal) {
if ($valorReal === false) { ## o possível valor passado é o nome da propriedade da entidade
if(method_exists($this->criteria->getEntity(), "get{$value}")) {
$this->bind[] = $this->criteria->getEntity()->{"get{$value}"}();
}
} else {
$this->bind[] = $value; ## relaxa, isso irá via prepared-statement !
}
}
/**
* Cria as Joins da critéria
*/
private function joinsInstruction() {
$this->condition = ''; ## as Joins vem antes do WHERE ;)
foreach ($this->criteria->getJoins() as $join) {
$this->condition .= $join . ' ';
}
}
/**
* Monta as instruções de retorno.
* SELECT (isso aqui!) FROM ...
* @return void
*/
private function rowsInstruction() {
$this->from = $this->criteria->getFrom(); ## anexando o FROM
if (is_null($this->criteria->getRows()) || count($this->criteria->getRows()) < 1) {
$this->rows = '*';
return ;
}
if (is_array($this->criteria->getRows())) {
foreach ($this->criteria->getRows() as $row) {
$this->rows .= $row . ',';
}
$this->rows = rtrim($this->rows,",");
}
}
/**
* Monta as instruções orderby
*/
private function orderbyInstruction() {
if ($this->criteria->getOrder()) {
$this->limit = ' ORDER BY ' . $this->criteria->getOrder() . ' ';
}
}
/**
* Monta as instruções de limites
* @return string
*/
private function limitInstruction() {
$this->orderbyInstruction();
if ($this->criteria->getStart() || $this->criteria->getRegistros()) {
$this->limit .= ' LIMIT ';
if ($this->criteria->getStart()) {
$this->limit .= $this->criteria->getStart() . ',';
}
$this->limit .= ($this->criteria->getRegistros() > 0) ? $this->criteria->getRegistros() : 5;
}
return $this->limit;
}
/**
* Clausulas de JOIN's, where, OrderBy e Limit
* @return string
*/
public function getWhere() { return $this->condition; }
/**
* Retorma os campos a serem buscados
* @return string
*/
public function getRows() { return $this->rows; }
/**
* Retorna as tabelas a serem pesquisadas
* @return string
*/
public function getFrom() { return $this->from ; }
/**
* Retorna os elementos a serem passados à prepared-statement
* @return array
*/
public function bindValues() { return $this->bind; }
/**
* Remove espaços excedentes do começo, meio e fim da string
* @param string $String
* @return string
*/
private function trim($String) {
$space = 0;
$newstr = "";
$strlen = strlen($String);
for ($i=0;$i<$strlen;$i++) {
if ($String[$i] == " ") {
$space++;
if ($space == 2) :
$space--;
continue;
endif;
} else {
$space = 0;
}
$newstr .= $String[$i];
}
$space = null; $strlen = null; // trash garbage
return trim($newstr);
}
/**
* Dumpa a classe toda para análise.
* Developer only!
*/
public function dump() {
var_dump($this);
}
/*
* Exemplo de utilização da Query Object
*
$criteria = new Criteria;
$criteria->setEntity(new Pessoa(id, 'nome', idade))
->addRow('nome')
->addRow('idade')
->addLike('nome', 'LIKE', array(null, '%'))
->addOperator('and')
->startParentGroup()
->add('idade','>')
->addOperator('or')
->add('idade', '<', 50)
->addOperator('and')
->startParentGroup()
->add('selected','=', false)
->addOperator('or')
->add('hahaha','IS NOT', null)
->endParentGroup()
->addOperator('and')
->add('hohoho','IS', null)
->endParentGroup()
->add('nome','=')
->startFrom(1)
->stopWith(10)
->orderBy('idade', 'DESC');
$qo = new QueryObject(new Criteria);
var_dump($qo);
*
*/
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment