Files
majestic/model/SqlModel.php
2013-09-17 18:42:04 +04:00

377 lines
13 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Класс модели данных
*
* @copyright NetMonsters <team@netmonsters.ru>
* @link http://netmonsters.ru
* @package Majestic
* @subpackage Model
* @since 2011-11-11
*/
/**
* @property SqlDbDriver $db
*/
abstract class SqlModel extends Model
{
const SELECT = 10;
const DISTINCT = 11;
const WHERE = 12;
const ORDER = 13;
const LIMIT = 15;
const OFFSET = 16;
const ORDER_DESC = 20;
const ORDER_ASC = 21;
const ASSOC_BY_FIELD = 30;
const ASSOC_AS_ARRAY = 31;
const ASSOC_BY_FIELD_IN_ARRAY = 32;
protected static $criteria_data_keys = array(
self::SELECT,
self::DISTINCT,
self::WHERE,
self::ORDER,
self::LIMIT,
/** self::OFFSET // that key no to be a process in foreach criteria_data @see SqlModel::find() */
);
/**
* @param string $ident
* @return string Quoted identifier.
*/
public function identify($ident)
{
return $this->db->quoteIdentifier($ident);
}
/**
* @param mixed $value
* @return string Quoted value.
*/
public function quote($value)
{
return $this->db->quote($value);
}
/**
* @param int $id
* @return object
*/
public function get($id)
{
$sql = 'SELECT * FROM :table WHERE :pk=?';
return $this->fetch($sql, $id);
}
/**
* @param array $data
* @param mixed $where
* @return int Number of affected rows
*/
public function update($data, $where)
{
if (is_int($where) || $where === (string) (int) $where) {
$where = $this->identify($this->key) . '=' . (int) $where;
}
return parent::update($data, $where);
}
/**
* @param int $id Int id
* @return int Number of affected rows
*/
public function delete($id)
{
$where = $this->identify($this->key) . '=' . (int) $id;
return $this->db->delete($this->table(), $where);
}
/**
* Creates order sql string
*
* @param array $params
* @param array $sortable
* @return string
*/
protected function order($params, $sortable = array('id'))
{
$sql = '';
if (isset($params['sort'])) {
$order = (isset($params['order']) && $params['order'] == 'desc') ? 'DESC' : 'ASC';
if (in_array($params['sort'], $sortable)) {
$sql = ' ORDER BY ' . $this->identify($params['sort']) . ' ' . $order;
}
}
return $sql;
}
/**
* Searches using like
*
* @param array $params
* @param array $searchable
* @param string $table_prefix
* @return string
*/
protected function search($params, $searchable = array('id'), $table_prefix = '')
{
$sql = '';
if (isset($params['q']) && isset($params['qt']) && in_array($params['qt'], $searchable)) {
if ($table_prefix) {
$sql = $table_prefix . '.';
}
$sql .= $this->identify($params['qt']) . ' LIKE ' . $this->quote('%' . $params['q'] . '%');
}
return $sql;
}
/**
* This method appends to params table and primary key.
* So they can be accessed through `:table` and `:pk` placeholders.
*
* @param string $sql
* @param array $params
* @return DbStatement
*/
protected function query($sql, $params = array())
{
if (!is_array($params)) {
$params = array($params);
}
$params = array(
'table' => new DbExpr($this->identify($this->table())),
'pk' => new DbExpr($this->identify($this->key)),
) + $params;
return $this->db->query($sql, $params);
}
/**
* @param string $data Request
* @param array $params Request parameters
* @param string $field Requested field name
* @param CacheKey $cache_key Key for caching in
* @return mixed
*/
protected function fetchField($data, $params = array(), $field, $cache_key = null)
{
if (!$cache_key || !$result = $cache_key->get()) {
$result = $this->query($data, $params)->fetchField($field);
if ($cache_key) {
$cache_key->set($result);
}
}
return $result;
}
/**
* @param string $data Request
* @param array $params Request parameters
* @param CacheKey $cache_key Key for caching in
* @return mixed
*/
protected function fetch($data, $params = array(), $cache_key = null)
{
if (!$cache_key || !$result = $cache_key->get()) {
$result = $this->query($data, $params)->fetch();
if ($cache_key) {
$cache_key->set($result);
}
}
return $result;
}
/**
* @param string $data
* @param array $params
* @param CacheKey $cache_key
* @return array
*/
protected function fetchAll($data, $params = array(), $cache_key = null)
{
if (!$cache_key || !$result = $cache_key->get()) {
$result = $this->query($data, $params)->fetchAll();
if ($cache_key) {
$cache_key->set($result);
}
}
return $result;
}
/**
* @param $criteria SqlCriteria
* @return string Sql string expression
*/
protected function criteria($criteria)
{
if (!is_array($criteria->select)) {
$criteria->select = array(new DbExpr($criteria->select));
}
foreach ($criteria->select as $field => &$term) {
if (is_int($field)) {
if ($term instanceof DbExpr) {
$term = (string) $term;
} else {
$term = $this->db->quoteIdentifier($term);
}
} else {
$term = $this->db->quoteIdentifier($field) . ' as ' . $this->db->quoteIdentifier($term);
}
}
foreach ($criteria->where as $cond => &$term) {
if (is_int($cond)) {
if ($term instanceof DbExpr) {
$term = (string) $term;
}
} else {
if (is_array($term)) {
$term = new DbExpr('(' . implode(',', array_map(function($item){return intval($item);}, $term)) . ')');
}
$term = $this->db->quoteInto($cond, $term);
}
}
foreach ($criteria->order as $field => $term) {
if (is_int($field)) {
$term = $this->db->quoteIdentifier($term);
} else {
$order_direction = '';
switch ($term) {
case self::ORDER_DESC:
$order_direction .= ' DESC';
break;
case self::ORDER_ASC:
$order_direction .= ' ASC';
break;
}
$term = $this->db->quoteIdentifier($field) . $order_direction;
}
}
if ($criteria->distinct != '') {
$criteria->distinct = 'DISTINCT ' . $this->db->quoteIdentifier($criteria->distinct);
}
if ($criteria->limit != '') {
$criteria->limit = implode(',',array_map(function($item){return intval($item);},explode(',',$criteria->limit)));
}
return 'SELECT ' . (($criteria->distinct) ? $criteria->distinct : implode(',', $criteria->select))
. ' FROM ' . $this->table()
. (($criteria->where) ? (' WHERE ' . implode(' AND ', $criteria->where)) : '')
. (($criteria->order) ? (' ORDER BY ' . implode(',', $criteria->order)) : '')
. (($criteria->limit) ? (' LIMIT ' . $criteria->limit) : '');
}
/**
* @param $criteria_data array
* @ex array(
* SqlModel::SELECT => '*',
* SqlModel::WHERE => array('field=?', 1),
* SqlModel::ORDER => array('last_name', 'first_name' => SqlModel::ORDER_DESC),
* SqlModel::LIMIT => 10)
* @return mixed|DbStatement[]
*/
public function find($criteria_data)
{
$criteria = new SqlCriteria();
foreach (self::$criteria_data_keys as $criteria_data_key) {
if (isset($criteria_data[$criteria_data_key])) {
$value = $criteria_data[$criteria_data_key];
switch ($criteria_data_key) {
case self::SELECT:
$criteria->select($value);
break;
case self::DISTINCT:
$criteria->distinct($value);
break;
case self::WHERE:
$criteria->where($value);
break;
case self::ORDER:
$criteria->order($value);
break;
case self::LIMIT:
$criteria->limit($value, ((isset($criteria_data[self::OFFSET])) ? $criteria_data[self::OFFSET] : null));
break;
}
}
}
$data = $this->criteria($criteria);
if (isset($criteria_data[self::ASSOC_BY_FIELD])) {
return self::fetchAllAssocByField($data, array(), $criteria_data[self::ASSOC_BY_FIELD], ((isset($criteria_data[self::ASSOC_AS_ARRAY])) ? $criteria_data[self::ASSOC_AS_ARRAY] : false), ((isset($criteria_data[self::ASSOC_BY_FIELD_IN_ARRAY])) ? $criteria_data[self::ASSOC_BY_FIELD_IN_ARRAY] : null));
} else {
return self::fetchAll($data);
}
}
/**
* @param $data string
* @param $params array
* @param null|string $field
* @param bool $assoc_as_array
* @param null|string $field_assoc_in_array
* @return array
* @throws ErrorException|GeneralException
*/
protected function fetchAllAssocByField($data, $params, $field = null, $assoc_as_array = false, $field_assoc_in_array = null)
{
$items = self::fetchAll($data, $params);
if (is_null($field)) {
$field = $this->key;
}
return self::assocByField($items, $field, $assoc_as_array, $field_assoc_in_array);
}
/**
* @param $items
* @param $field
* @param bool $assoc_as_array
* @param $field_assoc_in_array null|string
* @param $disable_assoc_in_array bool
* @return array
* @throws ErrorException|GeneralException
*/
public function assocByField($items, $field, $assoc_as_array = false, $field_assoc_in_array = null, $disable_assoc_in_array = false)
{
$items_assoc_by_field = array();
$primary_key_not_exists_in_items = false;
foreach ($items as $item) {
if (!isset($item->{$field})) {
throw new GeneralException('Undefined field. ' . $field);
}
if ($assoc_as_array) {
if (!isset($items_assoc_by_field[$item->{$field}])) {
$items_assoc_by_field[$item->{$field}] = array();
}
$items_assoc_by_field[$item->{$field}][] = $item;
if (!$primary_key_not_exists_in_items && $this->key && !isset($item->{$this->key})) {
$primary_key_not_exists_in_items = true;
}
} else {
if (isset($items_assoc_by_field[$item->{$field}])) {
throw new ErrorException('Field not unique. May be use assoc_as_array. ' . $field);
}
$items_assoc_by_field[$item->{$field}] = $item;
}
}
// Ассоциирование внутри каждого элемента массива по первичному ключу
// Если первичного ключа нет (не указан в модели или нет поля с первичным ключом в результах выборки) - ассоциирование внутри не производится
// Если указан $field_assoc_in_array - ассоциирование произовдится по этому полю (вернет ошибку, если этого поля нет в результатх выборки)
// Параметр $disable_assoc_in_array отключает ассоциирование внутри результирующего массива
if ($assoc_as_array && !$disable_assoc_in_array &&
(($this->key && !$primary_key_not_exists_in_items) || $field_assoc_in_array)
) {
foreach ($items_assoc_by_field as $key => $value) {
$items_assoc_by_field[$key] = self::assocByField($items_assoc_by_field[$key], (($field_assoc_in_array) ? $field_assoc_in_array : $this->key));
}
}
return $items_assoc_by_field;
}
public function assocByFieldAndGetKeys($items, $field)
{
$items = self::assocByField($items, $field, true, null, true);
return array_keys($items);
}
}