'array', 'limit' => array('integer', 'NULL'), 'offset' => array('integer', 'NULL'), 'group_by' => array('string', 'NULL'), 'type' => 'string'); /** * UNION queries * @var array */ protected $_union = array(); /** * Generates a query in format that can be executed on current database implementation * */ public abstract function query(); /** * Creates a new query * * @param DB $db Database connection * @param string $type Query type. Available types: select, update, insert, delete, count * @return void */ public function __construct($db, $type) { $this->_db = $db; $this->_type = $type; } /** * Sets fields to be queried from the database. You can add aliases to the fields * by passing them as: * * array('field_name','alias') * * Example: $query->fields('id', array('name', 'fairy_name')) * * @param mixed $field,... Fields to be selected from the table * @return mixed If no parameters are passed returns current array of fields, * otherwise returns self. */ public function fields() { $p = func_get_args(); if (empty($p)) { return $this->_fields; } else { $this->_fields = $p; } return $this; } /** * Sets the table to perform operations on, also supports subqueries * * @param string|Query_database|Expression_database $table table to select from * @param string $alias Alias for this table * @return mixed Returns self if a table is passed, otherwise returns the table */ public function table($table = null, $alias = null) { if ($table == null) { return is_array($this->_table) ? $this->_table[1] : $this->_table; } if (!is_string($table) && $alias == null) { $alias = $this->add_alias(); } $this->_table = $alias == null ? $table : array($table, $alias); return $this; } /** * Magic methods to create methods for all generic query parts * * @param string $method Name of the method to call * @param array $args Array of parameters * @return mixed If no arguments are passed returns the current value of the property, * otherwise returns self. * @throws \Exception If method doesn't exist * @throws \Exception If value is of incorrect type * @see $methods */ public function __call($method, $args) { if (isset($this->methods[$method])) { $property = '_'.$method; if (empty($args)) { return $this->$property; } $val = $args[0]; if (is_numeric($val)) { $val = (int) $val; } $allowed_types = $this->methods[$method]; if (!is_array($allowed_types)) { $allowed_types = array($allowed_types); } if (!in_array(gettype($val), $allowed_types)) { throw new \Exception("Method '{$method}' only accepts values of type: ".implode(' or ', $allowed_types).", '{$val}' was passed"); } $this->$property = $val; return $this; } throw new \Exception("Method '{$method}' doesn't exist."); } /** * Executes the query * * @return object Executes current query on its database connection * @see DB */ public function execute() { $query = $this->query(); $result = $this->_db->execute($query[0], $query[1]); if ($this->_type == 'count') { return $result->get('count'); } return $result; } /** * Adds a joined table to the query. * * @param string $table Table to join * @param array $conds Conditions to join tables on, same behavior as with where() method * @param string $type Type of join. Defaults to 'left' * @return Query_Database Returns self * @see where() */ public function join($table, $conds, $type = 'left') { $this->_joins[] = array($table, $type, $this->get_condition_part($conds)); return $this; } /** * Sets conditions for aggregate functions, same behavior as with where() method * * @return Query_Database Returns self * @see where() */ public function having() { $p = func_get_args(); $cond = $this->get_condition_part($p); $this->_having = array_merge($this->_having, array($cond)); return $this; } /** * Adds a column to ordering parameters. * * @param string $column Column to order by * @param string $dir Ordering direction. * @return Query_Database Returns self * @throws \Exception If ordering direction isn't DESC or ASC */ public function order_by($column, $dir = 'ASC') { $dir = strtoupper($dir); if ($dir != 'DESC' && $dir != 'ASC') { throw new \Exception("Invalid sorting direction {$dir} specified"); } $this->_order_by[] = array($column, $dir); return $this; } /** * Sets conditions for the query. * Can be called in many ways, examples: * Shorthand equals condition: * * $q->where('name', 'Tinkerbell') * * Conditions with operator: * * $q->where('id', '>', 3) * * OR logic: * * $q->where('or', array('name', 'Tinkerbell')) * * OR logic with operator * * ->where('or', array('id', '>', 3)) * * Arrays represent brackets. e.g * * $q->where('name', 'Tinkerbell') * ->where('or', array( * array('id', '>', 7), * array('id', '<', 15) * ); * //Will produce "WHERE `name`='Tinkerbell' OR (`id` > 7 AND `id` < 15)" * * Multiple calls to where() append new conditions to previous ones * * @param mixed $column Column name, logic parameter 'OR' or 'AND' or an array of conditions * @param mixed $operator Condition value, operator or an array of parameters * @param mixed $val Condition value * * @return Query_Database Returns self */ public function where() { $p = func_get_args(); $cond = $this->get_condition_part($p); $this->_conditions = array_merge($this->_conditions, array($cond)); return $this; } /** * Recursively builds condition arrays for methods like where(), having() * * @param array $p Parameters passed to the method * @return array Array in condition format * @throws \Exception If condition format is incorrect */ private function get_condition_part($p) { if (is_string($p[0]) && (strtolower($p[0]) == 'or' || strtolower($p[0]) == 'and') && isset($p[1]) && is_array($p[1])) { $cond = $this->get_condition_part($p[1]); $cond['logic'] = strtolower($p[0]); return $cond; } if (is_array($p[0])) { if (count($p) == 1) { return $this->get_condition_part($p[0]); } $conds = array(); foreach ($p as $q) { $conds[] = $this->get_condition_part($q); } if (count($conds) == 1) { return $conds; } return array('logic' => 'and', 'conditions' => $conds); } if ((is_string($p[0]) || $p[0] instanceof \PHPixie\DB\Expression) && isset($p[1])) { if (is_string($p[0]) && strpos($p[0], '.') === false) { $p[0] = $this->last_alias().'.'.$p[0]; } return array( 'logic' => 'and', 'conditions' => array( 'field' => $p[0], 'operator' => isset($p[2]) ? $p[1] : '=', 'value' => isset($p[2]) ? $p[2] : $p[1] ) ); } throw new \Exception('Incorrect conditional statement passed'); } /** * Adds a UNION to the query * * @param Query_Database|Expression_Database $query Query for the UNION * @param string $all whether to do a UNION ALL, e.g. keep duplicate rows * @return Query_Database Returns self */ public function union($query,$all=true) { $this->_union[] = array($query,$all); return $this; } /** * Gets last generated alias * * @return string Last generated alias. If no alias were created returns table name. */ public function last_alias() { if ($this->_alias === null) { return $this->_table; } return 'a'.$this->_alias; } /** * Generates new alias. Useful for dynamically adding aliases to joins. * Alias is just a letter 'a' with an incremented number. * * @return string New alias */ public function add_alias() { if ($this->_alias === null) { $this->_alias = 0; } else { $this->_alias++; } return $this->last_alias(); } }