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)) {