diff --git a/core/library/think/Model.php b/core/library/think/Model.php index 12e34bf5..a870f347 100644 --- a/core/library/think/Model.php +++ b/core/library/think/Model.php @@ -41,7 +41,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess { // 数据库对象池 - private static $links = []; + protected static $links = []; // 数据库配置 protected $connection = []; // 当前模型名称 @@ -85,8 +85,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess protected $createTime = 'create_time'; // 更新时间字段 protected $updateTime = 'update_time'; - // 删除时间字段 - protected $deleteTime = 'delete_time'; // 时间字段取出后的默认时间格式 protected $dateFormat = 'Y-m-d H:i:s'; // 字段类型或者格式转换 @@ -168,10 +166,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $query->pk($this->pk); } - // 全局作用域 - if (method_exists($this, 'base')) { - call_user_func_array([$this, 'base'], [ & $query]); - } self::$links[$model] = $query; } // 返回当前模型的数据库查询对象 @@ -275,27 +269,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ public function setAttr($name, $value, $data = []) { - if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime, $this->deleteTime])) { + if (is_null($value) && $this->autoWriteTimestamp && in_array($name, [$this->createTime, $this->updateTime])) { // 自动写入的时间戳字段 - if (isset($this->type[$name])) { - $type = $this->type[$name]; - if (strpos($type, ':')) { - list($type, $param) = explode(':', $type, 2); - } - switch ($type) { - case 'datetime': - $format = !empty($param) ? $param : $this->dateFormat; - $value = date($format, $_SERVER['REQUEST_TIME']); - break; - case 'timestamp': - $value = $_SERVER['REQUEST_TIME']; - break; - } - } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), ['datetime', 'date', 'timestamp'])) { - $value = date($this->dateFormat, $_SERVER['REQUEST_TIME']); - } else { - $value = $_SERVER['REQUEST_TIME']; - } + $value = $this->autoWriteTimestamp($name); } else { // 检测修改器 $method = 'set' . Loader::parseName($name, 1) . 'Attr'; @@ -316,6 +292,36 @@ abstract class Model implements \JsonSerializable, \ArrayAccess return $this; } + /** + * 自动写入时间戳 + * @access public + * @param string $name 时间戳字段 + * @return mixed + */ + protected function autoWriteTimestamp($name) + { + if (isset($this->type[$name])) { + $type = $this->type[$name]; + if (strpos($type, ':')) { + list($type, $param) = explode(':', $type, 2); + } + switch ($type) { + case 'datetime': + $format = !empty($param) ? $param : $this->dateFormat; + $value = date($format, $_SERVER['REQUEST_TIME']); + break; + case 'timestamp': + $value = $_SERVER['REQUEST_TIME']; + break; + } + } elseif (is_string($this->autoWriteTimestamp) && in_array(strtolower($this->autoWriteTimestamp), ['datetime', 'date', 'timestamp'])) { + $value = date($this->dateFormat, $_SERVER['REQUEST_TIME']); + } else { + $value = $_SERVER['REQUEST_TIME']; + } + return $value; + } + /** * 数据写入 类型转换 * @access public @@ -777,9 +783,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess /** * 删除当前的记录 * @access public + * @param bool $force 是否强制删除 * @return integer */ - public function delete() + public function delete($force = false) { if (false === $this->trigger('before_delete', $this)) { return false; @@ -960,7 +967,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ public static function get($data = null, $with = [], $cache = false) { - $query = self::parseQuery($data, $with, $cache); + $query = static::parseQuery($data, $with, $cache); return $query->find($data); } @@ -975,7 +982,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess */ public static function all($data = null, $with = [], $cache = false) { - $query = self::parseQuery($data, $with, $cache); + $query = static::parseQuery($data, $with, $cache); return $query->select($data); } @@ -1279,14 +1286,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess public function __call($method, $args) { + $query = $this->db(); + // 全局作用域 + if (method_exists($this, 'base')) { + call_user_func_array('static::base', [ & $query]); + } if (method_exists($this, 'scope' . $method)) { // 动态调用命名范围 $method = 'scope' . $method; - array_unshift($args, $this->db()); + array_unshift($args, $query); call_user_func_array([$this, $method], $args); return $this; } else { - return call_user_func_array([$this->db(), $method], $args); + return call_user_func_array([$query, $method], $args); } } @@ -1297,6 +1309,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess self::$links[$model] = (new static())->db(); } $query = self::$links[$model]; + // 全局作用域 + if (method_exists($model, 'base')) { + call_user_func_array('static::base', [ & $query]); + } return call_user_func_array([$query, $method], $params); } diff --git a/core/library/think/Route.php b/core/library/think/Route.php index 966e569e..2ada5acf 100644 --- a/core/library/think/Route.php +++ b/core/library/think/Route.php @@ -1204,9 +1204,9 @@ class Route foreach ($matches[1] as $name) { if (strpos($name, '?')) { $name = substr($name, 0, -1); - $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '') . '?)'; + $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . '?)'; } else { - $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '') . ')'; + $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')'; } $value[] = $name; } diff --git a/core/library/think/Url.php b/core/library/think/Url.php index fcfd8b48..2d2acb82 100644 --- a/core/library/think/Url.php +++ b/core/library/think/Url.php @@ -19,6 +19,9 @@ use think\Route; class Url { + // 生成URL地址的root + protected static $root; + /** * URL生成 支持路由反射 * @param string $url URL表达式, @@ -129,7 +132,7 @@ class Url // 检测域名 $domain = self::parseDomain($url, $domain); // URL组装 - $url = $domain . Request::instance()->root() . '/' . ltrim($url, '/'); + $url = $domain . (self::$root ?: Request::instance()->root()) . '/' . ltrim($url, '/'); return $url; } @@ -354,4 +357,11 @@ class Url { Cache::rm('think_route_map'); } -} \ No newline at end of file + + // 指定当前生成URL地址的root + public static function root($root) + { + self::$root = $root; + Request::instance()->root($root); + } +} diff --git a/core/library/think/cache/driver/Memcache.php b/core/library/think/cache/driver/Memcache.php index 62f98bce..aacba930 100644 --- a/core/library/think/cache/driver/Memcache.php +++ b/core/library/think/cache/driver/Memcache.php @@ -109,6 +109,7 @@ class Memcache */ public function inc($name, $step = 1) { + $name = $this->options['prefix'] . $name; return $this->handler->increment($name, $step); } @@ -121,7 +122,14 @@ class Memcache */ public function dec($name, $step = 1) { - return $this->handler->decrement($name, $step); + $name = $this->options['prefix'] . $name; + $value = $this->handler->get($name) - $step; + $res = $this->handler->set($name, $value); + if (!$res) { + return false; + } else { + return $value; + } } /** diff --git a/core/library/think/cache/driver/Memcached.php b/core/library/think/cache/driver/Memcached.php index 2eec2ea2..f260fec6 100644 --- a/core/library/think/cache/driver/Memcached.php +++ b/core/library/think/cache/driver/Memcached.php @@ -115,6 +115,7 @@ class Memcached */ public function inc($name, $step = 1) { + $name = $this->options['prefix'] . $name; return $this->handler->increment($name, $step); } @@ -127,7 +128,14 @@ class Memcached */ public function dec($name, $step = 1) { - return $this->handler->decrement($name, $step); + $name = $this->options['prefix'] . $name; + $value = $this->handler->get($name) - $step; + $res = $this->handler->set($name, $value); + if (!$res) { + return false; + } else { + return $value; + } } /** diff --git a/core/library/think/cache/driver/Redis.php b/core/library/think/cache/driver/Redis.php index 99f9583d..4caa3fb8 100644 --- a/core/library/think/cache/driver/Redis.php +++ b/core/library/think/cache/driver/Redis.php @@ -115,6 +115,7 @@ class Redis */ public function inc($name, $step = 1) { + $name = $this->options['prefix'] . $name; return $this->handler->incrby($name, $step); } @@ -127,6 +128,7 @@ class Redis */ public function dec($name, $step = 1) { + $name = $this->options['prefix'] . $name; return $this->handler->decrby($name, $step); } diff --git a/core/library/think/cache/driver/Wincache.php b/core/library/think/cache/driver/Wincache.php index e8e79b47..77fb74a4 100644 --- a/core/library/think/cache/driver/Wincache.php +++ b/core/library/think/cache/driver/Wincache.php @@ -94,6 +94,7 @@ class Wincache */ public function inc($name, $step = 1) { + $name = $this->options['prefix'] . $name; return wincache_ucache_inc($name, $step); } @@ -106,6 +107,7 @@ class Wincache */ public function dec($name, $step = 1) { + $name = $this->options['prefix'] . $name; return wincache_ucache_dec($name, $step); } diff --git a/core/library/think/cache/driver/Xcache.php b/core/library/think/cache/driver/Xcache.php index e58e5102..e012112f 100644 --- a/core/library/think/cache/driver/Xcache.php +++ b/core/library/think/cache/driver/Xcache.php @@ -94,6 +94,7 @@ class Xcache */ public function inc($name, $step = 1) { + $name = $this->options['prefix'] . $name; return xcache_inc($name, $step); } @@ -106,6 +107,7 @@ class Xcache */ public function dec($name, $step = 1) { + $name = $this->options['prefix'] . $name; return xcache_dec($name, $step); } diff --git a/core/library/think/db/Connection.php b/core/library/think/db/Connection.php index 6712e0d9..c4e09e6d 100644 --- a/core/library/think/db/Connection.php +++ b/core/library/think/db/Connection.php @@ -68,45 +68,47 @@ abstract class Connection // 数据库连接参数配置 protected $config = [ // 数据库类型 - 'type' => '', + 'type' => '', // 服务器地址 - 'hostname' => '', + 'hostname' => '', // 数据库名 - 'database' => '', + 'database' => '', // 用户名 - 'username' => '', + 'username' => '', // 密码 - 'password' => '', + 'password' => '', // 端口 - 'hostport' => '', + 'hostport' => '', // 连接dsn - 'dsn' => '', + 'dsn' => '', // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库编码默认采用utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 数据库表前缀 - 'prefix' => '', + 'prefix' => '', // 数据库调试模式 - 'debug' => false, + 'debug' => false, // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 是否严格检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 数据集返回类型 - 'resultset_type' => 'array', + 'resultset_type' => 'array', // 自动写入时间戳字段 - 'auto_timestamp' => false, + 'auto_timestamp' => false, // 是否需要进行SQL性能分析 - 'sql_explain' => false, + 'sql_explain' => false, // Builder类 - 'builder' => '', + 'builder' => '', + // 软删除字段 + 'soft_delete_field' => '', ]; // PDO连接参数 diff --git a/core/library/think/db/Query.php b/core/library/think/db/Query.php index d0aa4f1f..816d8d48 100644 --- a/core/library/think/db/Query.php +++ b/core/library/think/db/Query.php @@ -566,7 +566,11 @@ class Query $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); $step = $this->lazyWrite('inc', $guid, $step, $lazyTime); if (false === $step) { - return true; // 等待下次写入 + // 清空查询条件 + $this->options = []; + return true; + } else { + return $this->setField($field, $step); } } return $this->setField($field, ['exp', $field . '+' . $step]); @@ -593,7 +597,11 @@ class Query $guid = md5($this->getTable() . '_' . $field . '_' . serialize($condition)); $step = $this->lazyWrite('dec', $guid, $step, $lazyTime); if (false === $step) { - return true; // 等待下次写入 + // 清空查询条件 + $this->options = []; + return true; + } else { + return $this->setField($field, $step); } } return $this->setField($field, ['exp', $field . '-' . $step]); diff --git a/core/library/traits/model/SoftDelete.php b/core/library/traits/model/SoftDelete.php new file mode 100644 index 00000000..f580243a --- /dev/null +++ b/core/library/traits/model/SoftDelete.php @@ -0,0 +1,111 @@ +db(); + } + + /** + * 只查询软删除数据 + * @access public + * @return \think\db\Query + */ + public static function onlyTrashed() + { + $model = new static(); + return $model->db()->where(static::$deleteTime, '>', 0); + } + + /** + * 删除当前的记录 + * @access public + * @param bool $force 是否强制删除 + * @return integer + */ + public function delete($force = false) + { + if (false === $this->trigger('before_delete', $this)) { + return false; + } + + if (static::$deleteTime && !$force) { + // 软删除 + $name = static::$deleteTime; + $this->change[] = $name; + $this->data[$name] = $this->autoWriteTimestamp($name); + $result = $this->isUpdate()->save(); + } else { + $result = $this->db()->delete($this->data); + } + + $this->trigger('after_delete', $this); + return $result; + } + + /** + * 删除记录 + * @access public + * @param mixed $data 主键列表 支持闭包查询条件 + * @param bool $force 是否强制删除 + * @return integer 成功删除的记录数 + */ + public static function destroy($data, $force = false) + { + $model = new static(); + $query = $model->db(); + if (is_array($data) && key($data) !== 0) { + $query->where($data); + $data = null; + } elseif ($data instanceof \Closure) { + call_user_func_array($data, [ & $query]); + $data = null; + } + $resultSet = $query->select($data); + $count = 0; + if ($resultSet) { + foreach ($resultSet as $data) { + $result = $data->delete($force); + $count += $result; + } + } + return $count; + } + + /** + * 恢复被软删除的记录 + * @access public + * @return integer + */ + public function restore() + { + if (static::$deleteTime) { + // 恢复删除 + $this->setAttr(static::$deleteTime, 0); + return $this->isUpdate()->save(); + } + return false; + } + + /** + * 查询默认不包含软删除数据 + * @access protected + * @return void + */ + protected static function base($query) + { + if (static::$deleteTime) { + $query->where(static::$deleteTime, 0); + } + } + +}