thinkphp框架升级

This commit is contained in:
devil_gong
2019-11-11 14:37:00 +08:00
parent 27540686b3
commit 820030d987
55 changed files with 844 additions and 341 deletions

View File

@ -26,7 +26,7 @@
"require-dev": {
"phpunit/phpunit": "^5.0|^6.0",
"johnkary/phpunit-speedtrap": "^1.0",
"mikey179/vfsStream": "~1.6",
"mikey179/vfsstream": "~1.6",
"phploc/phploc": "2.*",
"sebastian/phpcpd": "2.*",
"squizlabs/php_codesniffer": "2.*",

View File

@ -313,9 +313,9 @@ if (!function_exists('download')) {
* @param integer $expire 有效期(秒)
* @return \think\response\Download
*/
function download($filename, $name = '', $content = false, $expire = 360, $openinBrower = false)
function download($filename, $name = '', $content = false, $expire = 360, $openinBrowser = false)
{
return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire)->openinBrower($openinBrower);
return Response::create($filename, 'download')->name($name)->isContent($content)->expire($expire)->openinBrowser($openinBrowser);
}
}
@ -533,7 +533,7 @@ if (!function_exists('response')) {
* @param string $type
* @return Response
*/
function response($data = [], $code = 200, $header = [], $type = 'html')
function response($data = '', $code = 200, $header = [], $type = 'html')
{
return Response::create($data, $type, $code, $header);
}
@ -686,7 +686,13 @@ if (!function_exists('widget')) {
*/
function widget($name, $data = [])
{
return app()->action($name, $data, 'widget');
$result = app()->action($name, $data, 'widget');
if (is_object($result)) {
$result = $result->getContent();
}
return $result;
}
}

View File

@ -20,7 +20,7 @@ use think\route\Dispatch;
*/
class App extends Container
{
const VERSION = '5.1.32 LTS';
const VERSION = '5.1.38 LTS';
/**
* 当前模块路径
@ -179,6 +179,11 @@ class App extends Container
$this->instance('app', $this);
// 加载环境变量配置文件
if (is_file($this->rootPath . '.env')) {
$this->env->load($this->rootPath . '.env');
}
$this->configExt = $this->env->get('config_ext', '.php');
// 加载惯例配置文件
@ -196,11 +201,6 @@ class App extends Container
'vendor_path' => $this->rootPath . 'vendor' . DIRECTORY_SEPARATOR,
]);
// 加载环境变量配置文件
if (is_file($this->rootPath . '.env')) {
$this->env->load($this->rootPath . '.env');
}
$this->namespace = $this->env->get('app_namespace', $this->namespace);
$this->env->set('app_namespace', $this->namespace);

View File

@ -353,7 +353,7 @@ class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSeria
$result = isset($data[$field]) ? $data[$field] : null;
}
switch ($operator) {
switch (strtolower($operator)) {
case '===':
return $result === $value;
case '!==':

View File

@ -487,12 +487,17 @@ class Console
public function getNamespaces()
{
$namespaces = [];
foreach ($this->commands as $command) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
foreach ($this->commands as $name => $command) {
if (is_string($command)) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($name));
} else {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($command->getName()));
foreach ($command->getAliases() as $alias) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
foreach ($command->getAliases() as $alias) {
$namespaces = array_merge($namespaces, $this->extractAllNamespaces($alias));
}
}
}
return array_values(array_unique(array_filter($namespaces)));

View File

@ -158,7 +158,7 @@ class Controller
*/
protected function fetch($template = '', $vars = [], $config = [])
{
return $this->view->fetch($template, $vars, $config);
return Response::create($template, 'view')->assign($vars)->config($config);
}
/**
@ -171,7 +171,7 @@ class Controller
*/
protected function display($content = '', $vars = [], $config = [])
{
return $this->view->display($content, $vars, $config);
return Response::create($content, 'view')->assign($vars)->config($config)->isContent(true);
}
/**

View File

@ -300,7 +300,7 @@ class File extends SplFileObject
*/
public function checkSize($size)
{
if ($this->getSize() > $size) {
if ($this->getSize() > (int) $size) {
$this->error = 'filesize not match';
return false;
}

View File

@ -127,8 +127,10 @@ class Log implements LoggerInterface
}
if (PHP_SAPI == 'cli') {
// 命令行日志实时写入
$this->write($msg, $type, true);
if (empty($this->config['level']) || in_array($type, $this->config['level'])) {
// 命令行日志实时写入
$this->write($msg, $type, true);
}
} else {
$this->log[$type][] = $msg;
}

View File

@ -18,6 +18,33 @@ use think\db\Query;
* Class Model
* @package think
* @mixin Query
* @method Query where(mixed $field, string $op = null, mixed $condition = null) static 查询条件
* @method Query whereRaw(string $where, array $bind = []) static 表达式查询
* @method Query whereExp(string $field, string $condition, array $bind = []) static 字段表达式查询
* @method Query when(mixed $condition, mixed $query, mixed $otherwise = null) static 条件查询
* @method Query join(mixed $join, mixed $condition = null, string $type = 'INNER') static JOIN查询
* @method Query view(mixed $join, mixed $field = null, mixed $on = null, string $type = 'INNER') static 视图查询
* @method Query with(mixed $with) static 关联预载入
* @method Query count(string $field) static Count统计查询
* @method Query min(string $field) static Min统计查询
* @method Query max(string $field) static Max统计查询
* @method Query sum(string $field) static SUM统计查询
* @method Query avg(string $field) static Avg统计查询
* @method Query field(mixed $field, boolean $except = false) static 指定查询字段
* @method Query fieldRaw(string $field, array $bind = []) static 指定查询字段
* @method Query union(mixed $union, boolean $all = false) static UNION查询
* @method Query limit(mixed $offset, integer $length = null) static 查询LIMIT
* @method Query order(mixed $field, string $order = null) static 查询ORDER
* @method Query orderRaw(string $field, array $bind = []) static 查询ORDER
* @method Query cache(mixed $key = null , integer $expire = null) static 设置查询缓存
* @method mixed value(string $field) static 获取某个字段的值
* @method array column(string $field, string $key = '') static 获取某个列的值
* @method mixed find(mixed $data = null) static 查询单个记录
* @method mixed select(mixed $data = null) static 查询多个记录
* @method mixed get(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录 支持关联预载入
* @method mixed getOrFail(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录 不存在则抛出异常
* @method mixed findOrEmpty(mixed $data = null,mixed $with =[],bool $cache= false) static 查询单个记录 不存在则返回空模型
* @method mixed all(mixed $data = null,mixed $with =[],bool $cache= false) static 查询多个记录 支持关联预载入
* @method \think\Model withAttr(array $name,\Closure $closure) 动态定义获取器
*/
abstract class Model implements \JsonSerializable, \ArrayAccess
@ -410,6 +437,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return $this->exists;
}
/**
* 判断模型是否为空
* @access public
* @return bool
*/
public function isEmpty()
{
return empty($this->data);
}
/**
* 保存当前数据对象
* @access public
@ -534,7 +571,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$this->autoRelationUpdate();
}
return false;
return true;
} elseif ($this->autoWriteTimestamp && $this->updateTime && !isset($data[$this->updateTime])) {
// 自动写入更新时间
$data[$this->updateTime] = $this->autoWriteTimestamp($this->updateTime);
@ -743,12 +780,19 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 删除条件
$pk = $this->getPk();
$where = [];
if (is_string($pk) && isset($this->data[$pk])) {
$where[] = [$pk, '=', $this->data[$pk]];
} elseif (!empty($this->updateWhere)) {
$where = $this->updateWhere;
} else {
$where = null;
} elseif (is_array($pk)) {
foreach ($pk as $field) {
if (isset($this->data[$field])) {
$where[] = [$field, '=', $this->data[$field]];
}
}
}
if (empty($where)) {
$where = empty($this->updateWhere) ? null : $this->updateWhere;
}
return $where;

View File

@ -682,6 +682,7 @@ class Request
// 判断URL里面是否有兼容模式参数
$pathinfo = $_GET[$this->config['var_pathinfo']];
unset($_GET[$this->config['var_pathinfo']]);
unset($this->get[$this->config['var_pathinfo']]);
} elseif ($this->isCli()) {
// CLI模式下 index.php module/controller/action/params/...
$pathinfo = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
@ -702,6 +703,10 @@ class Request
}
}
if (!empty($pathinfo)) {
unset($this->get[$pathinfo], $this->request[$pathinfo]);
}
$this->pathinfo = empty($pathinfo) || '/' == $pathinfo ? '' : ltrim($pathinfo, '/');
}
@ -809,9 +814,14 @@ class Request
return $this->server('REQUEST_METHOD') ?: 'GET';
} elseif (!$this->method) {
if (isset($_POST[$this->config['var_method']])) {
$this->method = strtoupper($_POST[$this->config['var_method']]);
$method = strtolower($this->method);
$this->{$method} = $_POST;
$method = strtolower($_POST[$this->config['var_method']]);
if (in_array($method, ['get', 'post', 'put', 'patch', 'delete'])) {
$this->method = strtoupper($method);
$this->{$method} = $_POST;
} else {
$this->method = 'POST';
}
unset($_POST[$this->config['var_method']]);
} elseif ($this->server('HTTP_X_HTTP_METHOD_OVERRIDE')) {
$this->method = strtoupper($this->server('HTTP_X_HTTP_METHOD_OVERRIDE'));
} else {
@ -1034,7 +1044,7 @@ class Request
protected function getInputData($content)
{
if (false !== strpos($this->contentType(), 'application/json') || 0 === strpos($content, '{"')) {
if (false !== strpos($this->contentType(), 'json')) {
return (array) json_decode($content, true);
} elseif (strpos($content, '=')) {
parse_str($content, $data);
@ -1320,7 +1330,8 @@ class Request
* @param array $data 数据源
* @return void
*/
public function arrayReset(array &$data) {
public function arrayReset(array &$data)
{
foreach ($data as &$value) {
if (is_array($value)) {
$this->arrayReset($value);
@ -1523,7 +1534,7 @@ class Request
*/
public function has($name, $type = 'param', $checkEmpty = false)
{
if (!in_array($type, ['param', 'get', 'post', 'request', 'put', 'file', 'session', 'cookie', 'env', 'header', 'route'])) {
if (!in_array($type, ['param', 'get', 'post', 'request', 'put', 'patch', 'file', 'session', 'cookie', 'env', 'header', 'route'])) {
return false;
}
@ -1625,6 +1636,16 @@ class Request
return false;
}
/**
* 当前是否JSON请求
* @access public
* @return bool
*/
public function isJson()
{
return false !== strpos($this->type(), 'json');
}
/**
* 当前是否Ajax请求
* @access public

View File

@ -408,7 +408,7 @@ class Route
$result = $this->bind[$domain];
} elseif (isset($name) && isset($this->bind[$name])) {
$result = $this->bind[$name];
} elseif (isset($this->bind['*'])) {
} elseif (!empty($subDomain) && isset($this->bind['*'])) {
$result = $this->bind['*'];
} else {
$result = null;

View File

@ -1311,7 +1311,7 @@ class Template
public function __debugInfo()
{
$data = get_object_vars($this);
unset($data['app'], $data['storege']);
unset($data['app'], $data['storage']);
return $data;
}

View File

@ -130,7 +130,9 @@ class Url
// 匹配路由命名标识
$url = $match[0];
$domain = $match[1];
if ($domain) {
$domain = $match[1];
}
if (!is_null($match[2])) {
$suffix = $match[2];
@ -347,6 +349,7 @@ class Url
// 匹配路由地址
public function getRuleUrl($rule, &$vars = [], $allowDomain = '')
{
$port = $this->app['request']->port();
foreach ($rule as $item) {
list($url, $pattern, $domain, $suffix, $method) = $item;
@ -354,8 +357,8 @@ class Url
continue;
}
if (!in_array($this->app['request']->port(), [80, 443])) {
$domain .= ':' . $this->app['request']->port();
if ($port && !in_array($port, [80, 443])) {
$domain .= ':' . $port;
}
if (empty($pattern)) {

View File

@ -131,7 +131,7 @@ class Validate
* 内置正则验证规则
* @var array
*/
protected $regex = [
protected $defaultRegex = [
'alphaDash' => '/^[A-Za-z0-9\-\_]+$/',
'chs' => '/^[\x{4e00}-\x{9fa5}]+$/u',
'chsAlpha' => '/^[\x{4e00}-\x{9fa5}a-zA-Z]+$/u',
@ -178,6 +178,12 @@ class Validate
*/
protected $append = [];
/**
* 验证正则定义
* @var array
*/
protected $regex = [];
/**
* 架构函数
* @access public
@ -513,7 +519,7 @@ class Validate
if (isset($this->append[$field])) {
// 追加额外的验证规则
$rules = array_merge($rules, $this->append[$field]);
$rules = array_unique(array_merge($rules, $this->append[$field]), SORT_REGULAR);
}
$i = 0;
@ -561,10 +567,11 @@ class Validate
} elseif (true !== $result) {
// 返回自定义错误信息
if (is_string($result) && false !== strpos($result, ':')) {
$result = str_replace(
[':attribute', ':rule'],
[$title, (string) $rule],
$result);
$result = str_replace(':attribute', $title, $result);
if (strpos($result, ':rule') && is_scalar($rule)) {
$result = str_replace(':rule', (string) $rule, $result);
}
}
return $result;
@ -927,8 +934,8 @@ class Validate
if (isset($rule[2])) {
$imageType = strtolower($rule[2]);
if ('jpeg' == $imageType) {
$imageType = 'jpg';
if ('jpg' == $imageType) {
$imageType = 'jpeg';
}
if (image_type_to_extension($type, false) != $imageType) {
@ -1006,6 +1013,8 @@ class Validate
$map[] = [$key, '=', $data[$key]];
}
}
} elseif (strpos($key, '=')) {
parse_str($key, $map);
} elseif (isset($data[$field])) {
$map[] = [$key, '=', $data[$field]];
} else {
@ -1360,6 +1369,8 @@ class Validate
{
if (isset($this->regex[$rule])) {
$rule = $this->regex[$rule];
} elseif (isset($this->defaultRegex[$rule])) {
$rule = $this->defaultRegex[$rule];
}
if (0 !== strpos($rule, '/') && !preg_match('/\/[imsU]{0,4}$/', $rule)) {
@ -1461,13 +1472,17 @@ class Validate
$msg = $title . $lang->get('not conform to the rules');
}
if (is_string($msg) && 0 === strpos($msg, '{%')) {
if (!is_string($msg)) {
return $msg;
}
if (0 === strpos($msg, '{%')) {
$msg = $lang->get(substr($msg, 2, -1));
} elseif ($lang->has($msg)) {
$msg = $lang->get($msg);
}
if (is_string($msg) && is_scalar($rule) && false !== strpos($msg, ':')) {
if (is_scalar($rule) && false !== strpos($msg, ':')) {
// 变量替换
if (is_string($rule) && strpos($rule, ',')) {
$array = array_pad(explode(',', $rule), 3, '');
@ -1475,9 +1490,12 @@ class Validate
$array = array_pad([], 3, '');
}
$msg = str_replace(
[':attribute', ':rule', ':1', ':2', ':3'],
[$title, (string) $rule, $array[0], $array[1], $array[2]],
[':attribute', ':1', ':2', ':3'],
[$title, $array[0], $array[1], $array[2]],
$msg);
if (strpos($msg, ':rule')) {
$msg = str_replace(':rule', (string) $rule, $msg);
}
}
return $msg;
@ -1496,12 +1514,12 @@ class Validate
$scene = $this->currentScene;
}
$this->only = $this->append = $this->remove = [];
if (empty($scene)) {
return;
}
$this->only = $this->append = $this->remove = [];
if (method_exists($this, 'scene' . $scene)) {
call_user_func([$this, 'scene' . $scene]);
} elseif (isset($this->scene[$scene])) {

View File

@ -160,7 +160,10 @@ class View
*/
public function filter($filter)
{
$this->filter = $filter;
if ($filter) {
$this->filter = $filter;
}
return $this;
}

View File

@ -249,7 +249,6 @@ abstract class Driver
{
if ($this->tag) {
$key = $this->getTagkey($this->tag);
$prev = $this->tag;
$this->tag = null;
if ($this->has($key)) {
@ -266,7 +265,6 @@ abstract class Driver
}
$this->set($key, $value, 0);
$this->tag = $prev;
}
}
@ -360,4 +358,9 @@ abstract class Driver
{
return $this->writeTimes;
}
public function __call($method, $args)
{
return call_user_func_array([$this->handler, $method], $args);
}
}

View File

@ -278,11 +278,13 @@ class File extends Driver
if (is_dir($path)) {
$matches = glob($path . DIRECTORY_SEPARATOR . '*.php');
if (is_array($matches)) {
array_map('unlink', $matches);
array_map(function ($v) {
$this->unlink($v);
}, $matches);
}
rmdir($path);
} else {
unlink($path);
$this->unlink($path);
}
}

View File

@ -67,7 +67,7 @@ class Memcached extends Driver
}
$this->handler->addServers($servers);
$this->handler->setOption(\Memcached::OPT_COMPRESSION, false);
if ('' != $this->options['username']) {
$this->handler->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
$this->handler->setSaslAuthData($this->options['username'], $this->options['password']);
@ -232,7 +232,7 @@ class Memcached extends Driver
$this->handler->delete($tagName);
}
if (!$this->handler->has($tagName)) {
if (!$this->has($tagName)) {
$this->handler->set($tagName, '');
}
@ -255,7 +255,7 @@ class Memcached extends Driver
if ($this->tag) {
$tagName = $this->getTagKey($this->tag);
if ($this->handler->has($tagName)) {
if ($this->has($tagName)) {
$this->handler->append($tagName, ',' . $name);
} else {
$this->handler->set($tagName, $name);

View File

@ -313,9 +313,10 @@ abstract class Builder
// 使用闭包查询
$newQuery = $query->newQuery()->setConnection($this->connection);
$value($newQuery);
$whereClause = $this->buildWhere($query, $newQuery->getOptions('where'));
$whereClause = $this->buildWhere($newQuery, $newQuery->getOptions('where'));
if (!empty($whereClause)) {
$query->bind($newQuery->getBind(false));
$str[] = ' ' . $logic . ' ( ' . $whereClause . ' )';
}
} elseif (is_array($field)) {
@ -407,7 +408,7 @@ abstract class Builder
$jsonType = $query->getJsonFieldType($field);
$bindType = $this->connection->getFieldBindType($jsonType);
} else {
$bindType = isset($binds[$field]) ? $binds[$field] : PDO::PARAM_STR;
$bindType = isset($binds[$field]) && 'LIKE' != $exp ? $binds[$field] : PDO::PARAM_STR;
}
if (is_scalar($value) && !in_array($exp, ['EXP', 'NOT NULL', 'NULL', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN']) && strpos($exp, 'TIME') === false) {
@ -450,11 +451,11 @@ abstract class Builder
// 模糊匹配
if (is_array($value)) {
foreach ($value as $item) {
$name = $query->bind($item, $bindType);
$name = $query->bind($item, PDO::PARAM_STR);
$array[] = $key . ' ' . $exp . ' :' . $name;
}
$whereStr = '(' . implode($array, ' ' . strtoupper($logic) . ' ') . ')';
$whereStr = '(' . implode(' ' . strtoupper($logic) . ' ', $array) . ')';
} else {
$whereStr = $key . ' ' . $exp . ' ' . $value;
}
@ -604,6 +605,10 @@ abstract class Builder
$value = $this->parseClosure($query, $value);
}
if ('=' == $exp && is_null($value)) {
return $key . ' IS NULL';
}
return $key . ' ' . $exp . ' ' . $value;
}
@ -647,9 +652,10 @@ abstract class Builder
// IN 查询
if ($value instanceof \Closure) {
$value = $this->parseClosure($query, $value, false);
} elseif ($value instanceof Expression) {
$value = $value->getValue();
} else {
$value = array_unique(is_array($value) ? $value : explode(',', $value));
$array = [];
foreach ($value as $k => $v) {
@ -657,9 +663,12 @@ abstract class Builder
$array[] = ':' . $name;
}
$zone = implode(',', $array);
$value = empty($zone) ? "''" : $zone;
if (count($array) == 1) {
return $key . ('IN' == $exp ? ' = ' : ' <> ') . $array[0];
} else {
$zone = implode(',', $array);
$value = empty($zone) ? "''" : $zone;
}
}
return $key . ' ' . $exp . ' (' . $value . ')';

View File

@ -1246,22 +1246,14 @@ abstract class Connection
* @access public
* @param Query $query 查询对象
* @param string $field 字段名
* @param bool $default 默认值
* @param mixed $default 默认值
* @param bool $one 是否返回一个值
* @return mixed
*/
public function value(Query $query, $field, $default = null)
public function value(Query $query, $field, $default = null, $one = true)
{
$options = $query->getOptions();
if (empty($options['fetch_sql']) && !empty($options['cache'])) {
$cache = $options['cache'];
$result = $this->getCacheData($query, $cache, null, $key);
if (false !== $result) {
return $result;
}
}
if (isset($options['field'])) {
$query->removeOption('field');
}
@ -1271,7 +1263,19 @@ abstract class Connection
}
$query->setOption('field', $field);
$query->setOption('limit', 1);
if (empty($options['fetch_sql']) && !empty($options['cache'])) {
$cache = $options['cache'];
$result = $this->getCacheData($query, $cache, null, $key);
if (false !== $result) {
return $result;
}
}
if ($one) {
$query->setOption('limit', 1);
}
// 生成查询SQL
$sql = $this->builder->select($query);
@ -1320,7 +1324,7 @@ abstract class Connection
$field = $aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $this->builder->parseKey($query, $field, true) . ') AS tp_' . strtolower($aggregate);
return $this->value($query, $field, 0);
return $this->value($query, $field, 0, false);
}
/**
@ -1335,16 +1339,6 @@ abstract class Connection
{
$options = $query->getOptions();
if (empty($options['fetch_sql']) && !empty($options['cache'])) {
// 判断查询缓存
$cache = $options['cache'];
$result = $this->getCacheData($query, $cache, null, $guid);
if (false !== $result) {
return $result;
}
}
if (isset($options['field'])) {
$query->removeOption('field');
}
@ -1362,6 +1356,16 @@ abstract class Connection
$query->setOption('field', $field);
if (empty($options['fetch_sql']) && !empty($options['cache'])) {
// 判断查询缓存
$cache = $options['cache'];
$result = $this->getCacheData($query, $cache, null, $guid);
if (false !== $result) {
return $result;
}
}
// 生成查询SQL
$sql = $this->builder->select($query);
@ -1463,9 +1467,7 @@ abstract class Connection
$value = is_array($val) ? $val[0] : $val;
$type = is_array($val) ? $val[1] : PDO::PARAM_STR;
if (self::PARAM_FLOAT == $type) {
$value = (float) $value;
} elseif (PDO::PARAM_STR == $type) {
if ((self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) && is_string($value)) {
$value = '\'' . addslashes($value) . '\'';
} elseif (PDO::PARAM_INT == $type && '' === $value) {
$value = 0;
@ -1499,7 +1501,7 @@ abstract class Connection
if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
$val[0] = 0;
} elseif (self::PARAM_FLOAT == $val[1]) {
$val[0] = (float) $val[0];
$val[0] = is_string($val[0]) ? (float) $val[0] : $val[0];
$val[1] = PDO::PARAM_STR;
}

View File

@ -95,14 +95,14 @@ class Query
* @var array
*/
protected $timeRule = [
'today' => ['today', 'tomorrow'],
'yesterday' => ['yesterday', 'today'],
'week' => ['this week 00:00:00', 'next week 00:00:00'],
'last week' => ['last week 00:00:00', 'this week 00:00:00'],
'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00'],
'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00'],
'year' => ['this year 1/1', 'next year 1/1'],
'last year' => ['last year 1/1', 'this year 1/1'],
'today' => ['today', 'tomorrow -1second'],
'yesterday' => ['yesterday', 'today -1second'],
'week' => ['this week 00:00:00', 'next week 00:00:00 -1second'],
'last week' => ['last week 00:00:00', 'this week 00:00:00 -1second'],
'month' => ['first Day of this month 00:00:00', 'first Day of next month 00:00:00 -1second'],
'last month' => ['first Day of last month 00:00:00', 'first Day of this month 00:00:00 -1second'],
'year' => ['this year 1/1', 'next year 1/1 -1second'],
'last year' => ['last year 1/1', 'this year 1/1 -1second'],
];
/**
@ -133,7 +133,27 @@ class Query
*/
public function newQuery()
{
return new static($this->connection);
$query = new static($this->connection);
if ($this->model) {
$query->model($this->model);
}
if (isset($this->options['table'])) {
$query->table($this->options['table']);
} else {
$query->name($this->name);
}
if (isset($this->options['json'])) {
$query->json($this->options['json'], $this->options['json_assoc']);
}
if (isset($this->options['field_type'])) {
$query->setJsonFieldType($this->options['field_type']);
}
return $query;
}
/**
@ -611,9 +631,9 @@ class Query
/**
* 聚合查询
* @access public
* @param string $aggregate 聚合方法
* @param string $field 字段名
* @param bool $force 强制转为数字类型
* @param string $aggregate 聚合方法
* @param string|Expression $field 字段名
* @param bool $force 强制转为数字类型
* @return mixed
*/
public function aggregate($aggregate, $field, $force = false)
@ -634,7 +654,7 @@ class Query
/**
* COUNT查询
* @access public
* @param string $field 字段名
* @param string|Expression $field 字段名
* @return float|string
*/
public function count($field = '*')
@ -664,7 +684,7 @@ class Query
/**
* SUM查询
* @access public
* @param string $field 字段名
* @param string|Expression $field 字段名
* @return float
*/
public function sum($field)
@ -675,8 +695,8 @@ class Query
/**
* MIN查询
* @access public
* @param string $field 字段名
* @param bool $force 强制转为数字类型
* @param string|Expression $field 字段名
* @param bool $force 强制转为数字类型
* @return mixed
*/
public function min($field, $force = true)
@ -687,8 +707,8 @@ class Query
/**
* MAX查询
* @access public
* @param string $field 字段名
* @param bool $force 强制转为数字类型
* @param string|Expression $field 字段名
* @param bool $force 强制转为数字类型
* @return mixed
*/
public function max($field, $force = true)
@ -699,7 +719,7 @@ class Query
/**
* AVG查询
* @access public
* @param string $field 字段名
* @param string|Expression $field 字段名
* @return float
*/
public function avg($field)
@ -834,9 +854,10 @@ class Query
* @param mixed $join 关联的表名
* @param mixed $condition 条件
* @param string $type JOIN类型
* @param array $bind 参数绑定
* @return $this
*/
public function join($join, $condition = null, $type = 'INNER')
public function join($join, $condition = null, $type = 'INNER', $bind = [])
{
if (empty($condition)) {
// 如果为组数则循环调用join
@ -847,7 +868,9 @@ class Query
}
} else {
$table = $this->getJoinTable($join);
if ($bind) {
$this->bindParams($condition, $bind);
}
$this->options['join'][] = [$table, strtoupper($type), $condition];
}
@ -859,9 +882,10 @@ class Query
* @access public
* @param mixed $join 关联的表名
* @param mixed $condition 条件
* @param array $bind 参数绑定
* @return $this
*/
public function leftJoin($join, $condition = null)
public function leftJoin($join, $condition = null, $bind = [])
{
return $this->join($join, $condition, 'LEFT');
}
@ -871,9 +895,10 @@ class Query
* @access public
* @param mixed $join 关联的表名
* @param mixed $condition 条件
* @param array $bind 参数绑定
* @return $this
*/
public function rightJoin($join, $condition = null)
public function rightJoin($join, $condition = null, $bind = [])
{
return $this->join($join, $condition, 'RIGHT');
}
@ -883,9 +908,10 @@ class Query
* @access public
* @param mixed $join 关联的表名
* @param mixed $condition 条件
* @param array $bind 参数绑定
* @return $this
*/
public function fullJoin($join, $condition = null)
public function fullJoin($join, $condition = null, $bind = [])
{
return $this->join($join, $condition, 'FULL');
}
@ -943,6 +969,10 @@ class Query
*/
public function union($union, $all = false)
{
if (empty($union)) {
return $this;
}
$this->options['union']['type'] = $all ? 'UNION ALL' : 'UNION';
if (is_array($union)) {
@ -1502,6 +1532,7 @@ class Query
{
if ($field instanceof $this) {
$this->options['where'] = $field->getOptions('where');
$this->bind($field->getBind(false));
return $this;
}
@ -1517,10 +1548,10 @@ class Query
}
if ($field instanceof Expression) {
return $this->whereRaw($field, is_array($op) ? $op : []);
return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
} elseif ($strict) {
// 使用严格模式查询
$where = [$field, $op, $condition];
$where = [$field, $op, $condition, $logic];
} elseif (is_array($field)) {
// 解析数组批量查询
return $this->parseArrayWhereItems($field, $logic);
@ -1528,7 +1559,7 @@ class Query
$where = $field;
} elseif (is_string($field)) {
if (preg_match('/[,=\<\'\"\(\s]/', $field)) {
return $this->whereRaw($field, $op);
return $this->whereRaw($field, $op, $logic);
} elseif (is_string($op) && strtolower($op) == 'exp') {
$bind = isset($param[2]) && is_array($param[2]) ? $param[2] : null;
return $this->whereExp($field, $condition, $bind, $logic);
@ -1572,7 +1603,7 @@ class Query
// 字段相等查询
$where = [$field, '=', $op];
}
} elseif (in_array(strtoupper($op), ['REGEXP', 'NOT REGEXP', 'EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) {
} elseif (in_array(strtoupper($op), ['EXISTS', 'NOT EXISTS', 'NOTEXISTS'], true)) {
$where = [$field, $op, is_string($condition) ? $this->raw($condition) : $condition];
} else {
$where = $field ? [$field, $op, $condition, isset($param[2]) ? $param[2] : null] : null;
@ -1645,6 +1676,7 @@ class Query
{
if (true === $option) {
$this->options = [];
$this->bind = [];
} elseif (is_string($option) && isset($this->options[$option])) {
unset($this->options[$option]);
}
@ -2186,12 +2218,12 @@ class Query
}
/**
* 设置需要追加输出属性
* 设置需要附加的输出属性
* @access public
* @param array $append 需要追加的属性
* @param array $append 属性列表
* @return $this
*/
public function append(array $append)
public function append(array $append = [])
{
$this->options['append'] = $append;
return $this;
@ -2458,7 +2490,7 @@ class Query
if (is_array($value)) {
$this->bind = array_merge($this->bind, $value);
} else {
$name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_';
$name = $name ?: 'ThinkBind_' . (count($this->bind) + 1) . '_' . mt_rand() . '_';
$this->bind[$name] = [$value, $type];
return $name;
@ -3344,13 +3376,13 @@ class Query
// 输出属性控制
if (!empty($options['visible'])) {
$result->visible($options['visible']);
$result->visible($options['visible'], true);
} elseif (!empty($options['hidden'])) {
$result->hidden($options['hidden']);
$result->hidden($options['hidden'], true);
}
if (!empty($options['append'])) {
$result->append($options['append']);
$result->append($options['append'], true);
}
// 关联查询

View File

@ -62,7 +62,7 @@ class Mysql extends Builder
$bind = $this->connection->getFieldsBind($options['table']);
foreach ($dataSet as $k => $data) {
$data = $this->parseData($query, $data, $allowFields, $bind, '_' . $k);
$data = $this->parseData($query, $data, $allowFields, $bind);
$values[] = '( ' . implode(',', array_values($data)) . ' )';
@ -94,13 +94,17 @@ class Mysql extends Builder
* @param Query $query 查询对象
* @param string $key
* @param string $exp
* @param Expression $value
* @param mixed $value
* @param string $field
* @return string
*/
protected function parseRegexp(Query $query, $key, $exp, Expression $value, $field)
protected function parseRegexp(Query $query, $key, $exp, $value, $field)
{
return $key . ' ' . $exp . ' ' . $value->getValue();
if ($value instanceof Expression) {
$value = $value->getValue();
}
return $key . ' ' . $exp . ' ' . $value;
}
/**
@ -125,7 +129,7 @@ class Mysql extends Builder
// JSON字段支持
list($field, $name) = explode('->', $key, 2);
return 'json_extract(' . $this->parseKey($query, $field, true) . ', \'$.' . str_replace('->', '.', $name) . '\')';
return 'json_extract(' . $this->parseKey($query, $field, true) . ', \'$' . (strpos($name, '[') === 0 ? '' : '.') . str_replace('->', '.', $name) . '\')';
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
list($table, $key) = explode('.', $key, 2);
@ -145,7 +149,7 @@ class Mysql extends Builder
throw new Exception('not support data:' . $key);
}
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
$key = '`' . $key . '`';
}

View File

@ -114,7 +114,7 @@ class Sqlsrv extends Builder
throw new Exception('not support data:' . $key);
}
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {
if ('*' != $key && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) {
$key = '[' . $key . ']';
}

View File

@ -136,7 +136,27 @@ class Mysql extends Connection
*/
protected function getExplain($sql)
{
$pdo = $this->linkID->query("EXPLAIN " . $sql);
$pdo = $this->linkID->prepare("EXPLAIN " . $this->queryStr);
foreach ($this->bind as $key => $val) {
// 占位符
$param = is_int($key) ? $key + 1 : ':' . $key;
if (is_array($val)) {
if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
$val[0] = 0;
} elseif (self::PARAM_FLOAT == $val[1]) {
$val[0] = is_string($val[0]) ? (float) $val[0] : $val[0];
$val[1] = PDO::PARAM_STR;
}
$result = $pdo->bindValue($param, $val[0], $val[1]);
} else {
$result = $pdo->bindValue($param, $val);
}
}
$pdo->execute();
$result = $pdo->fetch(PDO::FETCH_ASSOC);
$result = array_change_key_case($result);

View File

@ -37,6 +37,10 @@ DECLARE
v_sql varchar;
v_rec RECORD;
v_key varchar;
v_conkey smallint[];
v_pk varchar[];
v_len smallint;
v_pos smallint := 1;
BEGIN
SELECT
pg_class.oid INTO v_oid
@ -49,6 +53,31 @@ BEGIN
RETURN;
END IF;
SELECT
pg_constraint.conkey INTO v_conkey
FROM
pg_constraint
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid
INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid
WHERE
pg_class.relname = a_table_name
AND pg_constraint.contype = 'p';
v_len := array_length(v_conkey,1) + 1;
WHILE v_pos < v_len LOOP
SELECT
pg_attribute.attname INTO v_key
FROM pg_constraint
INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid AND pg_attribute.attnum = pg_constraint.conkey [ v_conkey[v_pos] ]
INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid
WHERE pg_class.relname = a_table_name AND pg_constraint.contype = 'p';
v_pk := array_append(v_pk,v_key);
v_pos := v_pos + 1;
END LOOP;
v_sql='
SELECT
pg_attribute.attname AS fields_name,
@ -83,12 +112,19 @@ BEGIN
v_ret.fields_not_null=v_rec.fields_not_null;
v_ret.fields_default=v_rec.fields_default;
v_ret.fields_comment=v_rec.fields_comment;
SELECT constraint_name INTO v_key FROM information_schema.key_column_usage WHERE table_schema=a_schema_name AND table_name=a_table_name AND column_name=v_rec.fields_name;
IF FOUND THEN
v_ret.fields_key_name=v_key;
ELSE
v_ret.fields_key_name='';
END IF;
v_ret.fields_key_name='';
v_len := array_length(v_pk,1) + 1;
v_pos := 1;
WHILE v_pos < v_len LOOP
IF v_rec.fields_name = v_pk[v_pos] THEN
v_ret.fields_key_name=v_pk[v_pos];
EXIT;
END IF;
v_pos := v_pos + 1;
END LOOP;
RETURN NEXT v_ret;
END LOOP;
RETURN ;

View File

@ -26,7 +26,7 @@ class DbException extends Exception
* @param string $sql
* @param int $code
*/
public function __construct($message, array $config, $sql, $code = 10500)
public function __construct($message, array $config = [], $sql = '', $code = 10500)
{
$this->message = $message;
$this->code = $code;

View File

@ -18,7 +18,7 @@ class ValidateException extends \RuntimeException
public function __construct($error, $code = 0)
{
$this->error = $error;
$this->message = is_array($error) ? implode("\n\r", $error) : $error;
$this->message = is_array($error) ? implode(PHP_EOL, $error) : $error;
$this->code = $code;
}

View File

@ -16,11 +16,14 @@ use think\Facade;
/**
* @see \think\Config
* @mixin \think\Config
* @method array load(string $file, string $name = '') static 加载配置文件
* @method bool has(string $name) static 检测配置是否存在
* @method array pull(string $name) static 获取一级配置
* @method array pull(string $name) static 获取一级配置参数
* @method mixed get(string $name,mixed $default = null) static 获取配置参数
* @method mixed set(string $name, mixed $value = null) static 设置配置参数
* @method array reset(string $prefix ='') static 重置配置参数
* @method array set(mixed $name, mixed $value = null) static 设置配置参数
* @method array reset(string $name ='') static 重置配置参数
* @method void remove(string $name = '') static 移除配置
* @method void setYaconf(mixed $yaconf) static 设置开启Yaconf 或者指定配置文件名
*/
class Config extends Facade
{

View File

@ -21,6 +21,7 @@ use think\Facade;
* @method \think\Log record(mixed $msg, string $type = 'info', array $context = []) static 记录日志信息
* @method \think\Log clear() static 清空日志信息
* @method \think\Log key(string $key) static 当前日志记录的授权key
* @method \think\Log close() static 关闭本次请求日志写入
* @method bool check(array $config) static 检查日志写入权限
* @method bool save() static 保存调试信息
* @method void write(mixed $msg, string $type = 'info', bool $force = false) static 实时写入日志信息

View File

@ -19,7 +19,7 @@ use think\App;
class File
{
protected $config = [
'time_format' => ' c ',
'time_format' => 'c',
'single' => false,
'file_size' => 2097152,
'path' => '',
@ -107,7 +107,13 @@ class File
$info['timestamp'] = date($this->config['time_format']);
foreach ($message as $type => $msg) {
$info[$type] = is_array($msg) ? implode("\r\n", $msg) : $msg;
$msg = is_array($msg) ? implode(PHP_EOL, $msg) : $msg;
if (PHP_SAPI == 'cli') {
$info['msg'] = $msg;
$info['type'] = $type;
} else {
$info[$type] = $msg;
}
}
if (PHP_SAPI == 'cli') {
@ -140,13 +146,13 @@ class File
}
}
$cli = PHP_SAPI == 'cli' ? '_cli' : '';
if ($this->config['single']) {
$name = is_string($this->config['single']) ? $this->config['single'] : 'single';
$destination = $this->config['path'] . $name . '.log';
$destination = $this->config['path'] . $name . $cli . '.log';
} else {
$cli = PHP_SAPI == 'cli' ? '_cli' : '';
if ($this->config['max_files']) {
$filename = date('Ymd') . $cli . '.log';
} else {
@ -172,15 +178,13 @@ class File
if ($this->config['single']) {
$name = is_string($this->config['single']) ? $this->config['single'] : 'single';
$name .= '_' . $type;
} elseif ($this->config['max_files']) {
$name = date('Ymd') . '_' . $type . $cli;
$name = date('Ymd');
} else {
$name = date('d') . '_' . $type . $cli;
$name = date('d');
}
return $path . DIRECTORY_SEPARATOR . $name . '.log';
return $path . DIRECTORY_SEPARATOR . $name . '_' . $type . $cli . '.log';
}
/**
@ -208,14 +212,14 @@ class File
protected function parseCliLog($info)
{
if ($this->config['json']) {
$message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
$message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
} else {
$now = $info['timestamp'];
unset($info['timestamp']);
$message = implode("\r\n", $info);
$message = implode(PHP_EOL, $info);
$message = "[{$now}]" . $message . "\r\n";
$message = "[{$now}]" . $message . PHP_EOL;
}
return $message;
@ -238,13 +242,13 @@ class File
if ($this->config['json']) {
$info = $requestInfo + $info;
return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
}
array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
array_unshift($info, "---------------------------------------------------------------" . PHP_EOL . "\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
unset($info['timestamp']);
return implode("\r\n", $info) . "\r\n";
return implode(PHP_EOL, $info) . PHP_EOL;
}
protected function getDebugLog(&$info, $append, $apart)

View File

@ -24,8 +24,10 @@ class Collection extends BaseCollection
*/
public function load($relation)
{
$item = current($this->items);
$item->eagerlyResultSet($this->items, $relation);
if (!$this->isEmpty()) {
$item = current($this->items);
$item->eagerlyResultSet($this->items, $relation);
}
return $this;
}

View File

@ -58,6 +58,16 @@ abstract class Relation
return $this->query->getModel();
}
/**
* 获取当前的关联模型类的实例
* @access public
* @return Query
*/
public function getQuery()
{
return $this->query;
}
/**
* 设置当前关联为自关联
* @access public
@ -129,6 +139,17 @@ abstract class Relation
}
}
/**
* 更新数据
* @access public
* @param array $data 更新数据
* @return integer|string
*/
public function update(array $data = [])
{
return $this->query->update($data);
}
/**
* 删除记录
* @access public

View File

@ -12,6 +12,7 @@
namespace think\model\concern;
use InvalidArgumentException;
use think\db\Expression;
use think\Exception;
use think\Loader;
use think\model\Relation;
@ -326,9 +327,13 @@ trait Attribute
$method = 'set' . Loader::parseName($name, 1) . 'Attr';
if (method_exists($this, $method)) {
$value = $this->$method($value, array_merge($this->data, $data));
$origin = $this->data;
$value = $this->$method($value, array_merge($this->data, $data));
$this->set[$name] = true;
if (is_null($value) && $origin !== $this->data) {
return;
}
} elseif (isset($this->type[$name])) {
// 类型转换
$value = $this->writeTransform($value, $this->type[$name]);
@ -370,9 +375,7 @@ trait Attribute
switch ($type) {
case 'datetime':
case 'date':
$format = !empty($param) ? $param : $this->dateFormat;
$format .= strpos($format, 'u') || false !== strpos($format, '\\') ? '' : '.u';
$value = $this->formatDateTime($format);
$value = $this->formatDateTime('Y-m-d H:i:s.u');
break;
case 'timestamp':
case 'integer':
@ -385,8 +388,7 @@ trait Attribute
'date',
'timestamp',
])) {
$format = strpos($this->dateFormat, 'u') || false !== strpos($this->dateFormat, '\\') ? '' : '.u';
$value = $this->formatDateTime($this->dateFormat . $format);
$value = $this->formatDateTime('Y-m-d H:i:s.u');
} else {
$value = time();
}
@ -407,6 +409,10 @@ trait Attribute
return;
}
if ($value instanceof Expression) {
return $value;
}
if (is_array($type)) {
list($type, $param) = $type;
} elseif (strpos($type, ':')) {
@ -433,9 +439,8 @@ trait Attribute
}
break;
case 'datetime':
$format = !empty($param) ? $param : $this->dateFormat;
$value = is_numeric($value) ? $value : strtotime($value);
$value = $this->formatDateTime($format, $value);
$value = is_numeric($value) ? $value : strtotime($value);
$value = $this->formatDateTime('Y-m-d H:i:s.u', $value);
break;
case 'object':
if (is_object($value)) {

View File

@ -130,34 +130,52 @@ trait Conversion
*/
public function toArray()
{
$item = [];
$visible = [];
$hidden = [];
$item = [];
$hasVisible = false;
foreach ($this->visible as $key => $val) {
if (is_string($val)) {
if (strpos($val, '.')) {
list($relation, $name) = explode('.', $val);
$this->visible[$relation][] = $name;
} else {
$this->visible[$val] = true;
$hasVisible = true;
}
unset($this->visible[$key]);
}
}
foreach ($this->hidden as $key => $val) {
if (is_string($val)) {
if (strpos($val, '.')) {
list($relation, $name) = explode('.', $val);
$this->hidden[$relation][] = $name;
} else {
$this->hidden[$val] = true;
}
unset($this->hidden[$key]);
}
}
// 合并关联数据
$data = array_merge($this->data, $this->relation);
// 过滤属性
if (!empty($this->visible)) {
$array = $this->parseAttr($this->visible, $visible);
$data = array_intersect_key($data, array_flip($array));
} elseif (!empty($this->hidden)) {
$array = $this->parseAttr($this->hidden, $hidden, false);
$data = array_diff_key($data, array_flip($array));
}
foreach ($data as $key => $val) {
if ($val instanceof Model || $val instanceof ModelCollection) {
// 关联模型对象
if (isset($visible[$key])) {
$val->visible($visible[$key]);
} elseif (isset($hidden[$key])) {
$val->hidden($hidden[$key]);
if (isset($this->visible[$key]) && is_array($this->visible[$key])) {
$val->visible($this->visible[$key]);
} elseif (isset($this->hidden[$key]) && is_array($this->hidden[$key])) {
$val->hidden($this->hidden[$key]);
}
// 关联模型对象
$item[$key] = $val->toArray();
} else {
// 模型属性
if (!isset($this->hidden[$key]) || true !== $this->hidden[$key]) {
$item[$key] = $val->toArray();
}
} elseif (isset($this->visible[$key])) {
$item[$key] = $this->getAttr($key);
} elseif (!isset($this->hidden[$key]) && !$hasVisible) {
$item[$key] = $this->getAttr($key);
}
}
@ -171,10 +189,12 @@ trait Conversion
if (!$relation) {
$relation = $this->getAttr($key);
$relation->visible($name);
if ($relation) {
$relation->visible($name);
}
}
$item[$key] = $relation->append($name)->toArray();
$item[$key] = $relation ? $relation->append($name)->toArray() : [];
} elseif (strpos($name, '.')) {
list($key, $attr) = explode('.', $name);
// 追加关联对象属性
@ -182,15 +202,14 @@ trait Conversion
if (!$relation) {
$relation = $this->getAttr($key);
$relation->visible([$attr]);
if ($relation) {
$relation->visible([$attr]);
}
}
$item[$key] = $relation->append([$attr])->toArray();
$item[$key] = $relation ? $relation->append([$attr])->toArray() : [];
} else {
$value = $this->getAttr($name, $item);
if (false !== $value) {
$item[$name] = $value;
}
$item[$name] = $this->getAttr($name, $item);
}
}
}
@ -251,38 +270,4 @@ trait Conversion
return $collection;
}
/**
* 解析隐藏及显示属性
* @access protected
* @param array $attrs 属性
* @param array $result 结果集
* @param bool $visible
* @return array
*/
protected function parseAttr($attrs, &$result, $visible = true)
{
$array = [];
foreach ($attrs as $key => $val) {
if (is_array($val)) {
if ($visible) {
$array[] = $key;
}
$result[$key] = $val;
} elseif (strpos($val, '.')) {
list($key, $name) = explode('.', $val);
if ($visible) {
$array[] = $key;
}
$result[$key][] = $name;
} else {
$array[] = $val;
}
}
return $array;
}
}

View File

@ -204,7 +204,7 @@ trait RelationShip
$relationResult = $this->$method();
if (isset($withRelationAttr[$relationName])) {
$relationResult->withAttr($withRelationAttr[$relationName]);
$relationResult->getQuery()->withAttr($withRelationAttr[$relationName]);
}
$this->relation[$relation] = $relationResult->getRelation($subRelation, $closure);
@ -248,7 +248,7 @@ trait RelationShip
$relationResult = $this->$relation();
if (isset($withRelationAttr[$relationName])) {
$relationResult->withAttr($withRelationAttr[$relationName]);
$relationResult->getQuery()->withAttr($withRelationAttr[$relationName]);
}
$relationResult->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $join);
@ -290,7 +290,7 @@ trait RelationShip
$relationResult = $this->$relation();
if (isset($withRelationAttr[$relationName])) {
$relationResult->withAttr($withRelationAttr[$relationName]);
$relationResult->getQuery()->withAttr($withRelationAttr[$relationName]);
}
$relationResult->eagerlyResult($result, $relation, $subRelation, $closure, $join);

View File

@ -235,7 +235,8 @@ trait SoftDelete
$field = $this->getDeleteTimeField(true);
if ($field) {
$query->useSoftDelete($field, $this->defaultSoftDelete);
$condition = is_null($this->defaultSoftDelete) ? ['null', ''] : ['=', $this->defaultSoftDelete];
$query->useSoftDelete($field, $condition);
}
}
}

View File

@ -11,6 +11,7 @@
namespace think\model\relation;
use Closure;
use think\Loader;
use think\Model;
@ -49,7 +50,7 @@ class BelongsTo extends OneToOne
*/
public function getRelation($subRelation = '', $closure = null)
{
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -79,7 +80,7 @@ class BelongsTo extends OneToOne
*/
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
{
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
@ -111,7 +112,7 @@ class BelongsTo extends OneToOne
return 0;
}
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
@ -140,13 +141,17 @@ class BelongsTo extends OneToOne
$relation = basename(str_replace('\\', '/', $this->model));
$localKey = $this->localKey;
$foreignKey = $this->foreignKey;
$softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
$query->table([$table => $relation])
->field($relation . '.' . $localKey)
->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey);
->whereExp($model . '.' . $foreignKey, '=' . $relation . '.' . $localKey)
->when($softDelete, function ($query) use ($softDelete, $relation) {
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
});
});
}
@ -167,12 +172,16 @@ class BelongsTo extends OneToOne
$this->getQueryWhere($where, $relation);
}
$fields = $this->getRelationQueryFields($fields, $model);
$fields = $this->getRelationQueryFields($fields, $model);
$softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->field($fields)
->join([$table => $relation], $model . '.' . $this->foreignKey . '=' . $relation . '.' . $this->localKey, $this->joinType)
->when($softDelete, function ($query) use ($softDelete, $relation) {
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
})
->where($where);
}

View File

@ -11,6 +11,7 @@
namespace think\model\relation;
use Closure;
use think\Collection;
use think\db\Query;
use think\Exception;
@ -166,7 +167,7 @@ class BelongsToMany extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -377,7 +378,7 @@ class BelongsToMany extends Relation
$pk = $result->$pk;
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
@ -401,7 +402,7 @@ class BelongsToMany extends Relation
*/
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
{
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
@ -428,7 +429,7 @@ class BelongsToMany extends Relation
protected function eagerlyManyToMany($where, $relation, $subRelation = '', $closure = null)
{
// 预载入关联查询 支持嵌套预载入
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -561,6 +562,7 @@ class BelongsToMany extends Relation
$pivot[$this->foreignKey] = $id;
$this->pivot->replace()
->exists(false)
->data([])
->save($pivot);
$result[] = $this->newPivot($pivot, true);
}

View File

@ -11,6 +11,7 @@
namespace think\model\relation;
use Closure;
use think\db\Query;
use think\Loader;
use think\Model;
@ -48,7 +49,7 @@ class HasMany extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -163,7 +164,7 @@ class HasMany extends Relation
return 0;
}
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
$name = $return;
@ -186,7 +187,7 @@ class HasMany extends Relation
*/
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
{
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
@ -194,8 +195,8 @@ class HasMany extends Relation
}
}
return $this->query
->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
return $this->query->alias($aggregate . '_table')
->whereExp($aggregate . '_table.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
->fetchSql()
->$aggregate($field);
}
@ -216,7 +217,7 @@ class HasMany extends Relation
$this->query->removeWhereField($this->foreignKey);
// 预载入关联查询 支持嵌套预载入
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -266,11 +267,11 @@ class HasMany extends Relation
/**
* 批量保存当前关联数据对象
* @access public
* @param array $dataSet 数据集
* @param array|\think\Collection $dataSet 数据集
* @param boolean $replace 是否自动识别更新和写入
* @return array|false
*/
public function saveAll(array $dataSet, $replace = true)
public function saveAll($dataSet, $replace = true)
{
$result = [];
@ -292,14 +293,18 @@ class HasMany extends Relation
*/
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
{
$table = $this->query->getTable();
$model = basename(str_replace('\\', '/', get_class($this->parent)));
$relation = basename(str_replace('\\', '/', $this->model));
$table = $this->query->getTable();
$model = basename(str_replace('\\', '/', get_class($this->parent)));
$relation = basename(str_replace('\\', '/', $this->model));
$softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->field($model . '.*')
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $joinType)
->when($softDelete, function ($query) use ($softDelete, $relation) {
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
})
->group($relation . '.' . $this->foreignKey)
->having('count(' . $id . ')' . $operator . $count);
}
@ -321,13 +326,17 @@ class HasMany extends Relation
$this->getQueryWhere($where, $relation);
}
$fields = $this->getRelationQueryFields($fields, $model);
$fields = $this->getRelationQueryFields($fields, $model);
$softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->group($model . '.' . $this->localKey)
->field($fields)
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey)
->when($softDelete, function ($query) use ($softDelete, $relation) {
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
})
->where($where);
}

View File

@ -11,8 +11,8 @@
namespace think\model\relation;
use Closure;
use think\db\Query;
use think\Exception;
use think\Loader;
use think\Model;
use think\model\Relation;
@ -24,6 +24,12 @@ class HasManyThrough extends Relation
// 中间表模型
protected $through;
/**
* 中间主键
* @var string
*/
protected $throughPk;
/**
* 架构函数
* @access public
@ -38,9 +44,10 @@ class HasManyThrough extends Relation
{
$this->parent = $parent;
$this->model = $model;
$this->through = $through;
$this->through = (new $through)->db();
$this->foreignKey = $foreignKey;
$this->throughKey = $throughKey;
$this->throughPk = $this->through->getPk();
$this->localKey = $localKey;
$this->query = (new $model)->db();
}
@ -54,7 +61,7 @@ class HasManyThrough extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -74,7 +81,28 @@ class HasManyThrough extends Relation
*/
public function has($operator = '>=', $count = 1, $id = '*', $joinType = 'INNER')
{
return $this->parent;
$model = App::parseName(App::classBaseName($this->parent));
$throughTable = $this->through->getTable();
$pk = $this->throughPk;
$throughKey = $this->throughKey;
$relation = (new $this->model)->db();
$relationTable = $relation->getTable();
$softDelete = $this->query->getOptions('soft_delete');
if ('*' != $id) {
$id = $relationTable . '.' . $relation->getPk();
}
return $this->parent->db()
->alias($model)
->field($model . '.*')
->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey)
->join($relationTable, $relationTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk)
->when($softDelete, function ($query) use ($softDelete, $relationTable) {
$query->where($relationTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
})
->group($relationTable . '.' . $this->throughKey)
->having('count(' . $id . ')' . $operator . $count);
}
/**
@ -86,45 +114,225 @@ class HasManyThrough extends Relation
*/
public function hasWhere($where = [], $fields = null)
{
throw new Exception('relation not support: hasWhere');
$model = App::parseName(App::classBaseName($this->parent));
$throughTable = $this->through->getTable();
$pk = $this->throughPk;
$throughKey = $this->throughKey;
$modelTable = (new $this->model)->db()->getTable();
if (is_array($where)) {
$this->getQueryWhere($where, $modelTable);
}
$fields = $this->getRelationQueryFields($fields, $model);
$softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->join($throughTable, $throughTable . '.' . $this->foreignKey . '=' . $model . '.' . $this->localKey)
->join($modelTable, $modelTable . '.' . $throughKey . '=' . $throughTable . '.' . $this->throughPk)
->when($softDelete, function ($query) use ($softDelete, $modelTable) {
$query->where($modelTable . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
})
->group($modelTable . '.' . $this->throughKey)
->where($where)
->field($fields);
}
/**
* 预载入关联查询
* @access public
* @param array $resultSet 数据集
* @param string $relation 当前关联名
* @param string $subRelation 子关联名
* @param \Closure $closure 闭包
* 预载入关联查询(数据集)
* @access protected
* @param array $resultSet 数据集
* @param string $relation 当前关联名
* @param mixed $subRelation 子关联名
* @param Closure $closure 闭包
* @return void
*/
public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure)
{}
public function eagerlyResultSet(array &$resultSet, $relation, $subRelation = '', $closure = null)
{
$localKey = $this->localKey;
$foreignKey = $this->foreignKey;
$range = [];
foreach ($resultSet as $result) {
// 获取关联外键列表
if (isset($result->$localKey)) {
$range[] = $result->$localKey;
}
}
if (!empty($range)) {
$this->query->removeWhereField($foreignKey);
$data = $this->eagerlyWhere([
[$this->foreignKey, 'in', $range],
], $foreignKey, $relation, $subRelation, $closure);
// 关联属性名
$attr = App::parseName($relation);
// 关联数据封装
foreach ($resultSet as $result) {
$pk = $result->$localKey;
if (!isset($data[$pk])) {
$data[$pk] = [];
}
foreach ($data[$pk] as &$relationModel) {
$relationModel->setParent(clone $result);
}
// 设置关联属性
$result->setRelation($attr, $this->resultSetBuild($data[$pk]));
}
}
}
/**
* 预载入关联查询 返回模型对象
* @access public
* @param Model $result 数据对象
* @param string $relation 当前关联名
* @param string $subRelation 子关联名
* @param \Closure $closure 闭包
* 预载入关联查询(数据)
* @access protected
* @param Model $result 数据对象
* @param string $relation 当前关联名
* @param mixed $subRelation 子关联名
* @param Closure $closure 闭包
* @return void
*/
public function eagerlyResult(&$result, $relation, $subRelation, $closure)
{}
public function eagerlyResult($result, $relation, $subRelation = '', $closure = null)
{
$localKey = $this->localKey;
$foreignKey = $this->foreignKey;
$pk = $result->$localKey;
$this->query->removeWhereField($foreignKey);
$data = $this->eagerlyWhere([
[$foreignKey, '=', $pk],
], $foreignKey, $relation, $subRelation, $closure);
// 关联数据封装
if (!isset($data[$pk])) {
$data[$pk] = [];
}
foreach ($data[$pk] as &$relationModel) {
$relationModel->setParent(clone $result);
}
$result->setRelation(App::parseName($relation), $this->resultSetBuild($data[$pk]));
}
/**
* 关联模型预查询
* @access public
* @param array $where 关联预查询条件
* @param string $key 关联键名
* @param string $relation 关联名
* @param mixed $subRelation 子关联
* @param Closure $closure
* @return array
*/
protected function eagerlyWhere(array $where, $key, $relation, $subRelation = '', $closure = null)
{
// 预载入关联查询 支持嵌套预载入
$throughList = $this->through->where($where)->select();
$keys = $throughList->column($this->throughPk, $this->throughPk);
if ($closure instanceof Closure) {
$closure($this->query);
}
$list = $this->query->where($this->throughKey, 'in', $keys)->select();
// 组装模型数据
$data = [];
$keys = $throughList->column($this->foreignKey, $this->throughPk);
foreach ($list as $set) {
$data[$keys[$set->{$this->throughKey}]][] = $set;
}
return $data;
}
/**
* 关联统计
* @access public
* @param Model $result 数据对象
* @param \Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
* @param string $name 统计字段别名
* @param Model $result 数据对象
* @param Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
* @param string $name 统计字段别名
* @return integer
*/
public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = '')
{}
public function relationCount($result, $closure, $aggregate = 'count', $field = '*', &$name = null)
{
$localKey = $this->localKey;
if (!isset($result->$localKey)) {
return 0;
}
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
$name = $return;
}
}
$alias = App::parseName(App::classBaseName($this->model));
$throughTable = $this->through->getTable();
$pk = $this->throughPk;
$throughKey = $this->throughKey;
$modelTable = $this->parent->getTable();
if (false === strpos($field, '.')) {
$field = $alias . '.' . $field;
}
return $this->query
->alias($alias)
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
->where($throughTable . '.' . $this->foreignKey, $result->$localKey)
->$aggregate($field);
}
/**
* 创建关联统计子查询
* @access public
* @param Closure $closure 闭包
* @param string $aggregate 聚合查询方法
* @param string $field 字段
* @param string $name 统计字段别名
* @return string
*/
public function getRelationCountQuery($closure = null, $aggregate = 'count', $field = '*', &$name = null)
{
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
$name = $return;
}
}
$alias = App::parseName(App::classBaseName($this->model));
$throughTable = $this->through->getTable();
$pk = $this->throughPk;
$throughKey = $this->throughKey;
$modelTable = $this->parent->getTable();
if (false === strpos($field, '.')) {
$field = $alias . '.' . $field;
}
return $this->query
->alias($alias)
->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey)
->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey)
->whereExp($throughTable . '.' . $this->foreignKey, '=' . $this->parent->getTable() . '.' . $this->localKey)
->fetchSql()
->$aggregate($field);
}
/**
* 执行基础查询(仅执行一次)
@ -134,10 +342,9 @@ class HasManyThrough extends Relation
protected function baseQuery()
{
if (empty($this->baseQuery) && $this->parent->getData()) {
$through = $this->through;
$alias = Loader::parseName(basename(str_replace('\\', '/', $this->model)));
$throughTable = $through::getTable();
$pk = (new $through)->getPk();
$throughTable = $this->through->getTable();
$pk = $this->throughPk;
$throughKey = $this->throughKey;
$modelTable = $this->parent->getTable();
$fields = $this->getQueryFields($alias);

View File

@ -11,6 +11,7 @@
namespace think\model\relation;
use Closure;
use think\db\Query;
use think\Loader;
use think\Model;
@ -50,7 +51,7 @@ class HasOne extends OneToOne
{
$localKey = $this->localKey;
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -79,7 +80,7 @@ class HasOne extends OneToOne
*/
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
{
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
@ -111,7 +112,7 @@ class HasOne extends OneToOne
return 0;
}
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
$name = $return;
@ -139,13 +140,17 @@ class HasOne extends OneToOne
$relation = basename(str_replace('\\', '/', $this->model));
$localKey = $this->localKey;
$foreignKey = $this->foreignKey;
$softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->whereExists(function ($query) use ($table, $model, $relation, $localKey, $foreignKey) {
$query->table([$table => $relation])
->field($relation . '.' . $foreignKey)
->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey);
->whereExp($model . '.' . $localKey, '=' . $relation . '.' . $foreignKey)
->when($softDelete, function ($query) use ($softDelete, $relation) {
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
});
});
}
@ -166,12 +171,16 @@ class HasOne extends OneToOne
$this->getQueryWhere($where, $relation);
}
$fields = $this->getRelationQueryFields($fields, $model);
$fields = $this->getRelationQueryFields($fields, $model);
$softDelete = $this->query->getOptions('soft_delete');
return $this->parent->db()
->alias($model)
->field($fields)
->join([$table => $relation], $model . '.' . $this->localKey . '=' . $relation . '.' . $this->foreignKey, $this->joinType)
->when($softDelete, function ($query) use ($softDelete, $relation) {
$query->where($relation . strstr($softDelete[0], '.'), '=' == $softDelete[1][0] ? $softDelete[1][1] : null);
})
->where($where);
}

View File

@ -11,6 +11,7 @@
namespace think\model\relation;
use Closure;
use think\db\Query;
use think\Exception;
use think\Loader;
@ -53,7 +54,7 @@ class MorphMany extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -197,7 +198,7 @@ class MorphMany extends Relation
return 0;
}
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
@ -224,7 +225,7 @@ class MorphMany extends Relation
*/
public function getRelationCountQuery($closure, $aggregate = 'count', $field = '*', &$aggregateAlias = '')
{
if ($closure) {
if ($closure instanceof Closure) {
$return = $closure($this->query);
if ($return && is_string($return)) {
@ -253,7 +254,7 @@ class MorphMany extends Relation
// 预载入关联查询 支持嵌套预载入
$this->query->removeOption('where');
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}

View File

@ -11,6 +11,7 @@
namespace think\model\relation;
use Closure;
use think\db\Query;
use think\Exception;
use think\Loader;
@ -53,7 +54,7 @@ class MorphOne extends Relation
*/
public function getRelation($subRelation = '', $closure = null)
{
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}
@ -186,7 +187,7 @@ class MorphOne extends Relation
protected function eagerlyMorphToOne($where, $relation, $subRelation = '', $closure = null)
{
// 预载入关联查询 支持嵌套预载入
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
}

View File

@ -11,6 +11,7 @@
namespace think\model\relation;
use Closure;
use think\Exception;
use think\Loader;
use think\Model;

View File

@ -11,6 +11,7 @@
namespace think\model\relation;
use Closure;
use think\db\Query;
use think\Exception;
use think\Loader;
@ -87,7 +88,7 @@ abstract class OneToOne extends Relation
$joinOn = $name . '.' . $this->localKey . '=' . $joinAlias . '.' . $this->foreignKey;
}
if ($closure) {
if ($closure instanceof Closure) {
// 执行闭包查询
$closure($query);
// 使用withField指定获取关联的字段
@ -311,7 +312,7 @@ abstract class OneToOne extends Relation
protected function eagerlyWhere($where, $key, $relation, $subRelation = '', $closure = null)
{
// 预载入关联查询 支持嵌套预载入
if ($closure) {
if ($closure instanceof Closure) {
$closure($this->query);
if ($field = $this->query->getOptions('with_field')) {

View File

@ -20,7 +20,7 @@ class Download extends Response
protected $name;
protected $mimeType;
protected $isContent = false;
protected $openinBrower = false;
protected $openinBrowser = false;
/**
* 处理数据
* @access protected
@ -53,7 +53,7 @@ class Download extends Response
$this->header['Pragma'] = 'public';
$this->header['Content-Type'] = $mimeType ?: 'application/octet-stream';
$this->header['Cache-control'] = 'max-age=' . $this->expire;
$this->header['Content-Disposition'] = $this->openinBrower ? 'inline' : 'attachment; filename="' . $name . '"';
$this->header['Content-Disposition'] = $this->openinBrowser ? 'inline' : 'attachment; filename="' . $name . '"';
$this->header['Content-Length'] = $size;
$this->header['Content-Transfer-Encoding'] = 'binary';
$this->header['Expires'] = gmdate("D, d M Y H:i:s", time() + $this->expire) . ' GMT';
@ -138,11 +138,11 @@ class Download extends Response
/**
* 设置是否在浏览器中显示文件
* @access public
* @param bool $openinBrower 是否在浏览器中显示文件
* @param bool $openinBrowser 是否在浏览器中显示文件
* @return $this
*/
public function openinBrower($openinBrower) {
$this->openinBrower = $openinBrower;
public function openinBrowser($openinBrowser) {
$this->openinBrowser = $openinBrowser;
return $this;
}
}

View File

@ -87,11 +87,12 @@ class Redirect extends Response
/**
* 记住当前url后跳转
* @access public
* @param string $url 指定记住的url
* @return $this
*/
public function remember()
public function remember($url = null)
{
$this->app['session']->set('redirect_url', $this->app['request']->url());
$this->app['session']->set('redirect_url', $url ?: $this->app['request']->url());
return $this;
}

View File

@ -18,9 +18,16 @@ class View extends Response
// 输出参数
protected $options = [];
protected $vars = [];
protected $config = [];
protected $filter;
protected $contentType = 'text/html';
/**
* 是否内容渲染
* @var bool
*/
protected $isContent = false;
/**
* 处理数据
* @access protected
@ -32,7 +39,19 @@ class View extends Response
// 渲染模板输出
return $this->app['view']
->filter($this->filter)
->fetch($data, $this->vars);
->fetch($data, $this->vars, $this->config, $this->isContent);
}
/**
* 设置是否为内容渲染
* @access public
* @param bool $content
* @return $this
*/
public function isContent($content = true)
{
$this->isContent = $content;
return $this;
}
/**
@ -68,6 +87,12 @@ class View extends Response
return $this;
}
public function config($config)
{
$this->config = $config;
return $this;
}
/**
* 视图内容过滤
* @access public

View File

@ -11,9 +11,9 @@
namespace think\route;
use think\App;
use think\Container;
use think\exception\ValidateException;
use think\App;
use think\Request;
use think\Response;
@ -181,9 +181,10 @@ abstract class Dispatch
$response = Response::create($data, $type);
} else {
$data = ob_get_clean();
$content = false === $data ? '' : $data;
$status = '' === $content && $this->request->isAjax() ? 204 : 200;
$data = ob_get_clean();
$content = false === $data ? '' : $data;
$status = '' === $content && $this->request->isJson() ? 204 : 200;
$response = Response::create($content, '', $status);
}

View File

@ -642,17 +642,26 @@ abstract class Rule
protected function checkCrossDomain($request)
{
if (!empty($this->option['cross_domain'])) {
$header = [
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE',
'Access-Control-Allow-Headers' => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With',
'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, DELETE',
'Access-Control-Allow-Headers' => 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With',
];
if (!empty($this->option['header'])) {
$header = array_merge($header, $this->option['header']);
}
if (!isset($header['Access-Control-Allow-Origin'])) {
$httpOrigin = $request->header('origin');
if ($httpOrigin && strpos(config('cookie.domain'), $httpOrigin)) {
$header['Access-Control-Allow-Origin'] = $httpOrigin;
} else {
$header['Access-Control-Allow-Origin'] = '*';
}
}
$this->option['header'] = $header;
if ($request->method(true) == 'OPTIONS') {
@ -1001,7 +1010,7 @@ abstract class Rule
}
}
$regex = str_replace($match, $replace, $rule);
$regex = str_replace(array_unique($match), array_unique($replace), $rule);
$regex = str_replace([')?/', ')/', ')?-', ')-', '\\\\/'], [')\/', ')\/', ')\-', ')\-', '\/'], $regex);
if (isset($hasSlash)) {

View File

@ -118,8 +118,8 @@ class RuleGroup extends Rule
*/
public function check($request, $url, $completeMatch = false)
{
// 跨域OPTIONS请求
if ($dispatch = $this->checkCrossDomain($request)) {
// 跨域OPTIONS请求
return $dispatch;
}
@ -151,10 +151,6 @@ class RuleGroup extends Rule
$method = strtolower($request->method());
$rules = $this->getMethodRules($method);
if (count($rules) == 0) {
return false;
}
if ($this->parent) {
// 合并分组参数
$this->mergeGroupOptions();

View File

@ -149,11 +149,6 @@ class RuleItem extends Rule
*/
public function checkRule($request, $url, $match = null, $completeMatch = false)
{
if ($dispatch = $this->checkCrossDomain($request)) {
// 允许跨域
return $dispatch;
}
// 检查参数有效性
if (!$this->checkOption($this->option, $request)) {
return false;
@ -169,6 +164,15 @@ class RuleItem extends Rule
}
if (false !== $match) {
if (!empty($option['cross_domain'])) {
if ($dispatch = $this->checkCrossDomain($request)) {
// 允许跨域
return $dispatch;
}
$option['header'] = $this->option['header'];
}
// 检查前置行为
if (isset($option['before']) && false === $this->checkBefore($option['before'])) {
return false;

View File

@ -12,7 +12,6 @@
namespace think\route\dispatch;
use ReflectionMethod;
use think\Controller;
use think\exception\ClassNotFoundException;
use think\exception\HttpException;
use think\Loader;

View File

@ -124,7 +124,7 @@ class Redis implements SessionHandlerInterface
*/
public function destroy($sessID)
{
return $this->handler->delete($this->config['session_name'] . $sessID) > 0;
return $this->handler->del($this->config['session_name'] . $sessID) > 0;
}
/**

View File

@ -280,9 +280,6 @@
pre.prettyprint .atv { color: #080 } /* a markup attribute value */
pre.prettyprint .dec, pre.prettyprint .var { color: #606 } /* a declaration; a variable name */
pre.prettyprint .fun { color: red } /* a function name */
/* bbs */
.bbs-ask { margin-left: 15px; }
</style>
</head>
<body>
@ -413,10 +410,9 @@
<?php } ?>
<div class="copyright">
<a title="<?php echo implode('', ['S','h','o','p','X','O']); ?>官网" href="<?php echo implode('', ['h','t','t','p',':','/','/','w','w','w','.','s','h','o','p','x','o','.','n','e','t']); ?>"target="_blank"><?php echo implode('', ['S','h','o','p','X','O']); ?>官网</a>
<span><?php echo APPLICATION_VERSION; ?></span>
<a title="<?php echo implode('', ['S','h','o','p','X','O']); ?>社区" href="<?php echo implode('', ['h','t','t','p',':','/','/','a','s','k','.','s','h','o','p','x','o','.','n','e','t']); ?>" target="_blank" class="bbs-ask"><?php echo implode('', ['S','h','o','p','X','O']); ?>社区</a>
<span>{ B2C免费开源电商系统 }</span>
<a title="官方网站" href="http://www.thinkphp.cn">ThinkPHP</a>
<span>V<?php echo \think\facade\App::version(); ?></span>
<span>{ -API开发设计的高性能框架 }</span>
</div>
<?php if(\think\facade\App::isDebug()) { ?>
<script>