|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894 |
- <?php
-
- namespace Kuxin;
-
- use Kuxin\Helper\Json;
-
- /**
- * Class Model
- *
- * @package Kuxin
- * @author Pakey <pakey@qq.com>
- */
- class Model
- {
-
- /**
- * 数据库节点信息
- *
- * @var string
- */
- protected $connection = 'common';
-
- /**
- * 数据表名
- *
- * @var string
- */
- protected $table = null;
-
- /**
- * 数据表的主键信息
- *
- * @var string
- */
- protected $pk = 'id';
-
- /**
- * model所对应的数据表名的前缀
- *
- * @var string
- */
- protected $prefix = '';
-
- /**
- * SQL语句容器,用于存放SQL语句,为SQL语句组装函数提供SQL语句片段的存放空间。
- *
- * @var array
- */
- protected $data = [];
-
- /**
- * SQL语句容器,用于存放SQL语句,为SQL语句组装函数提供SQL语句片段的存放空间。
- *
- * @var array
- */
- protected $bindParams = [];
-
- /**
- * 错误信息
- *
- * @var string
- */
- protected $errorinfo = null;
-
- /**
- * @var \Kuxin\Db\Mysql
- */
- protected $db = null;
-
-
- /**
- * Model constructor.
- */
- public function __construct()
- {
- $this->prefix = Config::get('database.prefix');
- }
-
- /**
- * 实例化 单例
- *
- * @param ...
- * @return static
- */
- public static function I()
- {
- return Loader::instance(static::class, func_get_args());
- }
-
- /**
- * @return \Kuxin\Db\Mysql
- */
- public function db(): \Kuxin\Db\Mysql
- {
- if (!$this->db) {
- $this->db = DI::DB($this->connection);
- }
- return $this->db;
- }
-
- public function __call($method, $args)
- {
- trigger_error('不具备的Model操作' . $method);
- }
-
- /**
- * 求和
- * @param string $value
- * @return string
- */
- public function sum(string $value): string
- {
- $this->data['field'] = "sum({$value}) as kx_num";
- return $this->getField('kx_num') ?: 0;
- }
-
- /**
- * 平均数
- * @param string $value
- * @return string
- */
- public function avg(string $value): string
- {
- $this->data['field'] = "avg({$value}) as kx_num";
- return $this->getField('kx_num');
- }
-
- /**
- * @param string $value
- * @return string
- */
- public function min(string $value): string
- {
- $this->data['field'] = "min({$value}) as kx_num";
- return $this->getField('kx_num');
- }
-
- /**
- * @param string $value
- * @return string
- */
- public function max(string $value)
- {
- $this->data['field'] = "max({$value}) as kx_num";
- return (int)$this->getField('kx_num');
- }
-
- /**
- * @param string $value
- * @return int
- */
- public function count(string $value = '*'): int
- {
- $this->data['field'] = "count({$value}) as kx_num";
- return (int)$this->getField('kx_num');
- }
-
- /**
- * @param $data
- * @param array $option
- * @return $this
- */
- public function where($data, array $option = [])
- {
- if (is_string($data)) {
- $this->data['where'][] = ['_string' => $data];
- $this->bindParams = array_merge($this->bindParams, $option);
- } elseif (is_array($data)) {
- foreach ($data as $field => $var) {
- //where条件
- $this->data['where'][] = [$field => $var];
- }
- }
- return $this;
- }
-
- /**
- * @param $value
- * @return $this
- */
- public function database(string $value)
- {
- $this->data['database'] = $value;
- return $this;
- }
-
- /**
- * @param $value
- * @return $this
- */
- public function config(string $value)
- {
- $this->data['config'] = $value;
- return $this;
- }
-
- public function distinct(string $value)
- {
- $this->data['distinct'] = $value;
- return $this;
- }
-
- public function table(string $value)
- {
- $this->data['table'] = $value;
- return $this;
- }
-
- public function having(string $value)
- {
- if (is_string($value)) {
- $this->data['having'][] = ['_string' => $value];
- } else {
- $this->data['having'] = $value;
- }
- return $this;
- }
-
- public function group($value)
- {
- $this->data['group'] = $value;
- return $this;
- }
-
- public function page($value)
- {
- $this->data['page'] = $value;
- return $this;
- }
-
- public function limit($value)
- {
- $this->data['limit'] = $value;
- return $this;
- }
-
- public function index($value)
- {
- $this->data['index'] = $value;
- return $this;
- }
-
- public function order($value)
- {
- $this->data['order'] = $value;
- return $this;
- }
-
- public function field($value)
- {
- $this->data['field'] = $value;
- return $this;
- }
-
- public function join(string $table, string $on = '', string $type = 'left')
- {
- if (is_array($table)) {
- $this->data['join'][] = $table;
- } else {
- $this->data['join'][] = ['table' => $table, 'on' => $on, 'type' => $type];
- }
- return $this;
- }
-
- public function getTableName()
- {
- if (!$this->table) {
- trigger_error('请设置表名', E_USER_ERROR);
- }
- return $this->prefix . $this->table;
- }
-
- public function getTableField(string $tablename)
- {
- if (!($tablename = trim($tablename, ' '))) {
- trigger_error('您必须设置表名后才可以使用该方法');
- }
- if (($offset = stripos($tablename, ' as ')) !== false) {
- $tablename = substr($tablename, 0, $offset);
- } elseif (($offset = stripos($tablename, ' ')) !== false) {
- $tablename = substr($tablename, 0, $offset);
- }
- return Registry::get('tablefield_' . $tablename, function () use ($tablename) {
- $fields = DI::Cache()->debugGet('tablefield_' . $tablename, function () use ($tablename) {
- $fields = [];
- if ($tableInfo = $this->db()->fetchAll("SHOW FIELDS FROM {$tablename}")) {
- foreach ($tableInfo as $v) {
- if ($v['Key'] == 'PRI')
- $pks[] = strtolower($v['Field']);
- $fields[strtolower($v['Field'])] = strpos($v['Type'], 'int') !== false;
- }
- DI::Cache()->set('tablefield_' . $tablename, $fields);
- return $fields;
- } else {
- trigger_error('获取表' . $tablename . '信息发生错误 ', E_USER_ERROR);
- return false;
- }
- });
- Registry::set('tablefield_' . $tablename, $fields);
- return $fields;
- });
- }
-
- public function getPk(): string
- {
- return $this->pk;
- }
-
- /**
- * @param array $data
- * @param bool $replace
- * @return mixed
- */
- public function insert(array $data, bool $replace = false)
- {
- if ($data) {
- $insertData = [];
- $tablename = $this->parseTable();
- $fields = $this->getTableField($tablename);
- foreach ($data as $k => $v) { // 过滤参数
- if (isset($fields[$k])) {
- //写入数据
- $insertData[$this->parseKey($k)] = ':' . $k;
- //参数绑定
- $this->bindParams[':' . $k] = $this->parseBindValue($v);
- }
- }
- $sql = ($replace ? 'REPLACE' : 'INSERT') . ' INTO ' . $tablename . ' (' . implode(',', array_keys($insertData)) . ') VALUES (' . implode(',', $insertData) . ');';
-
- $result = $this->db()->execute($sql, $this->bindParams);
-
- $this->free();
-
- if (true === $result) {
- return $this->db()->lastInsertId();
- } else {
- return false;
- }
- } else {
- trigger_error('你的数据呢?', E_USER_WARNING);
- return false;
- }
- }
-
- /**
- * 插入记录
- *
- * @access public
- * @param mixed $datas 数据
- * @param boolean $replace 是否replace
- * @return false | integer
- */
- public function insertAll(array $datas, bool $replace = false)
- {
- if ($datas) {
- $values = [];
- $tablename = $this->parseTable();
- $fields = $this->getTableField($tablename);
- foreach ($datas as $data) {
- $value = [];
- foreach ($data as $key => $val) {
- if (isset($fields[$key])) {
- $value[$key] = $this->parseValue($val);
- }
- }
- $values[] = '(' . implode(',', $value) . ')';
- }
- $fields = array_map([$this, 'parseKey'], array_keys($datas[0]));
- $sql = ($replace ? 'REPLACE' : 'INSERT') . ' INTO ' . $this->parseTable() . ' (' . implode(',', $fields) . ') VALUES ' . implode(',', $values);
- $result = $this->db()->execute($sql);
- $this->free();
- if (true === $result) {
- return intval($this->db()->lastInsertId()) + count($datas) - 1;
- } else {
- return false;
- }
- } else {
- trigger_error('你的数据呢?', E_USER_WARNING);
- return false;
- }
- }
-
-
- /**
- * @param array $data
- * @return bool|int
- */
- public function update(array $data)
- {
- if ($data) {
- $sets = [];
- $tablename = $this->parseTable();
- $fields = $this->getTableField($tablename);
- if (isset($data[$this->pk])) {
- $this->where([$this->pk => $data[$this->pk]]);
- unset($data[$this->pk]);
- }
- foreach ($data as $k => $v) { // 数据解析
- if (isset($fields[$k])) {
- if (is_array($v) && isset($v[0]) && is_string($v[0]) && strtolower($v[0]) == 'exp') {
- $sets[] = $this->parseKey($k) . '= ' . $v['1'];
- } else {
- $sets[] = $this->parseKey($k) . '= :' . $k;
- //参数绑定
- $this->bindParams[':' . $k] = $this->parseBindValue($v);
- }
- }
- }
- $sql = 'UPDATE ' . $this->parseTable() . ' SET ' . implode(',', $sets)
- . $this->parseWhere()
- . $this->parseOrder()
- . $this->parseLimit();
-
- $result = $this->db()->execute($sql, $this->bindParams);
-
- $this->free();
-
- if (true === $result) {
- return $this->db()->rowCount();
- } else {
- return false;
- }
- } else {
- trigger_error('你的数据呢?', E_USER_WARNING);
- return false;
- }
- }
-
- public function delete()
- {
- if (empty($this->data['where'])) {
- trigger_error('删除语句必须制定where条件');
- }
- $sql = 'DELETE' . ' FROM ' . $this->parseTable()
- . $this->parseWhere()
- . $this->parseOrder()
- . $this->parseLimit();
- $this->data = [];
- $result = $this->db()->execute($sql, $this->bindParams);
-
- $this->free();
-
- if (true === $result) {
- return $this->db()->rowCount();
- } else {
- return false;
- }
- }
-
- public function find($id = null)
- {
- if ($id) {
- $this->data['where'][] = [$this->pk => $id];
- }
- $this->data['limit'] = 1;
- $sql = "SELECT " . $this->parseField() . ' FROM '
- . $this->parseTable()
- . $this->parseIndex()
- . $this->parseJoin()
- . $this->parseWhere()
- . $this->parseGroup()
- . $this->parseHaving()
- . $this->parseOrder()
- . $this->parseLimit()
- . $this->parseUnion();
- //清空存储
- $this->data = [];
- return $this->fetch($sql, $this->bindParams);
- }
-
- /**
- * @return array|bool
- */
- public function select()
- {
- $sql = "SELECT " . $this->parseField() . ' FROM '
- . $this->parseTable()
- . $this->parseIndex()
- . $this->parseJoin()
- . $this->parseWhere()
- . $this->parseGroup()
- . $this->parseHaving()
- . $this->parseOrder()
- . $this->parseLimit()
- . $this->parseUnion();
- $this->data = [];
- $this->errorinfo = ''; //清空存储
- return $this->fetchAll($sql, $this->bindParams);
- }
-
- /**
- * 获取具体字段的值
- *
- * @param $field
- * @param bool $multi 是否返回数组
- * @return mixed|null|string
- */
- public function getField($field, $multi = false)
- {
- if (empty($this->data['field'])) {
- $this->data['field'] = $field;
- }
- if ($multi) {
- $result = $this->select();
- if ($result === false) {
- return false;
- } elseif ($result) {
- if (strpos($field, ',')) {
- $field = explode(',', $field, 2)[0];
- return array_column($result, null, $field);
- } else {
- return array_column($result, $field);
- }
- } else {
- return [];
- }
- } else {
- $result = $this->find();
- if ($result === false) {
- return false;
- } elseif (isset($result[$field])) {
- return $result[$field];
- } else {
- return null;
- }
- }
- }
-
- /**
- * 设置某个字段的值
- *
- * @param $field
- * @param $data
- * @return bool|int
- */
- public function setField(string $field, $data)
- {
- return $this->update([$field => $data]);
- }
-
- /**
- * 增加数据库中某个字段值
- *
- * @param $field
- * @param int $step
- * @return bool|int
- */
- public function setInc(string $field, int $step = 1)
- {
- return $this->setField($field, ['exp', "`{$field}` + {$step}"]);
- }
-
- /**
- * 减少数据库中某个字段值
- *
- * @param $field
- * @param int $step
- * @return bool|int
- */
- public function setDec(string $field, int $step = 1)
- {
- return $this->setField($field, ['exp', "`{$field}` - {$step}"]);
- }
-
- public function getLastSql(): string
- {
- return $this->db()->lastSql();
- }
-
- public function getError()
- {
- return $this->db()->errorInfo();
- }
-
- public function startTrans()
- {
- $this->db()->startTrans();
- }
-
- public function commit()
- {
- $this->db()->commit();
- }
-
- public function rollback()
- {
- $this->db()->rollback();
- }
-
- public function fetch(string $sql, array $bindParams = [])
- {
- $result = $this->db()->fetch($sql, $bindParams);
- $this->free();
- return $result;
- }
-
- public function fetchAll(string $sql, array $bindParams = [])
- {
- $result = $this->db()->fetchAll($sql, $bindParams);
- $this->free();
- return $result;
- }
-
- public function execute(string $sql, array $bindParams = [])
- {
- $result = $this->db()->execute($sql, $bindParams);
- $this->free();
- return $result;
- }
-
- public function parseCount(string $method)
- {
- $this->data['field'] = "{$method}({$this->data['field']}) as kx_num";
- return $this->getField('kx_num');
- }
-
- /**
- * 字段和表名处理添加`
- *
- * @access protected
- * @param string $key
- * @return string
- */
- protected function parseKey(string $key): string
- {
- $key = trim($key);
- if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
- $key = '`' . $key . '`';
- }
- return $key;
- }
-
- /**
- * value分析
- *
- * @access protected
- * @param mixed $value
- * @return mixed
- */
- protected function parseBindValue($value)
- {
- if (is_array($value)) {
- $value = Json::encode($value);
- } elseif (is_bool($value)) {
- $value = $value ? 1 : 0;
- } elseif (is_null($value)) {
- $value = null;
- }
- return $value;
- }
-
- /**
- * value分析
- *
- * @access protected
- * @param mixed $value
- * @return string
- */
- protected function parseValue($value)
- {
- if (is_string($value)) {
- $value = $this->db()->quote($value);
- }
- if (isset($value[0]) && is_string($value[0]) && strtolower($value[0]) == 'exp') {
- $value = $value[1];
- } elseif (is_array($value)) {
- $value = $this->db()->quote(Json::encode($value));
- } elseif (is_bool($value)) {
- $value = $value ? '1' : '0';
- } elseif (is_null($value)) {
- $value = 'null';
- }
- return $value;
- }
-
- protected function parseWhere()
- {
- if (empty($this->data['where'])) {
- return ' WHERE 1';
- } else {
- return ' WHERE ' . $this->parseWhereCondition($this->data['where']);
- }
- }
-
- protected function parseWhereCondition(array $condition, $logic = 'AND')
- {
- $wheres = [];
- $tablename = $this->parseTable();
- $fields = $this->getTableField($tablename);
- foreach ($condition as $var) {
- $k = key($var);
- if (($offset = strpos($k, '.')) !== false) {
- $k = substr($k, $offset + 1);
- }
- $v = current($var);
- if (isset($fields[$k])) {
- if (empty($this->data['join'])) {
- $wheres[] = '(' . $this->parseWhereItem($k, $v) . ')';
- } else {
- $wheres[] = '(' . $this->parseWhereItem($this->parseKey($tablename) . '.' . $k, $v) . ')';
- }
- } elseif (is_array($v) && in_array(strtolower($k), ['or', 'and', 'xor'])) {
- $where[] = $this->parseWhereCondition($v, $k);
- } elseif ($k == '_logic' && in_array(strtolower($v), ['or', 'and', 'xor'])) {
- $logic = ' ' . strtoupper($v) . ' ';
- } elseif ($k == '_string') {
- $wheres[] = '(' . $v . ')';
- }
- }
- return ($wheres === []) ? 1 : implode(" {$logic} ", $wheres);
- }
-
- /**
- * @param $field
- * @param $var
- * @return mixed
- */
- protected function parseWhereItem(string $field, $var)
- {
- //参数绑定key
- $bindkey = ':' . $field . '_' . count($this->bindParams);
- $field = $this->parseKey($field);
- if (is_array($var)) {
- switch (strtolower($var['0'])) {
- case '>':
- case '<':
- case '>=':
- case '<=':
- case '=':
- case '<>':
- case 'like':
- case 'not like':
- //参数绑定存储
- $this->bindParams[$bindkey] = $this->parseBindValue($var['1']);
- return $field . ' ' . $var['0'] . ' ' . $bindkey;
- case 'in':
- case 'not in':
- if (empty($var['1']))
- return '1';
- if (is_array($var['1'])) {
- $inBindVar = [];
- foreach ($var['1'] as $num => $inval) {
- $inBindKey = $bindkey . '_' . $num;
- $inBindVar[] = $inBindKey;
- //
- $this->bindParams[$inBindKey] = $this->parseBindValue($inval);
- }
- $var['1'] = implode(',', $inBindVar);
- }
- return "{$field} {$var['0']} ( {$var['1']} )";
- case 'between':
- case 'not between':
- if (is_string($var['1'])) {
- $var['1'] = explode(',', $var['1']);
- }
- $this->bindParams[$bindkey . '_0'] = $this->parseBindValue($var['1']['0']);
- $this->bindParams[$bindkey . '_1'] = $this->parseBindValue($var['1']['1']);
- return "{$field} {$var['0']} {$bindkey}_0 and {$bindkey}_1";
- case 'exp':
- return "{$var['1']}";
- default:
- return '1';
- }
- } else {
- //参数绑定存储
- $this->bindParams[$bindkey] = $this->parseBindValue($var);
- return $field . ' = ' . $bindkey;
- }
- }
-
- protected function parseOrder()
- {
- if (!empty($this->data['order'])) {
- if (is_string($this->data['order'])) {
- return ' ORDER BY ' . $this->data['order'];
- }
- }
- return '';
- }
-
- protected function parseGroup()
- {
- if (!empty($this->data['group'])) {
- if (is_string($this->data['group'])) {
- return ' GROUP BY ' . $this->parseKey($this->data['group']);
- } elseif (is_array($this->data['group'])) {
- $this->data['group'] = array_map($this->data['group'], [$this, 'parseKey']);
- return ' GROUP BY ' . implode(',', $this->data['group']);
- }
- }
- return '';
- }
-
- protected function parseHaving()
- {
- if (empty($this->data['having'])) {
- return '';
- }
- return ' HAVING ' . $this->parseWhereCondition($this->data['having']);
- }
-
- protected function parseLimit()
- {
- if (isset($this->data['page'])) {
- // 根据页数计算limit
- if (strpos($this->data['page'], ',')) {
- list($page, $listRows) = explode(',', $this->data['page']);
- } else {
- $page = $this->data['page'];
- }
- $page = $page ? $page : 1;
- $listRows = isset($listRows) ? $listRows : (is_numeric($this->data['limit']) ? $this->data['limit'] : 20);
- $offset = $listRows * ((int)$page - 1);
- return ' LIMIT ' . $offset . ',' . $listRows;
- } elseif (!empty($this->data['limit'])) {
- return ' LIMIT ' . $this->data['limit'];
- } else {
- return '';
- }
- }
-
- protected function parseUnion()
- {
-
- }
-
- protected function parseJoin()
- {
- if (empty($this->data['join']))
- return '';
- $str = '';
- foreach ($this->data['join'] as $join) {
- $table = $join['table'];
- $type = $join['type'];
- $on = $join['on'];
- if (empty($table)) {
- return '';
- } elseif (strpos($table, $this->prefix) === false) {
- $table = $this->prefix . $table;
- }
- $str .= ' ' . $type . ' JOIN ' . $table . ' ON ' . $on;
- }
-
- return $str;
- }
-
- protected function parseField()
- {
- if (empty($this->data['field'])) {
- return '*';
- } else {
- if (is_string($this->data['field'])) {
- $this->data['field'] = explode(',', $this->data['field']);
- }
- $this->data['field'] = array_map([self::class, 'parseKey'], $this->data['field']);
- return implode(',', $this->data['field']);
- }
- }
-
- protected function parseTable()
- {
- if (empty($this->data['table'])) {
- return $this->getTableName();
- } else {
- $table = strtolower(strpos($this->data['table'], $this->prefix) === false) ? $this->prefix . $this->data['table'] : $this->data['table'];
- }
- $table = $this->parseKey($table);
- //判断是否带数据库
- return ((empty($this->data['database'])) ? $table : $this->parseKey($this->data['db']) . '.' . $table);
- }
-
- protected function parseIndex()
- {
- if (empty($this->data['index'])) {
- return '';
- } else {
- return 'force index (' . $this->data['index'] . ')';
- }
- }
-
- protected function parseDistinct()
- {
- return $this->data['distinct'] ? ' DISTINCT ' : '';
- }
-
- protected function free()
- {
- $this->data = [];
- $this->bindParams = [];
- $this->attribute = [];
- }
-
-
- }
|