'JOIN', self::JOIN_TYPE_LEFT => 'LEFT JOIN', self::JOIN_TYPE_RIGHT => 'RIGHT JOIN', self::JOIN_TYPE_INNER => 'INNER JOIN', self::JOIN_TYPE_OUTER => 'OUTER JOIN', ); private $select = array(); private $distinct = ''; private $where = array(); private $group_by = array(); private $order = array('sort' => array(), 'order' => array()); private $limit = ''; /** * @var SqlModel */ private $model; private $sql_expression; private $sql_expression_params = array(); private $sql_join_expressions = array(); private $join_table_placeholder = array(); /** * @param $model SqlModel * @param $sql_expression string|null Sql expression with SELECT and FROM operators. If fetched, then SqlCriteria::select(), SqlCriteria::distinct() disabled for use. * @param $sql_expression_params array additional params to be replaced in sql expression */ public function __construct($model, $sql_expression = null, $sql_expression_params = array()) { $this->model = $model; $this->sql_expression = $sql_expression; $this->sql_expression_params = $sql_expression_params; } /** * @return SqlResultProvider */ public function find() { $this->defineJoinExpressions(); return $this->model->find($this->select, $this->distinct, $this->where, $this->order, $this->limit, null, $this->group_by, $this->sql_expression, $this->sql_expression_params); } private function defineJoinExpressions($select = null) { if ($this->sql_join_expressions) { if (!$this->sql_expression) { $select = $this->model->getDb()->selectExpr($select ? : $this->select, $this->distinct); $this->sql_expression = 'SELECT ' . $select . ' FROM :table'; } $this->sql_expression .= ' ' . implode(' ', $this->sql_join_expressions); $this->sql_join_expressions = null; } } public function delete() { return $this->model->find('', '', $this->where, null, null, null, null, 'DELETE FROM :table', $this->sql_expression_params)->affectedRows(); } //TODO: перенести определение sql_expression в модель public function count($select = null) { $this->defineJoinExpressions($select); $sql_expression_backup = $this->sql_expression; $group_by_backup = $this->group_by; $select_parent_query = 'COUNT(*) as count'; $default_select = '*'; if ($this->group_by) { $default_select = 'DISTINCT ' . $this->group_by[0]; } $this->group_by = []; if (!$this->sql_expression) { $this->sql_expression = 'SELECT COUNT(' . ($select ? $select : $default_select) . ') as count FROM :table'; $select_parent_query = 'x.count'; } else { $this->sql_expression = preg_replace('#SELECT.+FROM#i', 'SELECT COUNT(' . ($select ? $select : $default_select) . ') as count FROM', $this->sql_expression); } $count = $this->model->count($select_parent_query, $this->where, null, $this->group_by, $this->sql_expression, $this->sql_expression_params)->fetchField('count'); $this->sql_expression = $sql_expression_backup; $this->group_by = $group_by_backup; return $count; } private function defineJoinTablePlaceholder($table_name) { if (!isset($this->join_table_placeholder[$table_name])) { $this->join_table_placeholder[$table_name] = ':table' . (count($this->join_table_placeholder) + 1); } } public function getTablePh($table_name) { $this->defineJoinTablePlaceholder($table_name); return $this->join_table_placeholder[$table_name]; } public function join($join_table_name, $join_field_name, $donor_table_name = null, $donor_field_name = null, $join_type = self::JOIN_TYPE_DEFAULT) { $donor_field_name = $donor_field_name ? : $join_field_name; $donor_table_placeholder = $donor_table_name ? $this->getTablePh($donor_table_name) : ':table'; $join_table_placeholder = $this->getTablePh($join_table_name); $this->sql_join_expressions[] = self::$join_reserved_keyword[$join_type] . ' ' . $join_table_placeholder . ' ON ' . $donor_table_placeholder . '.' . $donor_field_name . '=' . ' ' . $join_table_placeholder . '.' . $join_field_name; if ($donor_table_name) { $this->sql_expression_params[substr($donor_table_placeholder, 1)] = new DbExpr($this->model->identify($donor_table_name)); } $this->sql_expression_params[substr($join_table_placeholder, 1)] = new DbExpr($this->model->identify($join_table_name)); return $this; } /** * @param $cond string|array Condition with "?" placeholder @ex 'field=?' or 'field=1' or array('field=?' => 1', 'field=1') * @param $value string|array|DbExpr|null Value. Array transformed to DbExpr(implode(',' Array)) All elements in the array mast be integer * @return SqlCriteria * @desc Allow multiple calls */ public function where($cond, $value = null) { if (is_null($value)) { if (is_array($cond)) { $this->where = $this->where + $cond; } else { $this->where[] = $cond; } } else { $this->where[$cond] = $value; } return $this; } public function whereJoin($join_table_name, $cond, $value = null) { $join_table_placeholder = $this->getTablePh($join_table_name); if (is_array($cond)) { $cond_replace = array(); foreach ($cond as $key => $value) { $cond_replace[$this->getCondWithTablePlaceholderIfNeed($join_table_placeholder, $key)] = $value; } } else { $cond = $this->getCondWithTablePlaceholderIfNeed($join_table_placeholder, $cond); } return $this->where($cond, $value); } private function getCondWithTablePlaceholderIfNeed($table_placeholder, $cond) { if (!strstr('.', $cond)) { $cond = $table_placeholder . '.' . $cond; } return $cond; } /** * @param $field string * @param $value array * @return SqlCriteria */ public function whereIn($field, $value) { return $this->where($field . ' in ?', $value); } /** * @param $field string * @param $value array * @return SqlCriteria */ public function whereNotIn($field, $value) { return $this->where($field . ' not in ?', $value); } /** * @param $field string * @param $value array * @return SqlCriteria * @deprecated */ public function whereNot($field, $value) { return $this->whereNotIn($field, $value); } public function groupBy($fields) { if (is_array($fields)) { $this->group_by = $this->group_by + $fields; } else { $this->group_by[] = $fields; } return $this; } /** * @param $field string Field @ex 'field' * @param $order_desc bool Descendant sort direction * @return SqlCriteria * @desc Allow multiple calls */ public function order($field, $order_desc = false) { $this->order['sort'][] = $field; if ($order_desc) { $this->order['order'][$field] = 'desc'; } return $this; } /** * @param $offset int * @param $limit int * @return SqlCriteria */ public function limit($offset = 0, $limit) { if ($offset) { $this->limit = (int) $offset . ','; } $this->limit .= (int) $limit; return $this; } /** * @param string|array $fields * @param bool $convert_to_db_expression * @ex SqlCriteria::select('field') * @ex SqlCriteria->select(array('field1', 'field2')) * @ex SqlCriteria->select('field1,field2') * @return SqlCriteria */ public function select($fields, $convert_to_db_expression = false) { if (!is_array($fields)) { $fields = explode(',', $fields); } $fields = array_map(function($item){return trim($item);},$fields); if ($convert_to_db_expression) { $fields = array_map(function($item){return new DbExpr($item);},$fields); } $this->select = array_merge($this->select,$fields); return $this; } /** * @param $field string|bool If true then distinct by * * @return SqlCriteria */ public function distinct($field) { $this->distinct = $field; return $this; } }