From a8b6e5e2156fa3fa45ee2e05ebda3dea08cca397 Mon Sep 17 00:00:00 2001 From: Yurun Date: Fri, 25 Oct 2019 11:08:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E5=99=A8=E5=88=AB=E5=90=8D=EF=BC=8C=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=99=A8=E8=BF=9E=E8=B4=AF=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=9E=84=E5=BB=BA=E6=AD=A5=E9=AA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Db/Query/Builder/InsertBuilder.php | 2 +- src/Db/Query/Builder/ReplaceBuilder.php | 2 +- src/Db/Query/Builder/UpdateBuilder.php | 2 +- src/Db/Query/Interfaces/IQuery.php | 14 ++ src/Db/Query/Query.php | 129 ++++++++++++++++-- src/Db/Query/QueryOption.php | 6 + src/Model/Model.php | 105 +++++++++++--- .../Component/PHPUintListener/ImiListener.php | 1 + tests/unit/Component/Tests/ModelTest.php | 9 ++ 9 files changed, 236 insertions(+), 34 deletions(-) diff --git a/src/Db/Query/Builder/InsertBuilder.php b/src/Db/Query/Builder/InsertBuilder.php index 175d7d7b1d..b0c37aad3b 100644 --- a/src/Db/Query/Builder/InsertBuilder.php +++ b/src/Db/Query/Builder/InsertBuilder.php @@ -38,7 +38,7 @@ public function build(...$args) else { $fields[] = $this->parseKeyword($k); - $valueParam = $this->query->getAutoParamName(); + $valueParam = ':' . $k; $valueParams[] = $valueParam; $this->params[$valueParam] = $v; } diff --git a/src/Db/Query/Builder/ReplaceBuilder.php b/src/Db/Query/Builder/ReplaceBuilder.php index 1f235e5161..3d9051524b 100644 --- a/src/Db/Query/Builder/ReplaceBuilder.php +++ b/src/Db/Query/Builder/ReplaceBuilder.php @@ -42,7 +42,7 @@ public function build(...$args) } else { - $valueParam = $this->query->getAutoParamName(); + $valueParam = ':' . $k; $this->params[$valueParam] = $v; $setStrs[] = $this->parseKeyword($k) . ' = ' . $valueParam; } diff --git a/src/Db/Query/Builder/UpdateBuilder.php b/src/Db/Query/Builder/UpdateBuilder.php index 8212536c75..b08bd76dae 100644 --- a/src/Db/Query/Builder/UpdateBuilder.php +++ b/src/Db/Query/Builder/UpdateBuilder.php @@ -34,7 +34,7 @@ public function build(...$args) } else { - $valueParam = $this->query->getAutoParamName(); + $valueParam = ':' . $k; $valueParams[] = $valueParam; $this->params[$valueParam] = $v; $setStrs[] = $this->parseKeyword($k) . ' = ' . $valueParam; diff --git a/src/Db/Query/Interfaces/IQuery.php b/src/Db/Query/Interfaces/IQuery.php index 364c727d20..4b879b5047 100644 --- a/src/Db/Query/Interfaces/IQuery.php +++ b/src/Db/Query/Interfaces/IQuery.php @@ -594,5 +594,19 @@ public function setFieldInc($fieldName, float $incValue = 1); */ public function setFieldDec($fieldName, float $decValue = 1); + /** + * 获取自动起名的参数名称 + * @return string + */ public function getAutoParamName(); + + /** + * 查询器别名 + * + * @param string $name + * @param callable $callable + * @return static + */ + public function alias($name, $callable); + } \ No newline at end of file diff --git a/src/Db/Query/Query.php b/src/Db/Query/Query.php index 942e840cde..2b5e946e76 100644 --- a/src/Db/Query/Query.php +++ b/src/Db/Query/Query.php @@ -91,6 +91,27 @@ class Query implements IQuery */ protected $dbParamInc = 0; + /** + * 查询器别名集合 + * + * @var \Imi\Db\Query\Interfaces\IQuery[] + */ + protected static $aliasMap = []; + + /** + * 当前别名 + * + * @var string + */ + protected $alias; + + /** + * 别名 Sql 集合 + * + * @var string[] + */ + protected static $aliasSqls = []; + public function __construct(IDb $db = null, $modelClass = null, $poolName = null, $queryType = null) { $this->db = $db; @@ -111,6 +132,13 @@ public function __init() } } + public function __clone() + { + $this->isInitDb = false; + $this->db = null; + $this->option = clone $this->option; + } + /** * 获取所有操作的记录 * @return QueryOption @@ -856,8 +884,15 @@ public function getBinds() */ public function select(): IResult { - $builder = new SelectBuilder($this); - $sql = $builder->build(); + if($this->alias && isset(static::$aliasSqls[$this->alias])) + { + $sql = static::$aliasSqls[$this->alias]; + } + else + { + $builder = new SelectBuilder($this); + static::$aliasSqls[$this->alias] = $sql = $builder->build(); + } if(!$this->isInitQueryType && !$this->isInTransaction()) { $this->queryType = QueryType::READ; @@ -899,8 +934,29 @@ public function paginate($page, $count, $options = []): IPaginateResult */ public function insert($data = null): IResult { - $builder = new InsertBuilder($this); - $sql = $builder->build($data); + if($this->alias && isset(static::$aliasSqls[$this->alias])) + { + $sql = static::$aliasSqls[$this->alias]; + $bindValues = []; + $numberKey = isset($data[0]); + foreach($data as $k => $v) + { + if($numberKey) + { + $bindValues[':' . ($k + 1)] = $v; + } + else + { + $bindValues[':' . $k] = $v; + } + } + $this->bindValues($bindValues); + } + else + { + $builder = new InsertBuilder($this); + static::$aliasSqls[$this->alias] = $sql = $builder->build($data); + } return $this->execute($sql); } @@ -926,8 +982,21 @@ public function batchInsert($data = null): IResult */ public function update($data = null): IResult { - $builder = new UpdateBuilder($this); - $sql = $builder->build($data); + if($this->alias && isset(static::$aliasSqls[$this->alias])) + { + $sql = static::$aliasSqls[$this->alias]; + $bindValues = []; + foreach($data as $k => $v) + { + $bindValues[':' . $k] = $v; + } + $this->bindValues($bindValues); + } + else + { + $builder = new UpdateBuilder($this); + static::$aliasSqls[$this->alias] = $sql = $builder->build($data); + } return $this->execute($sql); } @@ -939,8 +1008,21 @@ public function update($data = null): IResult */ public function replace($data = null): IResult { - $builder = new ReplaceBuilder($this); - $sql = $builder->build($data); + if($this->alias && isset(static::$aliasSqls[$this->alias])) + { + $sql = static::$aliasSqls[$this->alias]; + $bindValues = []; + foreach($data as $k => $v) + { + $bindValues[':' . $k] = $v; + } + $this->bindValues($bindValues); + } + else + { + $builder = new ReplaceBuilder($this); + static::$aliasSqls[$this->alias] = $sql = $builder->build($data); + } return $this->execute($sql); } @@ -950,10 +1032,16 @@ public function replace($data = null): IResult */ public function delete(): IResult { - $builder = new DeleteBuilder($this); - $sql = $builder->build(); + if($this->alias && isset(static::$aliasSqls[$this->alias])) + { + $sql = static::$aliasSqls[$this->alias]; + } + else + { + $builder = new DeleteBuilder($this); + static::$aliasSqls[$this->alias] = $sql = $builder->build(); + } $result = $this->execute($sql); - $this->__init(); return $result; } @@ -1158,4 +1246,23 @@ private function isInTransaction() return false; } } + + /** + * 查询器别名 + * + * @param string $name + * @param callable $callable + * @return static + */ + public function alias($name, $callable) + { + if(!isset(static::$aliasMap[$name])) + { + $callable($this); + $this->alias = $name; + static::$aliasMap[$name] = $this; + } + return clone static::$aliasMap[$name]; + } + } \ No newline at end of file diff --git a/src/Db/Query/QueryOption.php b/src/Db/Query/QueryOption.php index 85f5eb9ebe..5e4b920c66 100644 --- a/src/Db/Query/QueryOption.php +++ b/src/Db/Query/QueryOption.php @@ -74,4 +74,10 @@ public function __construct() { $this->table = new Table(); } + + public function __clone() + { + $this->table = clone $this->table; + } + } \ No newline at end of file diff --git a/src/Model/Model.php b/src/Model/Model.php index 445d0ddc94..7274f8cdd9 100644 --- a/src/Model/Model.php +++ b/src/Model/Model.php @@ -85,22 +85,41 @@ public static function find(...$ids) if(is_array($ids[0])) { // 键值数组where条件 + $keys = []; + $bindValues = []; foreach($ids[0] as $k => $v) { - $query->where($k, '=', $v); + $keys[] = $k; + $bindValues[':' . $k] = $v; } + $query = $query->alias($realClassName . ':find:pk:' . md5(implode(',', $keys)), function(IQuery $query) use($keys){ + foreach($keys as $name) + { + $query->whereRaw($name . '=:' . $name); + } + })->bindValues($bindValues); } else { - // 主键值 - foreach(static::__getMeta()->getId() as $i => $idName) - { - if(!isset($ids[$i])) + do { + // 主键值 + $id = static::__getMeta()->getId(); + $bindValues = []; + foreach($id as $i => $idName) { - break; + if(!isset($ids[$i])) + { + break 2; + } + $bindValues[':' . $idName] = $ids[$i]; } - $query->where($idName, '=', $ids[$i]); - } + $query = $query->alias($realClassName . ':find:pk', function(IQuery $query) use($id){ + foreach($id as $idName) + { + $query->whereRaw($idName . '=:' . $idName); + } + })->bindValues($bindValues); + } while(0); } } @@ -171,7 +190,14 @@ public function insert($data = null): IResult 'query' => $query, ], $this, \Imi\Model\Event\Param\BeforeInsertEventParam::class); - $result = $query->insert($data); + $keys = []; + foreach($data as $k => $v) + { + $keys[] = $k; + } + $result = $query->alias($this->__realClass . ':insert:' . md5(implode(',', $keys)), function(IQuery $query){ + + })->insert($data); if($result->isSuccess()) { foreach($this->__meta->getFields() as $name => $column) @@ -209,7 +235,6 @@ public function insert($data = null): IResult public function update($data = null): IResult { $query = static::query($this); - $query = $this->parseWhereId($query); if(null === $data) { $data = static::parseSaveData(\iterator_to_array($this), 'update', $this); @@ -226,12 +251,30 @@ public function update($data = null): IResult 'query' => $query, ], $this, \Imi\Model\Event\Param\BeforeUpdateEventParam::class); - if(!isset($query->getOption()->where[0])) + $bindValues = []; + foreach($this->__meta->getId() as $idName) + { + if(isset($this->$idName)) + { + $bindValues[$idName] = $this->$idName; + } + } + if(empty($bindValues)) { throw new \RuntimeException('use Model->update(), primary key can not be null'); } - - $result = $query->update($data); + foreach($data as $k => $v) + { + $bindValues[$k] = $v; + } + $keys = []; + foreach($data as $k => $v) + { + $keys[] = $k; + } + $result = $query->alias($this->__realClass . ':update:' . md5(implode(',', $keys)), function(IQuery $query){ + $this->parseWhereId($query, true); + })->update($bindValues); // 更新后 $this->trigger(ModelEvents::AFTER_UPDATE, [ @@ -304,7 +347,6 @@ public static function updateBatch($data, $where = null): IResult public function save(): IResult { $query = static::query($this); - $query = $this->parseWhereId($query); $data = static::parseSaveData(\iterator_to_array($this), 'save', $this); // 保存前 @@ -314,7 +356,14 @@ public function save(): IResult 'query' => $query, ], $this, \Imi\Model\Event\Param\BeforeSaveEventParam::class); - $result = $query->replace($data); + $keys = []; + foreach($data as $k => $v) + { + $keys[] = $k; + } + $result = $query->alias($this->__realClass . ':save:' . md5(implode(',', $keys)), function(IQuery $query){ + $this->parseWhereId($query, true); + })->replace($data); if($result->isSuccess()) { foreach($this->__meta->getFields() as $name => $column) @@ -347,7 +396,6 @@ public function save(): IResult public function delete(): IResult { $query = static::query($this); - $query = $this->parseWhereId($query); // 删除前 $this->trigger(ModelEvents::BEFORE_DELETE, [ @@ -355,11 +403,21 @@ public function delete(): IResult 'query' => $query, ], $this, \Imi\Model\Event\Param\BeforeDeleteEventParam::class); - if(!isset($query->getOption()->where[0])) + $bindValues = []; + foreach($this->__meta->getId() as $idName) + { + if(isset($this->$idName)) + { + $bindValues[$idName] = $this->$idName; + } + } + if(empty($bindValues)) { throw new \RuntimeException('use Model->delete(), primary key can not be null'); } - $result = $query->delete(); + $result = $query->alias($this->__realClass . ':delete', function(IQuery $query){ + $this->parseWhereId($query, true); + })->bindValues($bindValues)->delete(); // 删除后 $this->trigger(ModelEvents::AFTER_DELETE, [ @@ -510,14 +568,21 @@ public static function aggregate($functionName, $fieldName, callable $queryCalla * @param IQuery $query * @return IQuery */ - private function parseWhereId(IQuery $query) + private function parseWhereId(IQuery $query, $raw = false) { // 主键条件加入 foreach($this->__meta->getId() as $idName) { if(isset($this->$idName)) { - $query->where($idName, '=', $this->$idName); + if($raw) + { + $query->whereRaw($idName . '=:' . $idName); + } + else + { + $query->where($idName, '=', $this->$idName); + } } } return $query; diff --git a/tests/unit/Component/PHPUintListener/ImiListener.php b/tests/unit/Component/PHPUintListener/ImiListener.php index 27c9b1fa35..722d7d82ac 100644 --- a/tests/unit/Component/PHPUintListener/ImiListener.php +++ b/tests/unit/Component/PHPUintListener/ImiListener.php @@ -13,6 +13,7 @@ use Imi\Pool\PoolManager; use Imi\Db\Interfaces\IDb; use Imi\Test\BaseTest; +use Imi\Test\Component\Model\Article; class ImiListener implements TestListener { diff --git a/tests/unit/Component/Tests/ModelTest.php b/tests/unit/Component/Tests/ModelTest.php index 12b4b81b5b..1c66790336 100644 --- a/tests/unit/Component/Tests/ModelTest.php +++ b/tests/unit/Component/Tests/ModelTest.php @@ -94,6 +94,15 @@ public function testFind() 'username' => '1', 'password' => '2', ], $member->toArray()); + + $member = Member::find([ + 'id' => 1, + ]); + $this->assertEquals([ + 'id' => 1, + 'username' => '1', + 'password' => '2', + ], $member->toArray()); } public function testSelect()