From 082b3bea816178b092e45361eb1a6a8dd747d682 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Tue, 13 Dec 2016 11:43:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=E4=B8=80=E5=AF=B9=E4=B8=80?= =?UTF-8?q?=E5=85=B3=E8=81=94=E9=A2=84=E8=BD=BD=E5=85=A5=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=20=E6=94=AF=E6=8C=81=E4=BD=BF=E7=94=A8IN=E6=96=B9=E5=BC=8F=20?= =?UTF-8?q?=E4=B8=80=E5=AF=B9=E4=B8=80=E5=85=B3=E8=81=94=E7=B1=BB=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0setEagerlyType=E6=96=B9=E6=B3=95=E7=94=A8=E4=BA=8E?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=A2=84=E8=BD=BD=E5=85=A5=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=96=B9=E5=BC=8F=20=E9=BB=98=E8=AE=A4=E4=B8=BAJOIN=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=EF=BC=8CsetEagerlyType(1)=20=E4=BD=BF=E7=94=A8IN?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Loader类的parseName方法增加第三个参数 用于驼峰法转换是否首字母大写 --- library/think/Loader.php | 8 +- library/think/Model.php | 6 +- library/think/db/Query.php | 7 +- library/think/model/relation/BelongsTo.php | 66 +++++++++++++- library/think/model/relation/HasOne.php | 65 ++++++++++++++ library/think/model/relation/OneToOne.php | 100 +++++++++++++++++---- 6 files changed, 222 insertions(+), 30 deletions(-) diff --git a/library/think/Loader.php b/library/think/Loader.php index 9f54ad3a..57f6a41f 100644 --- a/library/think/Loader.php +++ b/library/think/Loader.php @@ -494,14 +494,16 @@ class Loader * type 0 将Java风格转换为C的风格 1 将C风格转换为Java的风格 * @param string $name 字符串 * @param integer $type 转换类型 + * @param bool $ucfirst 首字母是否大写(驼峰规则) * @return string */ - public static function parseName($name, $type = 0) + public static function parseName($name, $type = 0, $ucfirst = true) { if ($type) { - return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function ($match) { + $name = preg_replace_callback('/_([a-zA-Z])/', function ($match) { return strtoupper($match[1]); - }, $name)); + }, $name); + return $ucfirst ? ucfirst($name) : lcfirst($name); } else { return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_")); } diff --git a/library/think/Model.php b/library/think/Model.php index 52c003cc..d40c6218 100644 --- a/library/think/Model.php +++ b/library/think/Model.php @@ -414,7 +414,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess // 类型转换 $value = $this->readTransform($value, $this->type[$name]); } elseif ($notFound) { - $method = Loader::parseName($name, 1); + $method = Loader::parseName($name, 1, false); if (method_exists($this, $method) && $this->$method() instanceof Relation) { // 不存在该字段 获取关联数据 $value = $this->$method()->getRelation(); @@ -1245,7 +1245,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation); } - $relation = Loader::parseName($relation, 1); + $relation = Loader::parseName($relation, 1, false); $this->$relation()->eagerlyResultSet($resultSet, $relation, $subRelation, $closure, $class); } } @@ -1272,7 +1272,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess if (strpos($relation, '.')) { list($relation, $subRelation) = explode('.', $relation); } - $relation = Loader::parseName($relation, 1); + $relation = Loader::parseName($relation, 1, false); $this->$relation()->eagerlyResult($result, $relation, $subRelation, $closure, $class); } } diff --git a/library/think/db/Query.php b/library/think/db/Query.php index 43a5b07c..ace9a90a 100644 --- a/library/think/db/Query.php +++ b/library/think/db/Query.php @@ -27,8 +27,7 @@ use think\exception\PDOException; use think\Loader; use think\Model; use think\model\Relation; -use think\model\relation\BelongsTo; -use think\model\relation\HasOne; +use think\model\relation\OneToOne; use think\Paginator; class Query @@ -1653,9 +1652,9 @@ class Query } /** @var Relation $model */ - $relation = Loader::parseName($relation, 1); + $relation = Loader::parseName($relation, 1, false); $model = $class->$relation(); - if ($model instanceof HasOne || $model instanceof BelongsTo) { + if ($model instanceof OneToOne && 0 == $model->getEagerlyType()) { $model->eagerly($this, $relation, $subRelation, $closure, $first); $first = false; } elseif ($closure) { diff --git a/library/think/model/relation/BelongsTo.php b/library/think/model/relation/BelongsTo.php index ffab752d..7626caf7 100644 --- a/library/think/model/relation/BelongsTo.php +++ b/library/think/model/relation/BelongsTo.php @@ -22,7 +22,7 @@ class BelongsTo extends OneToOne * @param Model $parent 上级模型对象 * @param string $model 模型名 * @param string $foreignKey 关联外键 - * @param string $otherKey 关联主键 + * @param string $localKey 关联主键 * @param array $alias 别名定义 * @param string $joinType JOIN类型 */ @@ -48,4 +48,68 @@ class BelongsTo extends OneToOne return $this->query->where($localKey, $this->parent->$foreignKey)->find(); } + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 + * @return void + */ + protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure, $class) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$foreignKey)) { + $range[] = $result->$foreignKey; + } + } + + if (!empty($range)) { + $this->where[$localKey] = ['in', $range]; + $data = $this->eagerlyWhere($this, [ + $localKey => [ + 'in', + $range, + ], + ], $localKey, $relation, $subRelation, $closure); + + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$foreignKey])) { + $data[$result->$foreignKey] = []; + } + $result->setAttr($relation, $this->resultSetBuild($data[$result->$foreignKey], $class)); + } + } + } + + /** + * 预载入关联查询(数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 + * @return void + */ + protected function eagerlyOne(&$result, $relation, $subRelation, $closure, $class) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + $data = $this->eagerlyWhere($this, [$localKey => $result->$foreignKey], $localKey, $relation, $subRelation, $closure); + // 关联数据封装 + if (!isset($data[$result->$foreignKey])) { + $data[$result->$foreignKey] = []; + } + $result->setAttr($relation, $this->resultSetBuild($data[$result->$foreignKey], $class)); + } + } diff --git a/library/think/model/relation/HasOne.php b/library/think/model/relation/HasOne.php index 5f75afa4..e76fbbaf 100644 --- a/library/think/model/relation/HasOne.php +++ b/library/think/model/relation/HasOne.php @@ -73,4 +73,69 @@ class HasOne extends OneToOne ->join($table . ' b', 'a.' . $this->localKey . '=b.' . $this->foreignKey, $this->joinType) ->where($where); } + + /** + * 预载入关联查询(数据集) + * @access public + * @param array $resultSet 数据集 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 + * @return void + */ + protected function eagerlySet(&$resultSet, $relation, $subRelation, $closure, $class) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + + $range = []; + foreach ($resultSet as $result) { + // 获取关联外键列表 + if (isset($result->$localKey)) { + $range[] = $result->$localKey; + } + } + + if (!empty($range)) { + $this->where[$foreignKey] = ['in', $range]; + $data = $this->eagerlyWhere($this, [ + $foreignKey => [ + 'in', + $range, + ], + ], $foreignKey, $relation, $subRelation, $closure); + + // 关联数据封装 + foreach ($resultSet as $result) { + if (!isset($data[$result->$localKey])) { + $data[$result->$localKey] = []; + } + $result->setAttr($relation, $this->resultSetBuild($data[$result->$localKey], $class)); + } + } + } + + /** + * 预载入关联查询(数据) + * @access public + * @param Model $result 数据对象 + * @param string $relation 当前关联名 + * @param string $subRelation 子关联名 + * @param \Closure $closure 闭包 + * @param string $class 数据集对象名 为空表示数组 + * @return void + */ + protected function eagerlyOne(&$result, $relation, $subRelation, $closure, $class) + { + $localKey = $this->localKey; + $foreignKey = $this->foreignKey; + $data = $this->eagerlyWhere($this, [$foreignKey => $result->$localKey], $foreignKey, $relation, $subRelation, $closure); + // 关联数据封装 + if (!isset($data[$result->$localKey])) { + $data[$result->$localKey] = []; + } + $result->setAttr($relation, $this->resultSetBuild($data[$result->$localKey], $class)); + } + } diff --git a/library/think/model/relation/OneToOne.php b/library/think/model/relation/OneToOne.php index 650000e9..354d718d 100644 --- a/library/think/model/relation/OneToOne.php +++ b/library/think/model/relation/OneToOne.php @@ -19,8 +19,11 @@ use think\model\relation\BelongsTo; abstract class OneToOne extends Relation { + // 预载入方式 + protected $eagerlyType = 0; + /** - * 预载入关联查询 + * 预载入关联查询(JOIN方式) * @access public * @param Query $query 查询对象 * @param string $relation 关联名 @@ -76,7 +79,7 @@ abstract class OneToOne extends Relation } /** - * 预载入关联查询 + * 预载入关联查询(数据集) * @access public * @param array $resultSet 数据集 * @param string $relation 当前关联名 @@ -85,16 +88,21 @@ abstract class OneToOne extends Relation * @param string $class 数据集对象名 为空表示数组 * @return void */ - public function eagerlyResultSet(&$resultSet, $relation) + public function eagerlyResultSet(&$resultSet, $relation, $subRelation, $closure, $class) { - foreach ($resultSet as $result) { + if (1 == $this->eagerlyType) { + // IN查询 + $this->eagerlySet($resultSet, $relation, $subRelation, $closure, $class); + } else { // 模型关联组装 - $this->match($this->model, $relation, $result); + foreach ($resultSet as $result) { + $this->match($this->model, $relation, $result); + } } } /** - * 预载入关联查询 返回模型对象 + * 预载入关联查询(数据) * @access public * @param Model $result 数据对象 * @param string $relation 当前关联名 @@ -103,10 +111,54 @@ abstract class OneToOne extends Relation * @param string $class 数据集对象名 为空表示数组 * @return void */ - public function eagerlyResult(&$result, $relation) + public function eagerlyResult(&$result, $relation, $subRelation, $closure, $class) { - // 模型关联组装 - $this->match($this->model, $relation, $result); + if (1 == $this->eagerlyType) { + // IN查询 + $this->eagerlyOne($result, $relation, $subRelation, $closure, $class); + } else { + // 模型关联组装 + $this->match($this->model, $relation, $result); + } + } + + /** + * 保存(新增)当前关联数据对象 + * @access public + * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 + * @return integer + */ + public function save($data) + { + if ($data instanceof Model) { + $data = $data->getData(); + } + // 保存关联表数据 + $data[$this->foreignKey] = $this->parent->{$this->localKey}; + $model = new $this->model; + return $model->save($data); + } + + /** + * 设置预载入方式 + * @access public + * @param integer $type 预载入方式 0 JOIN查询 1 IN查询 + * @return this + */ + public function setEagerlyType($type) + { + $this->eagerlyType = $type; + return $this; + } + + /** + * 获取预载入方式 + * @access public + * @return integer + */ + public function getEagerlyType() + { + return $this->eagerlyType; } /** @@ -134,20 +186,30 @@ abstract class OneToOne extends Relation } /** - * 保存(新增)当前关联数据对象 + * 一对一 关联模型预查询(IN方式) * @access public - * @param mixed $data 数据 可以使用数组 关联模型对象 和 关联对象的主键 - * @return integer + * @param object $model 关联模型对象 + * @param array $where 关联预查询条件 + * @param string $key 关联键名 + * @param string $relation 关联名 + * @param string $subRelation 子关联 + * @param bool $closure + * @return array */ - public function save($data) + protected function eagerlyWhere($model, $where, $key, $relation, $subRelation = '', $closure = false) { - if ($data instanceof Model) { - $data = $data->getData(); + // 预载入关联查询 支持嵌套预载入 + if ($closure) { + call_user_func_array($closure, [ & $model]); } - // 保存关联表数据 - $data[$this->foreignKey] = $this->parent->{$this->localKey}; - $model = new $this->model; - return $model->save($data); + $list = $model->where($where)->with($subRelation)->select(); + + // 组装模型数据 + $data = []; + foreach ($list as $set) { + $data[$set->$key][] = $set; + } + return $data; } /**