znt/lib/base/mao/mao.class.php

756 lines
19 KiB
PHP
Raw Permalink 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
/**
* ZenTaoPHP的mao类。
* The mao class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
/**
* Mao类。
* Mao class.
*
* @package framework
*/
class baseMao
{
/**
* 全局对象$app
* The global app object.
*
* @var object
* @access public
*/
public $app;
/**
* 全局对象$config
* The global config object.
*
* @var object
* @access public
*/
public $config;
/**
* 全局对象$dao
* The global dao object.
*
* @var object
* @access protected
*/
protected $dao;
/**
* 全局对象$cache
* The global cache object.
*
* @var object
* @access public
*/
public $cache = null;
/**
* 正在使用的表。
* The table of current query.
*
* @var string
* @access public
*/
public $table;
/**
* 查询的字段。
* The fields will be returned.
*
* @var array
* @access public
*/
public $fields;
/**
* 查询条件。
* Conditions.
*
* @var array
* @access public
*/
public $conditions;
/**
* 正在组装的查询条件。
* Condition.
*
* @var array
* @access public
*/
public $condition;
/**
* 是否在判断条件成立。
* Checking condition.
*
* @var bool
* @access public
*/
public $isConditionChecking;
/**
* 条件是否成立。
* Condition is true.
*
* @var bool
* @access public
*/
public $conditionIsTrue;
/**
* 待处理的数据。
* The data to be processed.
*
* @var array
* @access public
*/
public $data;
/**
* 待处理数据的column。
* The column of data.
*
* @var string
* @access public
*/
public $dataColumn;
/**
* 构造方法。
* The construct method.
*
* @access public
* @param object $app
* @return void
*/
public function __construct(object $app)
{
global $config;
$this->app = $app;
$this->config = $config;
$this->dao = $app->dao;
$this->cache = $app->cache;
$this->reset();
}
/**
* 设置$table属性。
* Set the $table property.
*
* @param string $table
* @access public
* @return void
*/
public function setTable(string $table)
{
$this->table = $table;
}
/**
* 设置$fields属性。
* Set the $fields property.
*
* @param array $fields
* @access public
* @return void
*/
public function setFields(array $fields)
{
$this->fields = $fields;
}
/**
* 设置条件。
* Set the $conditions property.
*
* @param array $conditions
* @access public
* @return void
*/
public function setConditions(array $conditions)
{
$this->conditions = $conditions;
$this->resetCondition();
}
/**
* 重置组装条件。
* Reset the $condition property.
*
* @access public
* @return void
*/
public function resetCondition()
{
$this->condition = array('field' => '', 'operator' => '', 'value' => null);
}
/**
* 添加条件。
* Add the $conditions property.
*
* @param string $condition
* @access public
* @return void
*/
public function addCondition()
{
$this->conditions[] = $this->condition;
}
/**
* 重置属性。
* Reset the vars.
*
* @access public
* @return void
*/
public function reset()
{
$this->setFields(array());
$this->setTable('');
$this->setConditions([]);
}
/**
* select方法调用sql::select()。
* The select method, call sql::select().
*
* @param string $fields
* @access public
* @return static|sql|baseDAO the dao object self.
*/
public function select(string $fields = '*')
{
$this->conditions = [];
$fields = explode(',', $fields);
$alias = [];
foreach($fields as $field)
{
$fieldInfo = explode(' ', trim($field));
if(count($fieldInfo) == 1)
{
$alias[$fieldInfo[0]] = $fieldInfo[0];
}
elseif(count($fieldInfo) == 2)
{
$alias[$fieldInfo[0]] = $fieldInfo[1];
}
else
{
$alias[$fieldInfo[0]] = $fieldInfo[2];
}
}
$this->setFields($alias);
return $this;
}
/**
* 设置要操作的表。
* Set the from table.
*
* @param string $tableName
* @access public
* @return static|sql the dao object self.
*/
public function from(string $tableName)
{
$this->setTable($tableName);
return $this;
}
/**
* 开始条件判断。
* Begin condition judge.
*
* @param bool|string $condition
* @access public
* @return static|sql the sql object.
*/
public function beginIF(bool|string $conditionResult)
{
$this->isConditionChecking = true;
$this->conditionIsTrue = (bool)$conditionResult;
return $this;
}
/**
* 结束条件判断。
* End the condition judge.
*
* @access public
* @return static|sql the sql object.
*/
public function fi()
{
$this->isConditionChecking = false;
if(!$this->conditionIsTrue)
{
$this->resetCondition();
return $this;
}
$this->addCondition();
$this->resetCondition();
return $this;
}
/**
* 创建WHERE部分。
* Create the where part.
*
* @param string $field the field name
* @access public
* @return static the dao object.
*/
public function where(string $field)
{
$this->resetCondition();
$this->condition['field'] = trim($field, '`');
return $this;
}
/**
* 创建andWHERE部分。
* Create the andWhere part.
*
* @param string $field the field name
* @access public
* @return static the dao object.
*/
public function andWhere(string $field)
{
$this->resetCondition();
$this->condition['field'] = trim($field, '`');
return $this;
}
/**
* 创建eq部分。
* Create the eq part.
*
* @param string $value
* @access public
* @return static the dao object.
*/
public function eq($value)
{
$this->condition['operator'] = 'eq';
$this->condition['value'] = $value;
if(!$this->isConditionChecking)
{
$this->addCondition();
$this->resetCondition();
}
return $this;
}
/**
* 创建ne部分。
* Create the ne part.
*
* @param string $value
* @access public
* @return static the dao object.
*/
public function ne($value)
{
$this->condition['operator'] = 'ne';
$this->condition['value'] = $value;
if(!$this->isConditionChecking)
{
$this->addCondition();
$this->resetCondition();
}
return $this;
}
/**
* 创建in部分。
* Create the in part.
*
* @param string|array $value
* @access public
* @return static the dao object.
*/
public function in(string|array $value)
{
$this->condition['operator'] = 'in';
$this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value;
if(!$this->isConditionChecking)
{
$this->addCondition();
$this->resetCondition();
}
return $this;
}
/**
* 创建notin部分。
* Create the in part.
*
* @param string|array $value
* @access public
* @return static the dao object.
*/
public function notin(string|array $value)
{
$this->condition['operator'] = 'notin';
$this->condition['value'] = is_string($value) ? explode(',', str_replace(' ', '', $value)) : $value;
if(!$this->isConditionChecking)
{
$this->addCondition();
$this->resetCondition();
}
return $this;
}
/**
* 判断匹配条件。
* Check condition is matched.
*
* @param object $object
* @param array $conditions
* @access public
* @return bool
*/
private function isConditionMatched(object $object, array $conditions)
{
foreach($conditions as $condition)
{
$value = $object->{$condition['field']};
if($condition['operator'] == 'eq')
{
if($condition['value'] != $value) return false;
}
elseif($condition['operator'] == 'ne')
{
if($condition['value'] == $value) return false;
}
elseif($condition['operator'] == 'in')
{
if(!in_array($value, $condition['value'])) return false;
}
elseif($condition['operator'] == 'notin')
{
if(in_array($value, $condition['value'])) return false;
}
else
{
return false;
}
}
return true;
}
/**
* 获取一个记录。
* Fetch one record.
*
* @param string $keyField 如果已经设置获取的字段,则只返回这个字段的值,否则返回这个记录。
* if the field is set, only return the value of this field, else return this record
* @access public
* @return object|mixed
*/
public function fetch(string $keyField = '')
{
if(empty($this->cache) || empty($this->config->cache->raw[$this->table]) || empty($this->conditions)) return $this->fetchFromDB('fetch', $keyField);
$rawResult = null;
/* 如果查询条件中有主键字段则尝试通过主键字段从缓存中获取数据。If the query condition contains the primary key field, try to get data from the cache by the primary key field. */
$field = $this->config->cache->raw[$this->table];
foreach($this->conditions as $condition)
{
if($condition['field'] == $field && $condition['operator'] == 'eq')
{
$rawResult = $this->cache->fetch($this->table, $condition['value']);
if($rawResult)
{
$keyField = trim($keyField, '`');
return $keyField ? $rawResult->$keyField : $rawResult;
}
break;
}
}
return $this->fetchFromDB('fetch', $keyField);
}
/**
* 获取所有记录。
* Fetch all records.
*
* @param string $keyField 返回以该字段做键的记录
* the key field, thus the return records is keyed by this field
* @access public
* @return array the records
*/
public function fetchAll(string $keyField = ''): array
{
if(empty($this->cache) || empty($this->config->cache->raw[$this->table]) || empty($this->conditions)) return $this->fetchFromDB('fetchAll', $keyField, false);
$rawResult = null;
/* 如果查询条件匹配到主键字段则通过主键字段从缓存中获取数据。If the query condition matches the primary key field, get data from the cache by the primary key field. */
$field = $this->config->cache->raw[$this->table];
foreach($this->conditions as $condition)
{
if($condition['field'] == $field && $condition['operator'] == 'in')
{
$value = $condition['value'];
if(is_numeric($value)) $value = [$value];
if(is_string($value)) $value = explode(',', str_replace(' ', '', $value));
if(is_array($value))
{
$rawResult = $this->cache->fetchAll($this->table, array_filter($value));
foreach($rawResult as $index => $row)
{
/* 根据主键字段获取数据后需要逐一匹配查询条件。After getting the data according to the primary key field, you need to match the query conditions one by one. */
if(!$this->isConditionMatched($row, $this->conditions)) unset($rawResult[$index]);
}
break;
}
}
}
/* 如果查询条件没有匹配到主键字段则从计算结果缓存中获取数据。If the query condition does not match the primary key field, get data from the calculated result cache. */
//if(is_null($rawResult)) $rawResult = $this->cache->fetchAutoCache($this->table, $this->conditions);
/* 如果没有缓存从数据库中获取数据。If there is no cache, get data from the database. */
if(is_null($rawResult)) return $this->fetchFromDB('fetchAll', $keyField, false);
if(!$rawResult) return [];
/* 从缓存中获取到的是原始数据需要根据查询字段和索引字段进行处理。The data obtained from the cache is raw data, which needs to be processed according to the query fields and index fields. */
$result = [];
foreach($rawResult as $row)
{
$data = new stdclass();
foreach($this->fields as $field => $alias)
{
if($field == '*')
{
$data = $row;
break;
}
$data->$alias = $row->$field;
}
empty($keyField) ? $result[] = $data : $result[$row->$keyField] = $data;
}
return $result;
}
/**
* 获取的记录是以关联数组的形式
* Fetch array like key=>value.
*
* 如果没有设置参数,用首末两键作为参数。
* If the keyFiled and valueField not set, use the first and last in the record.
*
* @param string $keyField
* @param string $valueField
* @access public
* @return array
*/
public function fetchPairs(string $keyField = '', string $valueField = '')
{
if(empty($this->cache) || empty($this->config->cache->raw[$this->table])) return $this->fetchFromDB('fetchPairs', $keyField, $valueField);
$rows = $this->fetchAll();
if(empty($rows)) return [];
if(empty($keyField)) $keyField = $this->fields[0];
if(empty($valueField)) $valueField = $this->fields[1];
$pairs = [];
foreach($rows as $row)
{
$pairs[$row->$keyField] = $row->$valueField;
}
return $pairs;
}
/**
* 从数据库中获取数据。
* Fetch data from database.
*
* @param string $fetchFunc fetch|fetchAll
* @param string $keyField
* @param string|bool $valueField
* @access private
* @return mixed
*/
private function fetchFromDB(string $fetchFunc, string $keyField, string|bool $valueField = '')
{
$fields = implode(',', $this->fields);
$this->dao->select($fields)->from($this->table)->where('1=1');
foreach($this->conditions as $condition)
{
$func = $condition['operator'];
$this->dao->andWhere($condition['field'])->$func($condition['value']);
}
return $this->dao->$fetchFunc($keyField, $valueField);
}
/**
* 把名为 findByXXX 的方法转换为 where 条件。
* Convert the method findByXXX to where condition.
*
* @param string $method
* @param array $args
* @access private
* @return object the mao object.
*/
private function findBy(string $method, array $args)
{
$field = str_replace('findby', '', $method);
if(count($args) == 1)
{
$operator = 'eq';
$value = $args[0];
}
else
{
$operator = $args[0];
$value = $args[1];
}
$this->setFields(['*']);
$this->conditions = [['field' => $field, 'operator' => $operator, 'value' => $value]];
return $this;
}
/**
* 获取缓存数据拼接到已有数据。
* Append cache fields to data.
*
* @param array $data
* @param string $keyField
* @access public
* @return void
*/
public function into(array $data, $keyField)
{
if(empty($data) || empty($keyField)) return $data;
/* Get data keys as conditions. */
$keyList = [];
foreach($data as $index => $row) $keyList[$index] = $row->$keyField;
if(empty($this->cache))
{
/* 如果缓存关闭从数据库中获取。If the cache is off, get from the database. */
$primaryKey = $this->config->cache->raw[$this->table];
$fields = $primaryKey . ',' . implode(',', array_keys($this->fields));
$cacheResult = $this->dao->select($fields)->from($this->table)->where($primaryKey)->in(array_unique($keyList))->fetchAll($primaryKey);
}
else
{
$cacheResult = $this->cache->fetchAll($this->table, array_unique($keyList));
}
foreach($data as $index => $row)
{
$key = $keyList[$index];
if(isset($cacheResult[$key]))
{
$cacheRow = $cacheResult[$key];
foreach($this->fields as $field => $alias) $row->$alias = $cacheRow->$field;
}
else
{
foreach($this->fields as $field => $alias) $row->$alias = '';
}
}
}
/**
* 清除缓存。
* Clear cache.
*
* @access public
* @return void
*/
public function clearCache()
{
if(!empty($this->cache)) $this->cache->clear();
}
/**
* 根据当前缓存键获取缓存。
* Get cache according to the current cache key.
*
* @access public
* @return mixed
*/
public function get()
{
if(empty($this->cache)) return false;
return $this->cache->get();
}
/**
* 魔术方法。
* 1. 转换 findByxxx 为 where 条件。
* 2. 调用cache对象的方法。
* Magic method.
* 1. Convert findByxxx to where condition.
* 2. Call the cache object method.
*
* @param string $method
* @param array $args
* @access public
* @return mixed
*/
public function __call(string $method, array $args)
{
$method = strtolower($method);
/*
* 如果是findByxxx转换为where条件语句。
* findByxxx, xxx as will be in the where.
**/
if(strpos($method, 'findby') !== false) return $this->findBy($method, $args);
if(empty($this->cache)) return $this;
if(method_exists($this->cache, $method)) return call_user_func_array([$this->cache, $method], $args);
$this->app->triggerError("Method $method not found in class baseMao.", __FILE__, __LINE__, $this->config->debug >= 2);
}
}