diff --git a/src/Model.php b/src/Model.php
index 03453584..b3a99ea1 100644
--- a/src/Model.php
+++ b/src/Model.php
@@ -260,11 +260,12 @@ public function getName(): string
/**
* 创建新的模型实例
* @access public
- * @param array $data 数据
- * @param mixed $where 更新条件
+ * @param array $data 数据
+ * @param mixed $where 更新条件
+ * @param array $options 参数
* @return Model
*/
- public function newInstance(array $data = [], $where = null): Model
+ public function newInstance(array $data = [], $where = null, array $options = []): Model
{
$model = new static($data);
@@ -970,21 +971,25 @@ public function __unset(string $name): void
}
// ArrayAccess
+ #[\ReturnTypeWillChange]
public function offsetSet($name, $value)
{
$this->setAttr($name, $value);
}
+ #[\ReturnTypeWillChange]
public function offsetExists($name): bool
{
return $this->__isset($name);
}
+ #[\ReturnTypeWillChange]
public function offsetUnset($name)
{
$this->__unset($name);
}
+ #[\ReturnTypeWillChange]
public function offsetGet($name)
{
return $this->getAttr($name);
@@ -1037,10 +1042,6 @@ public function __call($method, $args)
return call_user_func_array(static::$macro[static::class][$method]->bindTo($this, static::class), $args);
}
- if ('withattr' == strtolower($method)) {
- return call_user_func_array([$this, 'withAttribute'], $args);
- }
-
return call_user_func_array([$this->db(), $method], $args);
}
diff --git a/src/Paginator.php b/src/Paginator.php
index d8d43d7d..2f755ef4 100644
--- a/src/Paginator.php
+++ b/src/Paginator.php
@@ -410,7 +410,8 @@ public function each(callable $callback)
* @return Traversable An instance of an object implementing Iterator or
* Traversable
*/
- public function getIterator()
+ #[\ReturnTypeWillChange]
+ public function getIterator(): Traversable
{
return new ArrayIterator($this->items->all());
}
@@ -421,7 +422,8 @@ public function getIterator()
* @param mixed $offset
* @return bool
*/
- public function offsetExists($offset)
+ #[\ReturnTypeWillChange]
+ public function offsetExists($offset): bool
{
return $this->items->offsetExists($offset);
}
@@ -432,6 +434,7 @@ public function offsetExists($offset)
* @param mixed $offset
* @return mixed
*/
+ #[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->items->offsetGet($offset);
@@ -443,6 +446,7 @@ public function offsetGet($offset)
* @param mixed $offset
* @param mixed $value
*/
+ #[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->items->offsetSet($offset, $value);
@@ -455,6 +459,7 @@ public function offsetSet($offset, $value)
* @return void
* @since 5.0.0
*/
+ #[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
$this->items->offsetUnset($offset);
@@ -498,6 +503,7 @@ public function toArray(): array
/**
* Specify data which should be serialized to JSON
*/
+ #[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
diff --git a/src/db/BaseQuery.php b/src/db/BaseQuery.php
index 4bd4bfbf..cccd8ef8 100644
--- a/src/db/BaseQuery.php
+++ b/src/db/BaseQuery.php
@@ -137,7 +137,7 @@ public function newQuery(): BaseQuery
$query->name($this->name);
}
- if (isset($this->options['json'])) {
+ if (!empty($this->options['json'])) {
$query->json($this->options['json'], $this->options['json_assoc']);
}
@@ -278,7 +278,11 @@ public function value(string $field, $default = null)
public function column($field, string $key = ''): array
{
$result = $this->connection->column($this, $field, $key);
- $this->resultSet($result, false);
+
+ if (count($result) != count($result, 1)) {
+ $this->resultSet($result, false);
+ }
+
return $result;
}
@@ -867,6 +871,7 @@ public function json(array $json = [], bool $assoc = false)
{
$this->options['json'] = $json;
$this->options['json_assoc'] = $assoc;
+
return $this;
}
@@ -1124,7 +1129,7 @@ public function select($data = null): Collection
* 查找单条记录
* @access public
* @param mixed $data 查询数据
- * @return array|Model|null|static
+ * @return array|Model|null|static|mixed
* @throws Exception
* @throws ModelNotFoundException
* @throws DataNotFoundException
@@ -1149,7 +1154,7 @@ public function find($data = null)
if (!empty($this->model)) {
// 返回模型对象
- $this->resultToModel($result, $this->options);
+ $this->resultToModel($result);
} else {
$this->result($result);
}
@@ -1178,7 +1183,7 @@ public function parseOptions(): array
$this->parseView($options);
}
- foreach (['data', 'order', 'join', 'union'] as $name) {
+ foreach (['data', 'order', 'join', 'union', 'filter', 'json', 'with_attr', 'with_relation_attr'] as $name) {
if (!isset($options[$name])) {
$options[$name] = [];
}
@@ -1188,7 +1193,7 @@ public function parseOptions(): array
$options['strict'] = $this->connection->getConfig('fields_strict');
}
- foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure'] as $name) {
+ foreach (['master', 'lock', 'fetch_sql', 'array', 'distinct', 'procedure', 'with_cache'] as $name) {
if (!isset($options[$name])) {
$options[$name] = false;
}
diff --git a/src/db/Fetch.php b/src/db/Fetch.php
index 16caed2b..a997a859 100644
--- a/src/db/Fetch.php
+++ b/src/db/Fetch.php
@@ -421,10 +421,8 @@ public function count(string $field = '*'): string
if (!empty($options['group'])) {
// 支持GROUP
- $bind = $this->query->getBind();
- $subSql = $this->query->options($options)->field('count(' . $field . ') AS think_count')->bind($bind)->buildSql();
-
- $query = $this->query->newQuery()->table([$subSql => '_group_count_']);
+ $subSql = $this->query->field('count(' . $field . ') AS think_count')->buildSql();
+ $query = $this->query->newQuery()->table([$subSql => '_group_count_']);
return $query->fetchsql()->aggregate('COUNT', '*');
} else {
diff --git a/src/db/Mongo.php b/src/db/Mongo.php
index 5e8a09a5..cf6e9c4c 100644
--- a/src/db/Mongo.php
+++ b/src/db/Mongo.php
@@ -630,7 +630,7 @@ public function parseOptions(): array
$options['table'] = $this->getTable();
}
- foreach (['where', 'data'] as $name) {
+ foreach (['where', 'data', 'projection', 'filter', 'json', 'with_attr', 'with_relation_attr'] as $name) {
if (!isset($options[$name])) {
$options[$name] = [];
}
@@ -649,10 +649,6 @@ public function parseOptions(): array
$options['modifiers'] = $modifiers;
}
- if (!isset($options['projection'])) {
- $options['projection'] = [];
- }
-
if (!isset($options['typeMap'])) {
$options['typeMap'] = $this->getConfig('type_map');
}
diff --git a/src/db/PDOConnection.php b/src/db/PDOConnection.php
index 1a3d4a2b..40ac99e9 100644
--- a/src/db/PDOConnection.php
+++ b/src/db/PDOConnection.php
@@ -279,7 +279,7 @@ public function fieldCase(array $info): array
*/
protected function getFieldType(string $type): string
{
- if (0 === strpos($type, 'set') || 0 === strpos($type, 'enum')) {
+ if (0 === stripos($type, 'set') || 0 === stripos($type, 'enum')) {
$result = 'string';
} elseif (preg_match('/(double|float|decimal|real|numeric)/is', $type)) {
$result = 'float';
@@ -287,11 +287,11 @@ protected function getFieldType(string $type): string
$result = 'int';
} elseif (preg_match('/bool/is', $type)) {
$result = 'bool';
- } elseif (0 === strpos($type, 'timestamp')) {
+ } elseif (0 === stripos($type, 'timestamp')) {
$result = 'timestamp';
- } elseif (0 === strpos($type, 'datetime')) {
+ } elseif (0 === stripos($type, 'datetime')) {
$result = 'datetime';
- } elseif (0 === strpos($type, 'date')) {
+ } elseif (0 === stripos($type, 'date')) {
$result = 'date';
} else {
$result = 'string';
@@ -1273,7 +1273,7 @@ public function getRealSql(string $sql, array $bind = []): string
$type = is_array($val) ? $val[1] : PDO::PARAM_STR;
if (self::PARAM_FLOAT == $type || PDO::PARAM_STR == $type) {
- $value = '\'' . addslashes($value) . '\'';
+ $value = '\'' . addcslashes($value, "'") . '\'';
} elseif (PDO::PARAM_INT == $type && '' === $value) {
$value = '0';
}
diff --git a/src/db/builder/Sqlite.php b/src/db/builder/Sqlite.php
index 40cab7f8..ff17c5d6 100644
--- a/src/db/builder/Sqlite.php
+++ b/src/db/builder/Sqlite.php
@@ -24,8 +24,8 @@ class Sqlite extends Builder
/**
* limit
* @access public
- * @param Query $query 查询对象
- * @param mixed $limit
+ * @param Query $query 查询对象
+ * @param mixed $limit
* @return string
*/
public function parseLimit(Query $query, string $limit): string
@@ -47,7 +47,7 @@ public function parseLimit(Query $query, string $limit): string
/**
* 随机排序
* @access protected
- * @param Query $query 查询对象
+ * @param Query $query 查询对象
* @return string
*/
protected function parseRand(Query $query): string
@@ -58,9 +58,9 @@ protected function parseRand(Query $query): string
/**
* 字段和表名处理
* @access public
- * @param Query $query 查询对象
- * @param mixed $key 字段名
- * @param bool $strict 严格检测
+ * @param Query $query 查询对象
+ * @param mixed $key 字段名
+ * @param bool $strict 严格检测
* @return string
*/
public function parseKey(Query $query, $key, bool $strict = false): string
@@ -73,7 +73,7 @@ public function parseKey(Query $query, $key, bool $strict = false): string
$key = trim($key);
- if (strpos($key, '.')) {
+ if (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
[$table, $key] = explode('.', $key, 2);
$alias = $query->getOptions('alias');
@@ -88,10 +88,26 @@ public function parseKey(Query $query, $key, bool $strict = false): string
}
}
+ if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
+ $key = '`' . $key . '`';
+ }
+
if (isset($table)) {
- $key = $table . '.' . $key;
+ $key = '`' . $table . '`.' . $key;
}
return $key;
}
+
+ /**
+ * 设置锁机制
+ * @access protected
+ * @param Query $query 查询对象
+ * @param bool|string $lock
+ * @return string
+ */
+ protected function parseLock(Query $query, $lock = false): string
+ {
+ return '';
+ }
}
diff --git a/src/db/concern/ModelRelationQuery.php b/src/db/concern/ModelRelationQuery.php
index ffb72de4..0015a24a 100644
--- a/src/db/concern/ModelRelationQuery.php
+++ b/src/db/concern/ModelRelationQuery.php
@@ -54,36 +54,39 @@ public function getModel()
/**
* 设置需要隐藏的输出属性
* @access public
- * @param array $hidden 需要隐藏的字段名
+ * @param array $hidden 属性列表
* @return $this
*/
- public function hidden(array $hidden)
+ public function hidden(array $hidden = [])
{
$this->options['hidden'] = $hidden;
+
return $this;
}
/**
* 设置需要输出的属性
* @access public
- * @param array $visible 需要输出的属性
+ * @param array $visible
* @return $this
*/
- public function visible(array $visible)
+ public function visible(array $visible = [])
{
$this->options['visible'] = $visible;
+
return $this;
}
/**
- * 设置需要追加输出的属性
+ * 设置需要附加的输出属性
* @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;
}
@@ -130,10 +133,11 @@ public function scope($scope, ...$args)
*/
public function relation(array $relation)
{
- if (!empty($relation)) {
- $this->options['relation'] = $relation;
+ if (empty($this->model) || empty($relation)) {
+ return $this;
}
+ $this->options['relation'] = $relation;
return $this;
}
@@ -175,16 +179,30 @@ public function withSearch($fields, $data = [], string $prefix = '')
/**
* 设置数据字段获取器
* @access public
- * @param string|array $name 字段名
- * @param callable $callback 闭包获取器
+ * @param string|array $name 字段名
+ * @param callable $callback 闭包获取器
* @return $this
*/
public function withAttr($name, callable $callback = null)
{
if (is_array($name)) {
- $this->options['with_attr'] = $name;
- } else {
- $this->options['with_attr'][$name] = $callback;
+ foreach ($name as $key => $val) {
+ $this->withAttr($key, $val);
+ }
+ return $this;
+ }
+
+ $this->options['with_attr'][$name] = $callback;
+
+ if (strpos($name, '.')) {
+ [$relation, $field] = explode('.', $name);
+
+ if (!empty($this->options['json']) && in_array($relation, $this->options['json'])) {
+
+ } else {
+ $this->options['with_relation_attr'][$relation][$field] = $callback;
+ unset($this->options['with_attr'][$name]);
+ }
}
return $this;
@@ -198,10 +216,11 @@ public function withAttr($name, callable $callback = null)
*/
public function with($with)
{
- if (!empty($with)) {
- $this->options['with'] = (array) $with;
+ if (empty($this->model) || empty($with)) {
+ return $this;
}
+ $this->options['with'] = (array) $with;
return $this;
}
@@ -214,7 +233,7 @@ public function with($with)
*/
public function withJoin($with, string $joinType = '')
{
- if (empty($with)) {
+ if (empty($this->model) || empty($with)) {
return $this;
}
@@ -246,7 +265,6 @@ public function withJoin($with, string $joinType = '')
}
$this->via();
-
$this->options['with_join'] = $with;
return $this;
@@ -263,16 +281,20 @@ public function withJoin($with, string $joinType = '')
*/
protected function withAggregate($relations, string $aggregate = 'count', $field = '*', bool $subQuery = true)
{
+ if (empty($this->model)) {
+ return $this;
+ }
+
if (!$subQuery) {
- $this->options['with_count'][] = [$relations, $aggregate, $field];
- } else {
- if (!isset($this->options['field'])) {
- $this->field('*');
- }
+ $this->options['with_aggregate'][] = [(array) $relations, $aggregate, $field];
+ return $this;
+ }
- $this->model->relationCount($this, (array) $relations, $aggregate, $field, true);
+ if (!isset($this->options['field'])) {
+ $this->field('*');
}
+ $this->model->relationCount($this, (array) $relations, $aggregate, $field, true);
return $this;
}
@@ -287,6 +309,10 @@ protected function withAggregate($relations, string $aggregate = 'count', $field
*/
public function withCache($relation = true, $key = true, $expire = null, string $tag = null)
{
+ if (empty($this->model)) {
+ return $this;
+ }
+
if (false === $relation || false === $key || !$this->getConnection()->getCache()) {
return $this;
}
@@ -406,6 +432,32 @@ public function hasWhere(string $relation, $where = [], string $fields = '*', st
return $this->model->hasWhere($relation, $where, $fields, $joinType, $this);
}
+ /**
+ * JSON字段数据转换
+ * @access protected
+ * @param array $result 查询数据
+ * @return void
+ */
+ protected function jsonModelResult(array &$result): void
+ {
+ $withAttr = $this->options['with_attr'];
+ foreach ($this->options['json'] as $name) {
+ if (!isset($result[$name])) {
+ continue;
+ }
+
+ $jsonData = json_decode($result[$name], true);
+
+ if (isset($withAttr[$name])) {
+ foreach ($withAttr[$name] as $key => $closure) {
+ $jsonData[$key] = $closure($jsonData[$key] ?? null, $jsonData);
+ }
+ }
+
+ $result[$name] = !$this->options['json_assoc'] ? (object) $jsonData : $jsonData;
+ }
+ }
+
/**
* 查询数据转换为模型数据集对象
* @access protected
@@ -418,33 +470,24 @@ protected function resultSetToModelCollection(array $resultSet): ModelCollection
return $this->model->toCollection();
}
- // 检查动态获取器
- if (!empty($this->options['with_attr'])) {
- foreach ($this->options['with_attr'] as $name => $val) {
- if (strpos($name, '.')) {
- [$relation, $field] = explode('.', $name);
-
- $withRelationAttr[$relation][$field] = $val;
- unset($this->options['with_attr'][$name]);
- }
- }
- }
-
- $withRelationAttr = $withRelationAttr ?? [];
+ $this->options['is_resultSet'] = true;
foreach ($resultSet as $key => &$result) {
// 数据转换为模型对象
- $this->resultToModel($result, $this->options, true, $withRelationAttr);
- }
-
- if (!empty($this->options['with'])) {
- // 预载入
- $result->eagerlyResultSet($resultSet, $this->options['with'], $withRelationAttr, false, $this->options['with_cache'] ?? false);
+ $this->resultToModel($result);
}
- if (!empty($this->options['with_join'])) {
- // 预载入
- $result->eagerlyResultSet($resultSet, $this->options['with_join'], $withRelationAttr, true, $this->options['with_cache'] ?? false);
+ foreach (['with', 'with_join'] as $with) {
+ // 关联预载入
+ if (!empty($this->options[$with])) {
+ $result->eagerlyResultSet(
+ $resultSet,
+ $this->options[$with],
+ $this->options['with_relation_attr'],
+ 'with_join' == $with ? true : false,
+ $this->options['with_cache'] ?? false
+ );
+ }
}
// 模型数据集转换
@@ -455,70 +498,84 @@ protected function resultSetToModelCollection(array $resultSet): ModelCollection
* 查询数据转换为模型对象
* @access protected
* @param array $result 查询数据
- * @param array $options 查询参数
- * @param bool $resultSet 是否为数据集查询
- * @param array $withRelationAttr 关联字段获取器
* @return void
*/
- protected function resultToModel(array &$result, array $options = [], bool $resultSet = false, array $withRelationAttr = []): void
+ protected function resultToModel(array &$result): void
{
- // 动态获取器
- if (!empty($options['with_attr']) && empty($withRelationAttr)) {
- foreach ($options['with_attr'] as $name => $val) {
- if (strpos($name, '.')) {
- [$relation, $field] = explode('.', $name);
-
- $withRelationAttr[$relation][$field] = $val;
- unset($options['with_attr'][$name]);
- }
- }
- }
-
- // JSON 数据处理
- if (!empty($options['json'])) {
- $this->jsonResult($result, $options['json'], $options['json_assoc'], $withRelationAttr);
+ // JSON数据处理
+ if (!empty($this->options['json'])) {
+ $this->jsonModelResult($result);
}
- $result = $this->model
- ->newInstance($result, $resultSet ? null : $this->getModelUpdateCondition($options));
+ $result = $this->model->newInstance(
+ $result,
+ !empty($this->options['is_resultSet']) ? null : $this->getModelUpdateCondition($this->options),
+ $this->options
+ );
- // 动态获取器
- if (!empty($options['with_attr'])) {
- $result->withAttribute($options['with_attr']);
- }
-
- // 输出属性控制
- if (!empty($options['visible'])) {
- $result->visible($options['visible']);
- } elseif (!empty($options['hidden'])) {
- $result->hidden($options['hidden']);
+ // 模型数据处理
+ foreach ($this->options['filter'] as $filter) {
+ call_user_func_array($filter, [$result, $this->options]);
}
- if (!empty($options['append'])) {
- $result->append($options['append']);
+ // 关联查询
+ if (!empty($this->options['relation'])) {
+ $result->relationQuery($this->options['relation'], $this->options['with_relation_attr']);
}
- // 关联查询
- if (!empty($options['relation'])) {
- $result->relationQuery($options['relation'], $withRelationAttr);
+ // 关联预载入查询
+ if (empty($this->options['is_resultSet'])) {
+ foreach (['with', 'with_join'] as $with) {
+ if (!empty($this->options[$with])) {
+ $result->eagerlyResult(
+ $this->options[$with],
+ $this->options['with_relation_attr'],
+ 'with_join' == $with ? true : false,
+ $this->options['with_cache'] ?? false
+ );
+ }
+ }
}
- // 预载入查询
- if (!$resultSet && !empty($options['with'])) {
- $result->eagerlyResult($result, $options['with'], $withRelationAttr, false, $options['with_cache'] ?? false);
+ // 关联统计查询
+ if (!empty($this->options['with_aggregate'])) {
+ foreach ($this->options['with_aggregate'] as $val) {
+ $result->relationCount($this, $val[0], $val[1], $val[2], false);
+ }
}
- // JOIN预载入查询
- if (!$resultSet && !empty($options['with_join'])) {
- $result->eagerlyResult($result, $options['with_join'], $withRelationAttr, true, $options['with_cache'] ?? false);
+ // 动态获取器
+ if (!empty($this->options['with_attr'])) {
+ $result->withAttr($this->options['with_attr']);
}
- // 关联统计
- if (!empty($options['with_count'])) {
- foreach ($options['with_count'] as $val) {
- $result->relationCount($this, (array) $val[0], $val[1], $val[2], false);
+ foreach (['hidden', 'visible', 'append'] as $name) {
+ if (!empty($this->options[$name])) {
+ $result->$name($this->options[$name]);
}
}
+
+ // 刷新原始数据
+ $result->refreshOrigin();
+ }
+
+ /**
+ * 查询软删除数据
+ * @access public
+ * @return Query
+ */
+ public function withTrashed()
+ {
+ return $this->model ? $this->model->queryWithTrashed() : $this;
}
+ /**
+ * 只查询软删除数据
+ * @access public
+ * @return Query
+ */
+ public function onlyTrashed()
+ {
+ return $this->model ? $this->model->queryOnlyTrashed() : $this;
+ }
}
diff --git a/src/db/concern/ResultOperation.php b/src/db/concern/ResultOperation.php
index 77409d16..ea269163 100644
--- a/src/db/concern/ResultOperation.php
+++ b/src/db/concern/ResultOperation.php
@@ -26,6 +26,23 @@
*/
trait ResultOperation
{
+ /**
+ * 设置数据处理(支持模型)
+ * @access public
+ * @param callable $filter 数据处理Callable
+ * @param string $index 索引(唯一)
+ * @return $this
+ */
+ public function filter(callable $filter, string $index = null)
+ {
+ if ($index) {
+ $this->options['filter'][$index] = $filter;
+ } else {
+ $this->options['filter'][] = $filter;
+ }
+ return $this;
+ }
+
/**
* 是否允许返回空数据(或空模型)
* @access public
@@ -58,15 +75,20 @@ public function failException(bool $fail = true)
*/
protected function result(array &$result): void
{
+ // JSON数据处理
if (!empty($this->options['json'])) {
- $this->jsonResult($result, $this->options['json'], true);
+ $this->jsonResult($result);
}
+ // 查询数据处理
+ foreach ($this->options['filter'] as $filter) {
+ $result = call_user_func_array($filter, [$result, $this->options]);
+ }
+
+ // 获取器
if (!empty($this->options['with_attr'])) {
$this->getResultAttr($result, $this->options['with_attr']);
}
-
- $this->filterResult($result);
}
/**
@@ -78,22 +100,8 @@ protected function result(array &$result): void
*/
protected function resultSet(array &$resultSet, bool $toCollection = true): void
{
- if (!empty($this->options['json'])) {
- foreach ($resultSet as &$result) {
- $this->jsonResult($result, $this->options['json'], true);
- }
- }
-
- if (!empty($this->options['with_attr'])) {
- foreach ($resultSet as &$result) {
- $this->getResultAttr($result, $this->options['with_attr']);
- }
- }
-
- if (!empty($this->options['visible']) || !empty($this->options['hidden'])) {
- foreach ($resultSet as &$result) {
- $this->filterResult($result);
- }
+ foreach ($resultSet as &$result) {
+ $this->result($result);
}
// 返回Collection对象
@@ -102,28 +110,6 @@ protected function resultSet(array &$resultSet, bool $toCollection = true): void
}
}
- /**
- * 处理数据的可见和隐藏
- * @access protected
- * @param array $result 查询数据
- * @return void
- */
- protected function filterResult(&$result): void
- {
- $array = [];
- if (!empty($this->options['visible'])) {
- foreach ($this->options['visible'] as $key) {
- $array[] = $key;
- }
- $result = array_intersect_key($result, array_flip($array));
- } elseif (!empty($this->options['hidden'])) {
- foreach ($this->options['hidden'] as $key) {
- $array[] = $key;
- }
- $result = array_diff_key($result, array_flip($array));
- }
- }
-
/**
* 使用获取器处理数据
* @access protected
@@ -170,7 +156,7 @@ protected function resultToEmpty()
* 查找单条记录 不存在返回空数据(或者空模型)
* @access public
* @param mixed $data 数据
- * @return array|Model|static
+ * @return array|Model|static|mixed
*/
public function findOrEmpty($data = null)
{
@@ -180,30 +166,17 @@ public function findOrEmpty($data = null)
/**
* JSON字段数据转换
* @access protected
- * @param array $result 查询数据
- * @param array $json JSON字段
- * @param bool $assoc 是否转换为数组
- * @param array $withRelationAttr 关联获取器
+ * @param array $result 查询数据
* @return void
*/
- protected function jsonResult(array &$result, array $json = [], bool $assoc = false, array $withRelationAttr = []): void
+ protected function jsonResult(array &$result): void
{
- foreach ($json as $name) {
+ foreach ($this->options['json'] as $name) {
if (!isset($result[$name])) {
continue;
}
$result[$name] = json_decode($result[$name], true);
-
- if (isset($withRelationAttr[$name])) {
- foreach ($withRelationAttr[$name] as $key => $closure) {
- $result[$name][$key] = $closure($result[$name][$key] ?? null, $result[$name]);
- }
- }
-
- if (!$assoc) {
- $result[$name] = (object) $result[$name];
- }
}
}
@@ -242,7 +215,7 @@ public function selectOrFail($data = null)
* 查找单条记录 如果不存在则抛出异常
* @access public
* @param array|string|Query|Closure $data 数据
- * @return array|Model|static
+ * @return array|Model|static|mixed
* @throws ModelNotFoundException
* @throws DataNotFoundException
*/
diff --git a/src/db/concern/TimeFieldQuery.php b/src/db/concern/TimeFieldQuery.php
index 1267e540..69b7eae4 100644
--- a/src/db/concern/TimeFieldQuery.php
+++ b/src/db/concern/TimeFieldQuery.php
@@ -182,7 +182,7 @@ public function whereBetweenTime(string $field, $startTime, $endTime, string $lo
public function whereNotBetweenTime(string $field, $startTime, $endTime)
{
return $this->whereTime($field, '<', $startTime)
- ->whereTime($field, '>', $endTime);
+ ->whereTime($field, '>', $endTime, 'OR');
}
/**
diff --git a/src/db/concern/WhereQuery.php b/src/db/concern/WhereQuery.php
index d2deb03c..ef845f5d 100644
--- a/src/db/concern/WhereQuery.php
+++ b/src/db/concern/WhereQuery.php
@@ -361,9 +361,7 @@ protected function parseWhereExp(string $logic, $field, $op, $condition, array $
$field = $this->options['via'] . '.' . $field;
}
- if ($field instanceof Raw) {
- return $this->whereRaw($field, is_array($op) ? $op : [], $logic);
- } elseif ($strict) {
+ if ($strict) {
// 使用严格模式查询
if ('=' == $op) {
$where = $this->whereEq($field, $condition);
diff --git a/src/db/connector/Sqlite.php b/src/db/connector/Sqlite.php
index c664f202..3e42a909 100644
--- a/src/db/connector/Sqlite.php
+++ b/src/db/connector/Sqlite.php
@@ -42,7 +42,7 @@ protected function parseDsn(array $config): string
public function getFields(string $tableName): array
{
[$tableName] = explode(' ', $tableName);
- $sql = 'PRAGMA table_info( ' . $tableName . ' )';
+ $sql = 'PRAGMA table_info( \'' . $tableName . '\' )';
$pdo = $this->getPDOStatement($sql);
$result = $pdo->fetchAll(PDO::FETCH_ASSOC);
diff --git a/src/model/Collection.php b/src/model/Collection.php
index f017e328..c8ff385a 100644
--- a/src/model/Collection.php
+++ b/src/model/Collection.php
@@ -157,7 +157,7 @@ public function setParent(Model $parent)
public function withAttr($name, $callback = null)
{
$this->each(function (Model $model) use ($name, $callback) {
- $model->withAttribute($name, $callback);
+ $model->withAttr($name, $callback);
});
return $this;
diff --git a/src/model/Relation.php b/src/model/Relation.php
index 97b6c595..358842d6 100644
--- a/src/model/Relation.php
+++ b/src/model/Relation.php
@@ -85,6 +85,12 @@ abstract class Relation
*/
protected $withoutField;
+ /**
+ * 默认数据
+ * @var mixed
+ */
+ protected $default;
+
/**
* 获取关联的所属模型
* @access public
@@ -234,6 +240,38 @@ public function withoutField($field)
return $this;
}
+ /**
+ * 设置关联数据不存在的时候默认值
+ * @access public
+ * @param mixed $data 默认值
+ * @return $this
+ */
+ public function withDefault($data = null)
+ {
+ $this->default = $data;
+ return $this;
+ }
+
+ /**
+ * 获取关联数据默认值
+ * @access protected
+ * @return mixed
+ */
+ protected function getDefaultModel()
+ {
+ if (is_array($this->default)) {
+ $model = (new $this->model)->data($this->default);
+ } elseif ($this->default instanceof Closure) {
+ $closure = $this->default;
+ $model = new $this->model;
+ $closure($model);
+ } else {
+ $model = $this->default;
+ }
+
+ return $model;
+ }
+
/**
* 判断闭包的参数类型
* @access protected
diff --git a/src/model/concern/Attribute.php b/src/model/concern/Attribute.php
index 34cb59a4..e4aa07b4 100644
--- a/src/model/concern/Attribute.php
+++ b/src/model/concern/Attribute.php
@@ -250,6 +250,17 @@ public function appendData(array $data, bool $set = false)
return $this;
}
+ /**
+ * 刷新对象原始数据(为当前数据)
+ * @access public
+ * @return $this
+ */
+ public function refreshOrigin()
+ {
+ $this->origin = $this->data;
+ return $this;
+ }
+
/**
* 获取对象原始数据 如果不存在指定字段返回null
* @access public
@@ -371,6 +382,9 @@ public function setAttr(string $name, $value, array $data = []): void
} elseif (isset($this->type[$name])) {
// 类型转换
$value = $this->writeTransform($value, $this->type[$name]);
+ } elseif (is_object($value) && method_exists($value, '__toString')) {
+ // 对象类型
+ $value = $value->__toString();
}
// 设置数据对象属性
@@ -531,6 +545,10 @@ protected function getValue(string $name, $value, $relation = false)
*/
protected function getJsonValue($name, $value)
{
+ if (is_null($value)) {
+ return $value;
+ }
+
foreach ($this->withAttr[$name] as $key => $closure) {
if ($this->jsonAssoc) {
$value[$key] = $closure($value[$key], $value);
@@ -633,11 +651,11 @@ protected function readTransform($value, $type)
* @param callable $callback 闭包获取器
* @return $this
*/
- public function withAttribute($name, callable $callback = null)
+ public function withAttr($name, callable $callback = null)
{
if (is_array($name)) {
foreach ($name as $key => $val) {
- $this->withAttribute($key, $val);
+ $this->withAttr($key, $val);
}
} else {
$name = $this->getRealFieldName($name);
diff --git a/src/model/concern/Conversion.php b/src/model/concern/Conversion.php
index 35d96d05..22d62565 100644
--- a/src/model/concern/Conversion.php
+++ b/src/model/concern/Conversion.php
@@ -330,6 +330,7 @@ public function __toString()
}
// JsonSerializable
+ #[\ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
diff --git a/src/model/concern/RelationShip.php b/src/model/concern/RelationShip.php
index 33c1b890..8faadf47 100644
--- a/src/model/concern/RelationShip.php
+++ b/src/model/concern/RelationShip.php
@@ -291,14 +291,13 @@ public function eagerlyResultSet(array &$resultSet, array $relations, array $wit
/**
* 预载入关联查询 返回模型对象
* @access public
- * @param Model $result 数据对象
* @param array $relations 关联
* @param array $withRelationAttr 关联获取器
* @param bool $join 是否为JOIN方式
* @param mixed $cache 关联缓存
* @return void
*/
- public function eagerlyResult(Model $result, array $relations, array $withRelationAttr = [], bool $join = false, $cache = false): void
+ public function eagerlyResult(array $relations, array $withRelationAttr = [], bool $join = false, $cache = false): void
{
foreach ($relations as $key => $relation) {
$subRelation = [];
@@ -333,7 +332,7 @@ public function eagerlyResult(Model $result, array $relations, array $withRelati
$relationCache = $cache[$relationName] ?? [];
}
- $relationResult->eagerlyResult($result, $relationName, $subRelation, $closure, $relationCache, $join);
+ $relationResult->eagerlyResult($this, $relationName, $subRelation, $closure, $relationCache, $join);
}
}
diff --git a/src/model/concern/SoftDelete.php b/src/model/concern/SoftDelete.php
index 8d76bb07..117f1ef7 100644
--- a/src/model/concern/SoftDelete.php
+++ b/src/model/concern/SoftDelete.php
@@ -55,6 +55,16 @@ public static function withTrashed(): Query
return $model->withTrashedData(true)->db();
}
+ /**
+ * 查询软删除数据
+ * @access public
+ * @return Query
+ */
+ public function queryWithTrashed(): Query
+ {
+ return $this->withTrashedData(true)->db();
+ }
+
/**
* 是否包含软删除数据
* @access protected
@@ -86,6 +96,23 @@ public static function onlyTrashed(): Query
return $model->db();
}
+ /**
+ * 只查询软删除数据
+ * @access public
+ * @return Query
+ */
+ public function queryOnlyTrashed(): Query
+ {
+ $field = $this->getDeleteTimeField(true);
+
+ if ($field) {
+ return $this->db()
+ ->useSoftDelete($field, $this->getWithTrashedExp());
+ }
+
+ return $this->db();
+ }
+
/**
* 获取软删除数据的查询条件
* @access protected
@@ -152,12 +179,12 @@ public function delete(): bool
public static function destroy($data, bool $force = false): bool
{
// 传入空值(包括空字符串和空数组)的时候不会做任何的数据删除操作,但传入0则是有效的
- if(empty($data) && $data !== 0){
+ if (empty($data) && 0 !== $data) {
return false;
}
// 仅当强制删除时包含软删除数据
$model = (new static());
- if($force){
+ if ($force) {
$model->withTrashedData(true);
}
$query = $model->db(false);
diff --git a/src/model/relation/BelongsTo.php b/src/model/relation/BelongsTo.php
index 7a590afd..0802b11a 100644
--- a/src/model/relation/BelongsTo.php
+++ b/src/model/relation/BelongsTo.php
@@ -73,6 +73,8 @@ public function getRelation(array $subRelation = [], Closure $closure = null)
}
$relationModel->setParent(clone $this->parent);
+ } else {
+ $relationModel = $this->getDefaultModel();
}
return $relationModel;
@@ -227,19 +229,18 @@ protected function eagerlySet(array &$resultSet, string $relation, array $subRel
foreach ($resultSet as $result) {
// 关联模型
if (!isset($data[$result->$foreignKey])) {
- $relationModel = null;
+ $relationModel = $this->getDefaultModel();
} else {
$relationModel = $data[$result->$foreignKey];
$relationModel->setParent(clone $result);
$relationModel->exists(true);
}
+ // 设置关联属性
+ $result->setRelation($relation, $relationModel);
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($result, $relationModel);
- } else {
- // 设置关联属性
- $result->setRelation($relation, $relationModel);
}
}
}
@@ -268,19 +269,19 @@ protected function eagerlyOne(Model $result, string $relation, array $subRelatio
// 关联模型
if (!isset($data[$result->$foreignKey])) {
- $relationModel = null;
+ $relationModel = $this->getDefaultModel();
} else {
$relationModel = $data[$result->$foreignKey];
$relationModel->setParent(clone $result);
$relationModel->exists(true);
}
+ // 设置关联属性
+ $result->setRelation($relation, $relationModel);
+
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($result, $relationModel);
- } else {
- // 设置关联属性
- $result->setRelation($relation, $relationModel);
}
}
diff --git a/src/model/relation/BelongsToMany.php b/src/model/relation/BelongsToMany.php
index 5f177c1d..98909068 100644
--- a/src/model/relation/BelongsToMany.php
+++ b/src/model/relation/BelongsToMany.php
@@ -19,7 +19,6 @@
use think\Model;
use think\model\Pivot;
use think\model\Relation;
-use think\Paginator;
/**
* 多对多关联类
@@ -120,31 +119,6 @@ protected function newPivot(array $data = []): Pivot
}
}
- /**
- * 合成中间表模型
- * @access protected
- * @param array|Collection|Paginator $models
- */
- protected function hydratePivot(iterable $models)
- {
- foreach ($models as $model) {
- $pivot = [];
-
- foreach ($model->getData() as $key => $val) {
- if (strpos($key, '__')) {
- [$name, $attr] = explode('__', $key, 2);
-
- if ('pivot' == $name) {
- $pivot[$attr] = $val;
- unset($model->$key);
- }
- }
- }
-
- $model->setRelation($this->pivotDataName, $this->newPivot($pivot));
- }
- }
-
/**
* 延迟获取关联数据
* @access public
@@ -158,62 +132,33 @@ public function getRelation(array $subRelation = [], Closure $closure = null): C
$closure($this->getClosureType($closure));
}
- $result = $this->relation($subRelation)
+ return $this->relation($subRelation)
->select()
->setParent(clone $this->parent);
-
- $this->hydratePivot($result);
-
- return $result;
- }
-
- /**
- * 重载select方法
- * @access public
- * @param mixed $data
- * @return Collection
- */
- public function select($data = null): Collection
- {
- $this->baseQuery();
- $result = $this->query->select($data);
- $this->hydratePivot($result);
-
- return $result;
- }
-
- /**
- * 重载paginate方法
- * @access public
- * @param int|array $listRows
- * @param int|bool $simple
- * @return Paginator
- */
- public function paginate($listRows = null, $simple = false): Paginator
- {
- $this->baseQuery();
- $result = $this->query->paginate($listRows, $simple);
- $this->hydratePivot($result);
-
- return $result;
}
/**
- * 重载find方法
+ * 组装Pivot模型
* @access public
- * @param mixed $data
- * @return Model
+ * @param Model $result 模型对象
+ * @return array
*/
- public function find($data = null)
+ protected function matchPivot(Model $result): array
{
- $this->baseQuery();
- $result = $this->query->find($data);
-
- if ($result && !$result->isEmpty()) {
- $this->hydratePivot([$result]);
+ $pivot = [];
+ foreach ($result->getData() as $key => $val) {
+ if (strpos($key, '__')) {
+ [$name, $attr] = explode('__', $key, 2);
+
+ if ('pivot' == $name) {
+ $pivot[$attr] = $val;
+ unset($result->$key);
+ }
+ }
}
- return $result;
+ $result->setRelation($this->pivotDataName, $this->newPivot($pivot));
+ return $pivot;
}
/**
@@ -405,24 +350,13 @@ protected function eagerlyManyToMany(array $where, array $subRelation = [], Clos
// 组装模型数据
$data = [];
foreach ($list as $set) {
- $pivot = [];
- foreach ($set->getData() as $key => $val) {
- if (strpos($key, '__')) {
- [$name, $attr] = explode('__', $key, 2);
- if ('pivot' == $name) {
- $pivot[$attr] = $val;
- unset($set->$key);
- }
- }
- }
- $key = $pivot[$this->localKey];
+ $pivot = $this->matchPivot($set);
+ $key = $pivot[$this->localKey];
if ($this->withLimit && isset($data[$key]) && count($data[$key]) >= $this->withLimit) {
continue;
}
- $set->setRelation($this->pivotDataName, $this->newPivot($pivot));
-
$data[$key][] = $set;
}
@@ -673,6 +607,10 @@ protected function baseQuery(): void
$foreignKey = $this->foreignKey;
$localKey = $this->localKey;
+ $this->query->filter(function ($result, $options) {
+ $this->matchPivot($result);
+ });
+
// 关联查询
if (null === $this->parent->getKey()) {
$condition = ['pivot.' . $localKey, 'exp', new Raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk())];
diff --git a/src/model/relation/HasOne.php b/src/model/relation/HasOne.php
index a37eca46..269f0d7f 100644
--- a/src/model/relation/HasOne.php
+++ b/src/model/relation/HasOne.php
@@ -72,6 +72,8 @@ public function getRelation(array $subRelation = [], Closure $closure = null)
}
$relationModel->setParent(clone $this->parent);
+ } else {
+ $relationModel = $this->getDefaultModel();
}
return $relationModel;
@@ -226,19 +228,18 @@ protected function eagerlySet(array &$resultSet, string $relation, array $subRel
foreach ($resultSet as $result) {
// 关联模型
if (!isset($data[$result->$localKey])) {
- $relationModel = null;
+ $relationModel = $this->getDefaultModel();
} else {
$relationModel = $data[$result->$localKey];
$relationModel->setParent(clone $result);
$relationModel->exists(true);
}
+ // 设置关联属性
+ $result->setRelation($relation, $relationModel);
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($result, $relationModel);
- } else {
- // 设置关联属性
- $result->setRelation($relation, $relationModel);
}
}
}
@@ -267,18 +268,19 @@ protected function eagerlyOne(Model $result, string $relation, array $subRelatio
// 关联模型
if (!isset($data[$result->$localKey])) {
- $relationModel = null;
+ $relationModel = $this->getDefaultModel();
} else {
$relationModel = $data[$result->$localKey];
$relationModel->setParent(clone $result);
$relationModel->exists(true);
}
+ // 设置关联属性
+ $result->setRelation($relation, $relationModel);
+
if (!empty($this->bindAttr)) {
// 绑定关联属性
$this->bindAttr($result, $relationModel);
- } else {
- $result->setRelation($relation, $relationModel);
}
}
diff --git a/src/model/relation/HasOneThrough.php b/src/model/relation/HasOneThrough.php
index 8ec42df4..0278533e 100644
--- a/src/model/relation/HasOneThrough.php
+++ b/src/model/relation/HasOneThrough.php
@@ -40,6 +40,8 @@ public function getRelation(array $subRelation = [], Closure $closure = null)
if ($relationModel) {
$relationModel->setParent(clone $this->parent);
+ } else {
+ $relationModel = $this->getDefaultModel();
}
return $relationModel;
@@ -79,7 +81,7 @@ public function eagerlyResultSet(array &$resultSet, string $relation, array $sub
foreach ($resultSet as $result) {
// 关联模型
if (!isset($data[$result->$localKey])) {
- $relationModel = null;
+ $relationModel = $this->getDefaultModel();
} else {
$relationModel = $data[$result->$localKey];
$relationModel->setParent(clone $result);
@@ -115,7 +117,7 @@ public function eagerlyResult(Model $result, string $relation, array $subRelatio
// 关联模型
if (!isset($data[$result->$localKey])) {
- $relationModel = null;
+ $relationModel = $this->getDefaultModel();
} else {
$relationModel = $data[$result->$localKey];
$relationModel->setParent(clone $result);
diff --git a/src/model/relation/MorphOne.php b/src/model/relation/MorphOne.php
index bc89c0ba..788dd295 100644
--- a/src/model/relation/MorphOne.php
+++ b/src/model/relation/MorphOne.php
@@ -90,6 +90,8 @@ public function getRelation(array $subRelation = [], Closure $closure = null)
}
$relationModel->setParent(clone $this->parent);
+ } else {
+ $relationModel = $this->getDefaultModel();
}
return $relationModel;
@@ -158,7 +160,7 @@ public function eagerlyResultSet(array &$resultSet, string $relation, array $sub
// 关联数据封装
foreach ($resultSet as $result) {
if (!isset($data[$result->$pk])) {
- $relationModel = null;
+ $relationModel = $this->getDefaultModel();
} else {
$relationModel = $data[$result->$pk];
$relationModel->setParent(clone $result);
@@ -202,7 +204,7 @@ public function eagerlyResult(Model $result, string $relation, array $subRelatio
$relationModel->setParent(clone $result);
$relationModel->exists(true);
} else {
- $relationModel = null;
+ $relationModel = $this->getDefaultModel();
}
if (!empty($this->bindAttr)) {