This commit is contained in:
2023-12-13 14:47:23 +08:00
parent 85eaeba8ba
commit 3179e95a82
15 changed files with 467 additions and 1019 deletions
+51 -135
View File
@@ -9,8 +9,10 @@ declare(strict_types=1);
namespace Database; namespace Database;
use Closure;
use Database\Traits\QueryTrait; use Database\Traits\QueryTrait;
use JetBrains\PhpStorm\ArrayShape; use Kiri\Di\Context;
use Swoole\Coroutine;
/** /**
* Class ActiveQuery * Class ActiveQuery
@@ -21,63 +23,9 @@ class ActiveQuery extends QueryTrait implements ISqlBuilder
public bool $asArray = FALSE; public bool $asArray = FALSE;
public ?Connection $db = NULL;
public array $attributes = [];
protected mixed $_mock = null; protected mixed $_mock = null;
/**
* Comply constructor.
* @param $model
* @throws
*/
public function __construct($model)
{
$this->modelClass = $model;
$this->builder = SqlBuilder::builder($this);
parent::__construct();
}
/**
* @param string $key
* @param mixed $value
* @return $this
*/
public function addParam(string $key, mixed $value): static
{
$this->attributes[$key] = $value;
return $this;
}
/**
* @param int $size
* @param int $page
* @return array
* @throws
*/
#[ArrayShape([])]
public function pagination(int $size = 20, int $page = 1): array
{
$page = max(1, $page);
$size = max(1, $size);
$offset = ($page - 1) * $size;
$count = $this->count();
$lists = $this->offset($offset)->limit($size)->get()->toArray();
return [
'code' => 0,
'message' => 'ok',
'size' => $size,
'page' => $page,
'count' => $count,
'next' => max($page + 1, 1),
'prev' => max($page - 1, 1),
'param' => $lists,
];
}
/** /**
* @param bool $asArray * @param bool $asArray
* @return static * @return static
@@ -88,19 +36,6 @@ class ActiveQuery extends QueryTrait implements ISqlBuilder
return $this; return $this;
} }
/**
* @param array $values
* @return $this
*/
public function addParams(array $values): static
{
foreach ($values as $key => $val) {
$this->addParam($key, $val);
}
return $this;
}
/** /**
* @param array $methods * @param array $methods
* @return $this * @return $this
@@ -113,90 +48,67 @@ class ActiveQuery extends QueryTrait implements ISqlBuilder
/** /**
* @param $sql * @return ModelInterface|array|null|bool
* @param array $params
* @return mixed
* @throws * @throws
*/ */
public function execute($sql, array $params = []): Command public function first(): ModelInterface|null|array|bool
{ {
return $this->modelClass->getConnection()->createCommand($sql, $params); $data = $this->buildCommand($this->builder->one())->one();
}
/**
* @return ModelInterface|array|null
* @throws
*/
public function first(): ModelInterface|null|array
{
$data = $this->limit(1)->execute($this->builder->one(), $this->attributes)->one();
if (is_array($data)) { if (is_array($data)) {
return $this->populate($data); return $this->populate($data);
} else {
return NULL;
} }
return $data;
} }
/** /**
* @return string * @return bool|Collection
* @throws
*/ */
public function toSql(): string public function get(): bool|Collection
{ {
return $this->builder->get(); $data = $this->buildCommand($this->builder->all())->all();
} if (is_array($data)) {
return new Collection($this, $this->modelClass, $data);
/**
* @return Collection
* @throws
*/
public function get(): Collection
{
$data = $this->execute($this->builder->all(), $this->attributes)->all();
if ($data !== false) {
return new Collection($this, $data, $this->modelClass);
} else {
return new Collection($this, [], $this->modelClass);
} }
return false;
} }
/** /**
* @throws * @throws
*/ */
public function flush(): array|bool|int|string|null public function flush(): bool
{ {
return $this->execute($this->builder->truncate())->exec(); return (bool)$this->buildCommand($this->builder->truncate())->exec();
} }
/** /**
* @param int $size * @param int $size
* @param callable $callback * @param Closure $closure
* @param int $offset * @return void
* @return Pagination
* @throws
*/ */
public function page(int $size, callable $callback, int $offset = 0): Pagination public function chunk(int $size, Closure $closure): void
{ {
$pagination = new Pagination($this); $data = $this->offset($this->offset)->limit($size)->get();
$pagination->setOffset($offset); if (!$data || $data->isEmpty()) {
$pagination->setLimit($size); return;
$pagination->setCallback($callback); }
return $pagination; if (Context::inCoroutine()) {
Coroutine::create(fn() => $closure($data));
} else {
call_user_func($closure, $data);
}
$this->offset += $size;
$this->chunk($size, $closure);
} }
/** /**
* @param string $field * @param string $field
* @param string $setKey * @param string|null $setKey
* *
* @return array|null * @return array|null
* @throws
*/ */
public function column(string $field, string $setKey = ''): ?array public function column(string $field, ?string $setKey = null): ?array
{ {
return $this->get()->column($field, $setKey); return $this->get()->column($field, $setKey);
} }
@@ -241,7 +153,7 @@ class ActiveQuery extends QueryTrait implements ISqlBuilder
*/ */
public function count(): int public function count(): int
{ {
return $this->execute($this->builder->count(), $this->attributes)->one()['row_count'] ?? 0; return $this->buildCommand($this->builder->count())->rowCount();
} }
@@ -257,7 +169,7 @@ class ActiveQuery extends QueryTrait implements ISqlBuilder
} }
$generate = $this->builder->update($data); $generate = $this->builder->update($data);
if (!is_bool($generate)) { if (!is_bool($generate)) {
return (bool)$this->execute($generate, $this->attributes)->exec(); return (bool)$this->buildCommand($generate)->exec();
} else { } else {
return $generate; return $generate;
} }
@@ -272,16 +184,16 @@ class ActiveQuery extends QueryTrait implements ISqlBuilder
{ {
[$sql, $params] = $this->builder->insert($data, TRUE); [$sql, $params] = $this->builder->insert($data, TRUE);
return (bool)$this->execute($sql, $params)->exec(); return (bool)$this->buildCommand($sql, $params)->exec();
} }
/** /**
* @param $filed * @param $filed
* *
* @return null * @return mixed
* @throws * @throws
*/ */
public function value($filed) public function value($filed): mixed
{ {
return $this->first()[$filed] ?? NULL; return $this->first()[$filed] ?? NULL;
} }
@@ -292,22 +204,26 @@ class ActiveQuery extends QueryTrait implements ISqlBuilder
*/ */
public function exists(): bool public function exists(): bool
{ {
return !empty($this->execute($this->builder->one(), $this->attributes)->fetchColumn()); return $this->buildCommand($this->builder->one())->rowCount() > 0;
} }
/** /**
* @param bool $getSql * @param string $sql
* @return bool|string * @param array $params
* @throws * @return int|bool
*/ */
public function delete(bool $getSql = FALSE): bool|string public function execute(string $sql, array $params = []): int|bool
{ {
$sql = $this->builder->delete(); return $this->buildCommand($sql, $params)->exec();
if ($getSql === FALSE) { }
return (bool)$this->execute($sql, $this->attributes)->delete();
} else {
return $sql; /**
} * @return bool
*/
public function delete(): bool
{
return $this->buildCommand($this->builder->delete())->delete();
} }
} }
+7 -20
View File
@@ -13,7 +13,6 @@ namespace Database\Base;
use ArrayIterator; use ArrayIterator;
use Database\ActiveQuery; use Database\ActiveQuery;
use Database\ModelInterface; use Database\ModelInterface;
use Exception;
use JetBrains\PhpStorm\Pure; use JetBrains\PhpStorm\Pure;
use Kiri\Abstracts\Component; use Kiri\Abstracts\Component;
use ReturnTypeWillChange; use ReturnTypeWillChange;
@@ -29,41 +28,29 @@ abstract class AbstractCollection extends Component implements \IteratorAggregat
/** /**
* @var ModelInterface[] * @var ModelInterface[]
*/ */
protected array $_item = []; private array $_item;
/** /**
* @var ModelInterface|string|null * @return array
*/ */
protected ModelInterface|string|null $model; public function getItems(): array
/**
* @var ActiveQuery
*/
protected ActiveQuery $query;
public function clean(): void
{ {
unset($this->query, $this->model, $this->_item); // TODO: Change the autogenerated stub
return $this->_item;
} }
/** /**
* Collection constructor. * Collection constructor.
* *
* @param $query * @param ActiveQuery $query
* @param array $array * @param array $array
* @param ModelInterface|null $model * @param ModelInterface|null $model
* @throws
*/ */
public function __construct($query, array $array = [], ModelInterface $model = null) public function __construct(public ActiveQuery $query, public ?ModelInterface $model = null, array $array = [])
{ {
$this->_item = $array; $this->_item = $array;
$this->query = $query;
$this->model = $model;
parent::__construct(); parent::__construct();
} }
+10 -9
View File
@@ -3,6 +3,7 @@
namespace Database\Base; namespace Database\Base;
use Closure; use Closure;
use Database\Collection;
use Database\Traits\QueryTrait; use Database\Traits\QueryTrait;
interface ActiveQueryInterface interface ActiveQueryInterface
@@ -77,29 +78,29 @@ interface ActiveQueryInterface
/** /**
* @param string $tableName * @param string $tableName
* @param string $alias * @param string $alias
* @param string|array $onCondition * @param array $onCondition
* @param array $param * @param array $param
* @return QueryTrait * @return QueryTrait
*/ */
public function leftJoin(string $tableName, string $alias, string|array $onCondition, array $param = []): QueryTrait; public function leftJoin(string $tableName, string $alias, array $onCondition, array $param = []): QueryTrait;
/** /**
* @param string $tableName * @param string $tableName
* @param string $alias * @param string $alias
* @param string|array $onCondition * @param array $onCondition
* @param array $param * @param array $param
* @return QueryTrait * @return QueryTrait
*/ */
public function rightJoin(string $tableName, string $alias, string|array $onCondition, array $param = []): QueryTrait; public function rightJoin(string $tableName, string $alias, array $onCondition, array $param = []): QueryTrait;
/** /**
* @param string $tableName * @param string $tableName
* @param string $alias * @param string $alias
* @param string|array $onCondition * @param array $onCondition
* @param array $param * @param array $param
* @return QueryTrait * @return QueryTrait
*/ */
public function innerJoin(string $tableName, string $alias, string|array $onCondition, array $param = []): QueryTrait; public function innerJoin(string $tableName, string $alias, array $onCondition, array $param = []): QueryTrait;
/** /**
* @param string $field * @param string $field
@@ -138,16 +139,16 @@ interface ActiveQueryInterface
public function orderBy(string|array $column, string $sort = 'DESC'): QueryTrait; public function orderBy(string|array $column, string $sort = 'DESC'): QueryTrait;
/** /**
* @param array|string $column * @param array $column
* *
* @return QueryTrait * @return QueryTrait
*/ */
public function select(array|string $column = '*'): QueryTrait; public function select(array $column = ['*']): QueryTrait;
/** /**
* @return QueryTrait * @return QueryTrait
*/ */
public function orderRand(): QueryTrait; public function rand(): QueryTrait;
/** /**
* @param array|Closure|string $conditionArray * @param array|Closure|string $conditionArray
+29 -27
View File
@@ -1,4 +1,4 @@
<?php <?php /** @noinspection ALL */
/** /**
* Created by PhpStorm. * Created by PhpStorm.
* User: whwyy * User: whwyy
@@ -178,7 +178,7 @@ abstract class Model extends Component implements ModelInterface, ArrayAccess, \
*/ */
public function getLastError(): string public function getLastError(): string
{ {
return Kiri::getLogger()->getLastError('mysql'); return $this->getLogger()->getLastError('mysql');
} }
@@ -237,16 +237,18 @@ abstract class Model extends Component implements ModelInterface, ArrayAccess, \
*/ */
public static function findOne(int|string|array $param, $db = NULL): ?static public static function findOne(int|string|array $param, $db = NULL): ?static
{ {
$model = new ActiveQuery(static::makeNewInstance()); $model = static::instance();
$model->from($model->getTable())->alias('t1');
$query = new ActiveQuery($model);
$query->from($model->getTable())->alias('t1');
if (is_numeric($param)) { if (is_numeric($param)) {
$model->where([$model->modelClass->getPrimary() => $param]); $query->where([$model->getPrimary() => $param]);
} else if (is_array($param)) { } else if (is_array($param)) {
$model->where($param); $query->where($param);
} else { } else {
$model->whereRaw($param); $query->whereRaw($param);
} }
return $model->first(); return $query->first();
} }
@@ -258,27 +260,27 @@ abstract class Model extends Component implements ModelInterface, ArrayAccess, \
*/ */
public static function primary(int $param, $db = NULL): ?static public static function primary(int $param, $db = NULL): ?static
{ {
$model = new ActiveQuery(static::makeNewInstance()); $model = static::instance();
$model->from($model->getTable())->alias('t1'); $query = new ActiveQuery($model);
$model->where([$model->modelClass->getPrimary() => $param]); $query->from($model->getTable())->alias('t1')->where([$model->getPrimary() => $param]);
return $model->first(); return $query->first();
} }
/** /**
* @return mixed * @return bool|int
* @throws * @throws Exception
*/ */
public function optimize(): mixed public function optimize(): bool|int
{ {
return static::query()->execute('OPTIMIZE TABLE ' . $this->getTable())->exec(); return static::query()->execute('OPTIMIZE TABLE ' . $this->getTable());
} }
/** /**
* @return static * @return static
*/ */
private static function makeNewInstance(): static protected static function instance(): static
{ {
return new static(); return new static();
} }
@@ -302,7 +304,7 @@ abstract class Model extends Component implements ModelInterface, ArrayAccess, \
*/ */
public static function all(string|array $condition): Collection public static function all(string|array $condition): Collection
{ {
$model = new ActiveQuery(static::makeNewInstance()); $model = new ActiveQuery(static::instance());
$model->from($model->getTable())->alias('t1'); $model->from($model->getTable())->alias('t1');
if (is_array($condition)) { if (is_array($condition)) {
$model->where($condition); $model->where($condition);
@@ -319,7 +321,7 @@ abstract class Model extends Component implements ModelInterface, ArrayAccess, \
*/ */
public static function query(): ActiveQuery public static function query(): ActiveQuery
{ {
$model = new ActiveQuery(static::makeNewInstance()); $model = new ActiveQuery(static::instance());
$model->from($model->getTable())->alias('t1'); $model->from($model->getTable())->alias('t1');
return $model; return $model;
} }
@@ -336,21 +338,21 @@ abstract class Model extends Component implements ModelInterface, ArrayAccess, \
/** /**
* @param array|string|null $condition * @param array|string $condition
* @param array $attributes * @param array $attributes
* *
* @return bool * @return bool
* @throws
*/ */
protected static function deleteByCondition(array|string|null $condition = NULL, array $attributes = []): bool protected static function deleteByCondition(array|string $condition = [], array $attributes = []): bool
{ {
$model = static::query()->bindParams($attributes); $model = static::query();
if (is_array($condition)) { $model->bindParams($attributes);
$model->where($condition); if (is_string($condition)) {
} else if (is_string($condition)) {
$model->whereRaw($condition); $model->whereRaw($condition);
} else {
$model->where($condition);
} }
return (bool)$model->delete(); return $model->delete();
} }
+30 -49
View File
@@ -21,33 +21,15 @@ use JetBrains\PhpStorm\Pure;
class Collection extends AbstractCollection class Collection extends AbstractCollection
{ {
/**
* @return array
*/
public function getItems(): array
{
// TODO: Change the autogenerated stub
return $this->_item;
}
/** /**
* @param $field * @param string $field
* *
* @return array|null * @return array|null
* @throws
*/ */
public function values($field): ?array public function values(string $field): ?array
{ {
if (empty($field) || !is_string($field)) { return array_values($this->column($field));
return NULL;
}
$_tmp = [];
$data = $this->toArray();
foreach ($data as $val) {
/** @var ModelInterface $val */
$_tmp[] = $val[$field];
}
return $_tmp;
} }
@@ -58,15 +40,10 @@ class Collection extends AbstractCollection
*/ */
public function update(array $attributes): bool public function update(array $attributes): bool
{ {
$lists = []; if ($this->isEmpty()) {
$model = $this->getModel(); return $this->getLogger()->failure('No data by update', 'mysql');
if (!$this->isEmpty()) {
foreach ($this->_item as $item) {
$lists[] = $item[$model->getPrimary()];
} }
return $model::query()->whereIn($model->getPrimary(), $lists)->update($attributes); return $this->batch()->update($attributes);
}
return false;
} }
/** /**
@@ -80,18 +57,9 @@ class Collection extends AbstractCollection
foreach ($column as $key => $value) { foreach ($column as $key => $value) {
$column[$key] = $array[$value]; $column[$key] = $array[$value];
} }
return $column; return $column;
} }
/**
* @return $this
*/
public function orderRand(): static
{
shuffle($this->_item);
return $this;
}
/** /**
* @param int $start * @param int $start
@@ -101,13 +69,13 @@ class Collection extends AbstractCollection
*/ */
#[Pure] public function slice(int $start = 0, int $length = 20): array #[Pure] public function slice(int $start = 0, int $length = 20): array
{ {
if (empty($this->_item)) { if (empty($this->getItems())) {
return []; return [];
} }
if (\count($this->_item) < $length) { if (\count($this->getItems()) < $length) {
return $this->_item; return $this->getItems();
} else { } else {
return array_slice($this->_item, $start, $length); return array_slice($this->getItems(), $start, $length);
} }
} }
@@ -149,7 +117,7 @@ class Collection extends AbstractCollection
*/ */
#[Pure] public function current(): ModelInterface|array #[Pure] public function current(): ModelInterface|array
{ {
return current($this->_item); return current($this->getItems());
} }
/** /**
@@ -157,7 +125,7 @@ class Collection extends AbstractCollection
*/ */
#[Pure] public function size(): int #[Pure] public function size(): int
{ {
return count($this->_item); return count($this->getItems());
} }
/** /**
@@ -171,7 +139,6 @@ class Collection extends AbstractCollection
foreach ($this as $value) { foreach ($this as $value) {
$array[] = $value->toArray(); $array[] = $value->toArray();
} }
$this->_item = [];
return $array; return $array;
} }
@@ -182,10 +149,24 @@ class Collection extends AbstractCollection
public function delete(): bool public function delete(): bool
{ {
$model = $this->getModel(); $model = $this->getModel();
if ($model->hasPrimary()) { if ($this->isEmpty()) {
return $model::query()->whereIn($model->getPrimary(), $this->column($model->getPrimary()))->delete(); return $this->getLogger()->failure('No data by delete', 'mysql');
} }
throw new Exception('Must set primary key. if you wante delete'); if (!$model->hasPrimary()) {
throw new Exception('Must set primary key. if you want to delete data');
}
return $this->batch()->delete();
}
/**
* @return ActiveQuery
* @throws Exception
*/
private function batch(): ActiveQuery
{
return $this->makeNewQuery()->whereIn($this->getModel()->getPrimary(),
$this->column($this->getModel()->getPrimary()));
} }
/** /**
@@ -205,7 +186,7 @@ class Collection extends AbstractCollection
} }
$_filters[] = $value; $_filters[] = $value;
} }
return new Collection($this->query, $_filters, $this->model); return new Collection($this->query, $this->model, $_filters);
} }
+45 -40
View File
@@ -41,21 +41,21 @@ class Command extends Component
/** /**
* @return int|bool * @return bool
* @throws * @throws
*/ */
public function incrOrDecr(): int|bool public function incrOrDecr(): bool
{ {
return $this->_prepare(); return (bool)$this->_prepare();
} }
/** /**
* @return int|bool * @return bool
* @throws * @throws
*/ */
public function save(): int|bool public function save(): bool
{ {
return $this->_prepare(); return (bool)$this->_prepare();
} }
@@ -69,10 +69,10 @@ class Command extends Component
} }
/** /**
* @return bool|array|null * @return array|bool|null
* @throws * @throws
*/ */
public function one(): null|bool|array public function one(): array|null|bool
{ {
return $this->search('fetch'); return $this->search('fetch');
} }
@@ -86,6 +86,29 @@ class Command extends Component
return $this->search('fetchColumn'); return $this->search('fetchColumn');
} }
/**
* @return mixed
* @throws
*/
public function rowCount(): mixed
{
return $this->search('rowCount');
}
/**
* @return bool
* @throws Exception
*/
public function exists(): bool
{
$total = $this->search('rowCount');
if ($total === false) {
throw new Exception('Query data is has error.');
}
return $total > 0;
}
/** /**
* @param string $method * @param string $method
@@ -101,13 +124,17 @@ class Command extends Component
} }
$prepare->execute($this->params); $prepare->execute($this->params);
$prepare->closeCursor();
if ($method == 'rowCount') {
return $prepare->rowCount();
}
return $prepare->{$method}(PDO::FETCH_ASSOC); return $prepare->{$method}(PDO::FETCH_ASSOC);
} catch (Throwable $throwable) { } catch (Throwable $throwable) {
if ($this->isRefresh($throwable)) { if ($this->isRefresh($throwable)) {
return $this->search($method); return $this->search($method);
} }
return $this->error($throwable); return $this->getLogger()->failure(throwable($throwable), 'mysql');
} finally { } finally {
$this->connection->release($client); $this->connection->release($client);
} }
@@ -115,20 +142,20 @@ class Command extends Component
/** /**
* @return int|bool * @return bool
* @throws * @throws
*/ */
public function flush(): int|bool public function flush(): bool
{ {
return $this->_prepare(); return (bool)$this->_prepare();
} }
/** /**
* @return PDOStatement|int * @return int|bool
* @throws * @throws
*/ */
private function _prepare(): bool|int private function _prepare(): int|bool
{ {
$client = $this->connection->getConnection(); $client = $this->connection->getConnection();
try { try {
@@ -142,12 +169,12 @@ class Command extends Component
$result = $client->lastInsertId(); $result = $client->lastInsertId();
return $result == 0 ? $prepare->rowCount() > 0 : (int)$result; return $result == 0 ? $prepare->rowCount() : (int)$result;
} catch (Throwable $throwable) { } catch (Throwable $throwable) {
if ($this->isRefresh($throwable)) { if ($this->isRefresh($throwable)) {
return $this->_prepare(); return $this->_prepare();
} }
return $this->error($throwable); return $this->getLogger()->failure(throwable($throwable), 'mysql');
} finally { } finally {
$this->connection->release($client); $this->connection->release($client);
} }
@@ -174,27 +201,16 @@ class Command extends Component
/** /**
* @param Throwable $throwable
* @return bool * @return bool
*/
private function error(Throwable $throwable): bool
{
return trigger_print_error($this->sql . '.' . json_encode($this->params, JSON_UNESCAPED_UNICODE) . PHP_EOL . throwable($throwable), 'mysql');
}
/**
* @return int|bool
* @throws * @throws
*/ */
public function delete(): int|bool public function delete(): bool
{ {
return $this->_prepare(); return (bool)$this->_prepare();
} }
/** /**
* @return int|bool * @return int|bool
* @throws
*/ */
public function exec(): int|bool public function exec(): int|bool
{ {
@@ -213,15 +229,4 @@ class Command extends Component
return $this; return $this;
} }
/**
* @param $sql
* @return $this
* @throws
*/
public function setSql($sql): static
{
$this->sql = $sql;
return $this;
}
} }
-2
View File
@@ -22,11 +22,9 @@ use Kiri\Abstracts\Component;
use Kiri\Di\Context; use Kiri\Di\Context;
use Kiri\Pool\Pool; use Kiri\Pool\Pool;
use Kiri\Events\EventProvider; use Kiri\Events\EventProvider;
use Kiri\Exception\NotFindClassException;
use PDO; use PDO;
use Kiri\Error\StdoutLogger; use Kiri\Error\StdoutLogger;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use ReflectionException;
use Kiri\Server\Events\OnWorkerStart; use Kiri\Server\Events\OnWorkerStart;
use Kiri\Server\Events\OnTaskerStart; use Kiri\Server\Events\OnTaskerStart;
use Kiri\Server\Events\OnAfterRequest; use Kiri\Server\Events\OnAfterRequest;
+18 -42
View File
@@ -15,6 +15,7 @@ use Database\Affair\Commit;
use Database\Affair\Rollback; use Database\Affair\Rollback;
use Database\Traits\QueryTrait; use Database\Traits\QueryTrait;
use Exception; use Exception;
use Kiri;
use Throwable; use Throwable;
/** /**
@@ -42,9 +43,10 @@ class Db extends QueryTrait implements ISqlBuilder
{ {
$db = new Db(); $db = new Db();
if (is_string($dbname)) { if (is_string($dbname)) {
$dbname = \Kiri::getDi()->get(DatabasesProviders::class)->get($dbname); $db->connection = Kiri::getDi()->get(DatabasesProviders::class)->get($dbname);
} } else {
$db->connection = $dbname; $db->connection = $dbname;
}
return $db; return $db;
} }
@@ -58,30 +60,6 @@ class Db extends QueryTrait implements ISqlBuilder
} }
/**
* @param Closure $closure
* @param mixed ...$params
* @return mixed
* @throws
*/
public static function Transaction(Closure $closure, ...$params): mixed
{
static::beginTransaction();
try {
$result = call_user_func($closure, ...$params);
} catch (Throwable $throwable) {
$result = trigger_print_error($throwable->getMessage(), 'mysql');
} finally {
if ($result === false) {
static::rollback();
} else {
static::commit();
}
return $result;
}
}
/** /**
* @return void * @return void
* @throws * @throws
@@ -149,21 +127,21 @@ class Db extends QueryTrait implements ISqlBuilder
} }
/** /**
* @return bool|int * @return int
* @throws
*/ */
public function count(): bool|int public function count(): int
{ {
return $this->connection->createCommand(SqlBuilder::builder($this)->count())->one()['row_count']; $count = $this->connection->createCommand(SqlBuilder::builder($this)->count())->one();
return current($count);
} }
/** /**
* @return bool|int * @return bool
* @throws
*/ */
public function exists(): bool|int public function exists(): bool
{ {
return $this->connection->createCommand(SqlBuilder::builder($this)->one())->fetchColumn(); $fetchColumn = $this->connection->createCommand(SqlBuilder::builder($this)->one())->fetchColumn();
return !empty($fetchColumn);
} }
/** /**
@@ -180,20 +158,18 @@ class Db extends QueryTrait implements ISqlBuilder
/** /**
* @param string $sql * @param string $sql
* @param array $attributes * @param array $attributes
* @return array|bool|int|string|null * @return array|null
* @throws
*/ */
public function one(string $sql, array $attributes = []): int|bool|array|string|null public function one(string $sql, array $attributes = []): ?array
{ {
return $this->connection->createCommand($sql, $attributes)->one(); return $this->connection->createCommand($sql, $attributes)->one();
} }
/** /**
* @return bool|int * @return bool
* @throws
*/ */
public function delete(): bool|int public function delete(): bool
{ {
return $this->connection->createCommand(SqlBuilder::builder($this)->delete())->delete(); return $this->connection->createCommand(SqlBuilder::builder($this)->delete())->delete();
} }
@@ -215,7 +191,7 @@ class Db extends QueryTrait implements ISqlBuilder
->select('*') ->select('*')
->from('INFORMATION_SCHEMA.KEY_COLUMN_USAGE') ->from('INFORMATION_SCHEMA.KEY_COLUMN_USAGE')
->where(['REFERENCED_TABLE_NAME' => $table]) ->where(['REFERENCED_TABLE_NAME' => $table])
->getSql())->one(); ->build())->one();
} }
@@ -248,7 +224,7 @@ class Db extends QueryTrait implements ISqlBuilder
return $connection; return $connection;
} }
$databases = \config('databases.connections', []); $databases = \config('databases.connections', []);
$providers = \Kiri::getDi()->get(DatabasesProviders::class); $providers = Kiri::getDi()->get(DatabasesProviders::class);
if (empty($databases) || !is_array($databases)) { if (empty($databases) || !is_array($databases)) {
throw new Exception('Please configure the database link.'); throw new Exception('Please configure the database link.');
} }
+40 -60
View File
@@ -102,14 +102,23 @@ class Model extends Base\Model
*/ */
public static function findOrCreate(array $condition, array $attributes): bool|static public static function findOrCreate(array $condition, array $attributes): bool|static
{ {
return Db::Transaction(function ($condition, $attributes) { $model = static::instance();
$connection = $model->getConnection()->beginTransaction();
try {
/** @var static $select */ /** @var static $select */
$select = static::query()->where($condition)->first(); $select = $model::query()->where($condition)->first();
if ($select === null) { if ($select === null) {
$select = static::populate(array_merge($condition, $attributes))->save(); $select = $model::populate(array_merge($condition, $attributes));
if (!$select->save()) {
throw new Exception('保存失败: ' . $model->getLastError());
} }
}
$connection->commit();
return $select; return $select;
}, $condition, $attributes); } catch (\Throwable $throwable) {
$connection->rollback();
return \Kiri::getLogger()->failure($throwable);
}
} }
@@ -121,15 +130,24 @@ class Model extends Base\Model
*/ */
public static function createOrUpdate(array $condition, array $attributes = []): bool|static public static function createOrUpdate(array $condition, array $attributes = []): bool|static
{ {
return Db::Transaction(function ($condition, $attributes) { $model = static::instance();
$connection = $model->getConnection()->beginTransaction();
try {
/** @var static $select */ /** @var static $select */
$select = static::query()->where($condition)->first(); $select = static::query()->where($condition)->first();
if (empty($select)) { if (empty($select)) {
$select = static::populate($condition); $select = static::populate($condition);
} }
$select->attributes = $attributes; $select->attributes = $attributes;
return $select->save(); if (!$select->save()) {
}, $condition, $attributes); throw new Exception('保存失败: ' . $model->getLastError());
}
$connection->commit();
return $select;
} catch (\Throwable $throwable) {
$connection->rollback();
return \Kiri::getLogger()->failure($throwable);
}
} }
@@ -158,13 +176,12 @@ class Model extends Base\Model
private function mathematics($columns, $action): int|bool|array|string|null private function mathematics($columns, $action): int|bool|array|string|null
{ {
$condition = [$this->getPrimary() => $this->getPrimaryValue()]; $condition = [$this->getPrimary() => $this->getPrimaryValue()];
$activeQuery = static::query()->where($condition); $activeQuery = static::query()->where($condition);
$create = SqlBuilder::builder($activeQuery)->mathematics($columns, $action); $create = SqlBuilder::builder($activeQuery)->mathematics($columns, $action);
if (is_bool($create)) { if (is_bool($create)) {
return false; return false;
} }
return $this->getConnection()->createCommand($create, $activeQuery->attributes)->exec(); return $activeQuery->buildCommand($create)->exec();
} }
@@ -179,11 +196,16 @@ class Model extends Base\Model
return FALSE; return FALSE;
} }
$condition = array_diff_assoc($this->_oldAttributes, $params); $condition = [];
$oldPrams = [];
$old = array_intersect_key($this->_oldAttributes, $params); foreach ($this->_oldAttributes as $key => $attribute) {
if (!array_key_exists($key, $params) || $params[$key] == $attribute) {
return $this->updateInternal($old, $condition, $params); $condition[$key] = $attribute;
} else {
$oldPrams[$key] = $this->_oldAttributes[$attribute];
}
}
return $this->updateInternal($oldPrams, $condition, $params);
} }
@@ -206,58 +228,16 @@ class Model extends Base\Model
*/ */
public function delete(): bool public function delete(): bool
{ {
if ($this->beforeDelete()) { if (!$this->beforeDelete()) {
return false;
}
if ($this->hasPrimary()) { if ($this->hasPrimary()) {
$result = static::deleteByCondition("id = ?", [$this->getPrimaryValue()]); $result = static::deleteByCondition(['id' => $this->getPrimaryValue()]);
} else { } else {
$result = static::deleteByCondition($this->_attributes); $result = static::deleteByCondition($this->_attributes);
} }
$this->optimize();
return $this->afterDelete($result); return $this->afterDelete($result);
} }
return false;
}
/**
* @param mixed $condition
* @param array $attributes
*
* @return bool
* @throws
*/
public static function updateAll(mixed $condition, array $attributes = []): bool
{
return static::query()->where($condition)->update($attributes);
}
/**
* @param $condition
* @return array|Collection
* @throws
*/
public static function get($condition): Collection|array
{
return static::query()->where($condition)->get();
}
/**
* @param $condition
* @param array $attributes
*
* @return array|Collection
* @throws
*/
public static function findAll($condition, array $attributes = []): array|Collection
{
$query = static::query()->where($condition);
if (!empty($attributes)) {
$query->bindParams($attributes);
}
return $query->get();
}
/** /**
-28
View File
@@ -1,28 +0,0 @@
<?php
declare(strict_types=1);
namespace Database\Orm;
use Database\Traits\Builder;
/**
* Trait Condition
* @package Database\Orm
*/
trait Condition
{
use Builder;
/**
* @param $query
* @return string
* @throws
*/
public function getWhere($query): string
{
return $this->where($query);
}
}
-206
View File
@@ -1,206 +0,0 @@
<?php
declare(strict_types=1);
namespace Database;
use Closure;
use Exception;
use Kiri\Abstracts\Component;
/**
* Class Pagination
* @package Database
*/
class Pagination extends Component
{
/** @var ActiveQuery */
private ActiveQuery $activeQuery;
/** @var int 从第几个开始查 */
private int $_offset = 0;
/** @var int 每页数量 */
private int $_limit = 100;
/** @var int 最大查询数量 */
private int $_max = 0;
/** @var int 当前已查询数量 */
private int $_length = 0;
/** @var Closure */
private Closure $_callback;
/**
* PaginationIteration constructor.
* @param ActiveQuery $activeQuery
* @param array $config
* @throws
*/
public function __construct(ActiveQuery $activeQuery, array $config = [])
{
parent::__construct();
$this->activeQuery = $activeQuery;
}
/**
* @return void
*/
public function clean(): void
{
unset($this->activeQuery, $this->_callback, $this->_group);
$this->_offset = 0;
$this->_limit = 100;
$this->_max = 0;
$this->_length = 0;;
}
/**
* recover class by clone
*/
public function __clone()
{
$this->clean();
}
/**
* @param array|Closure $callback
* @throws
*/
public function setCallback(array|Closure $callback): void
{
if (!is_callable($callback, true)) {
throw new Exception('非法回调函数~');
}
$this->_callback = $callback;
}
/**
* @param int $number
* @return Pagination
*/
public function setOffset(int $number): static
{
if ($number < 0) {
$number = 0;
}
$this->_offset = $number;
return $this;
}
/**
* @param int $number
* @return Pagination
*/
public function setLimit(int $number): static
{
if ($number < 1) {
$number = 100;
} else if ($number > 5000) {
$number = 5000;
}
$this->_limit = $number;
return $this;
}
/**
* @param int $number
* @return Pagination
*/
public function setMax(int $number): static
{
if ($number < 0) {
return $this;
}
$this->_max = $number;
return $this;
}
/**
* @param array $param
* @return void
* @throws
*/
public function plunk(array $param = []): void
{
$this->loop($param);
}
/**
* 轮训
* @param $param
* @return array
* @throws
*/
public function loop($param): array
{
if ($this->_max > 0 && $this->_length >= $this->_max) {
return $this->output();
}
[$length, $data] = $this->get();
try {
call_user_func($this->_callback, $data, $param);
} catch (\Throwable $exception) {
error($exception);
} finally {
$data = null;
}
if ($length < $this->_limit) {
return $this->output();
}
return $this->loop($param);
}
/**
* @return array
*/
public function output(): array
{
return [];
}
/**
* @param $data
* @param $param
* @throws
*/
private function executed($data, $param): void
{
try {
call_user_func($this->_callback, $data, $param);
} catch (\Throwable $exception) {
error($exception);
}
}
/**
* @return array|Collection
* @throws
*/
private function get(): Collection|array
{
if ($this->_max > 0 && $this->_length + $this->_limit > $this->_max) {
$this->_limit = $this->_length + $this->_limit - $this->_max;
}
$data = $this->activeQuery->offset($this->_offset)->limit($this->_limit)->get();
$this->_offset += $this->_limit;
$size = $data->size();
$this->_length += $size;
return [$size, $data];
}
}
-19
View File
@@ -19,25 +19,6 @@ use Database\Traits\QueryTrait;
class Query extends QueryTrait implements ISqlBuilder class Query extends QueryTrait implements ISqlBuilder
{ {
/**
* @throws
*/
public function __construct()
{
$this->builder = SqlBuilder::builder($this);
parent::__construct();
}
/**
* @return string
* @throws
*/
public function getSql(): string
{
return $this->builder->get();
}
/** /**
* @return string * @return string
+115 -47
View File
@@ -1,12 +1,12 @@
<?php <?php /** @noinspection ALL */
declare(strict_types=1); declare(strict_types=1);
namespace Database; namespace Database;
use Database\Traits\Builder;
use JetBrains\PhpStorm\Pure; use JetBrains\PhpStorm\Pure;
use Kiri;
use Kiri\Abstracts\Component; use Kiri\Abstracts\Component;
@@ -17,9 +17,6 @@ use Kiri\Abstracts\Component;
class SqlBuilder extends Component class SqlBuilder extends Component
{ {
use Builder;
/** /**
* @var ActiveQuery|Query|ISqlBuilder|null * @var ActiveQuery|Query|ISqlBuilder|null
*/ */
@@ -54,7 +51,7 @@ class SqlBuilder extends Component
*/ */
public function getCondition(): string public function getCondition(): string
{ {
return $this->conditionToString(); return $this->where($this->query->where);
} }
@@ -76,9 +73,9 @@ class SqlBuilder extends Component
*/ */
public function update(array $attributes): bool|string public function update(array $attributes): bool|string
{ {
$conditions = $this->query->attributes; $conditions = $this->query->params;
$this->query->attributes = []; $this->query->params = [];
$data = $this->__updateBuilder($this->builderParams($attributes)); $data = $this->__updateBuilder($this->makeParams($attributes));
foreach ($conditions as $condition) { foreach ($conditions as $condition) {
$this->query->pushParam($condition); $this->query->pushParam($condition);
} }
@@ -110,9 +107,9 @@ class SqlBuilder extends Component
private function __updateBuilder(array $string): string|bool private function __updateBuilder(array $string): string|bool
{ {
if (empty($string)) { if (empty($string)) {
return \Kiri::getLogger()->failure('None data update.'); return Kiri::getLogger()->failure('None data update.');
} }
return 'UPDATE ' . $this->query->from . ' SET ' . implode(',', $string) . $this->_prefix(); return 'UPDATE ' . $this->query->from . ' SET ' . implode(',', $string) . $this->make();
} }
@@ -130,15 +127,13 @@ class SqlBuilder extends Component
} }
$update .= '(' . implode(',', $this->getFields($attributes)) . ') VALUES '; $update .= '(' . implode(',', $this->getFields($attributes)) . ') VALUES ';
$order = 0;
$keys = []; $keys = [];
foreach ($attributes as $attribute) { foreach ($attributes as $attribute) {
$_keys = $this->builderParams($attribute, true, $order); $_keys = $this->makeParams($attribute, true);
$keys[] = implode(',', $_keys); $keys[] = implode(',', $_keys);
$order++;
} }
return [$update . '(' . implode('),(', $keys) . ')', $this->query->attributes]; return [$update . '(' . implode('),(', $keys) . ')', $this->query->params];
} }
@@ -148,7 +143,7 @@ class SqlBuilder extends Component
*/ */
public function delete(): string public function delete(): string
{ {
return 'DELETE FROM ' . $this->query->from . $this->_prefix(); return 'DELETE FROM ' . $this->query->from . $this->make();
} }
@@ -165,19 +160,18 @@ class SqlBuilder extends Component
/** /**
* @param array $attributes * @param array $attributes
* @param bool $isInsert * @param bool $isInsert
* @param int $order
* @return array[] * @return array[]
* a=:b, * a=:b,
*/ */
private function builderParams(array $attributes, bool $isInsert = false, int $order = 0): array private function makeParams(array $attributes, bool $isInsert = false): array
{ {
$keys = []; $keys = [];
foreach ($attributes as $key => $value) { foreach ($attributes as $key => $value) {
if ($isInsert === true) { if ($isInsert !== true) {
$keys[] = '?'; $keys[] = '?';
$this->query->pushParam($value); $this->query->pushParam($value);
} else { } else {
$keys = $this->resolveParams($key, $value, $order, $keys); $keys = $this->resolveParams($key, $value, $keys);
} }
} }
return $keys; return $keys;
@@ -187,16 +181,15 @@ class SqlBuilder extends Component
/** /**
* @param string $key * @param string $key
* @param mixed $value * @param mixed $value
* @param int $order
* @param array $keys * @param array $keys
* @return array * @return array
*/ */
private function resolveParams(string $key, mixed $value, int $order, array $keys): array private function resolveParams(string $key, mixed $value, array $keys): array
{ {
if (is_null($value)) { if (is_null($value)) {
return $keys; return $keys;
} }
if (is_string($value) && (str_starts_with($value, '+ ') || str_starts_with($value, '- '))) { if (is_string($value) && $this->isMath($value)) {
$keys[] = $key . '=' . $key . ' ' . $value; $keys[] = $key . '=' . $key . ' ' . $value;
} else { } else {
$this->query->pushParam($value); $this->query->pushParam($value);
@@ -206,16 +199,23 @@ class SqlBuilder extends Component
} }
/**
* @param string $value
* @return bool
*/
private function isMath(string $value): bool
{
return str_starts_with($value, '+ ') || str_starts_with($value, '- ');
}
/** /**
* @return string * @return string
* @throws * @throws
*/ */
public function one(): string public function one(): string
{ {
if (count($this->query->select) < 1) { return $this->makeSelect($this->query->select) . $this->make() . $this->makeLimit($this->query->limit(1));
$this->query->select = ['*'];
}
return $this->_selectPrefix($this->query->select) . $this->_prefix() . $this->builderLimit($this->query);
} }
@@ -225,10 +225,7 @@ class SqlBuilder extends Component
*/ */
public function all(): string public function all(): string
{ {
if (count($this->query->select) < 1) { return $this->makeSelect($this->query->select) . $this->make() . $this->makeLimit($this->query);
$this->query->select = ['*'];
}
return $this->_selectPrefix($this->query->select) . $this->_prefix() . $this->builderLimit($this->query);
} }
@@ -238,7 +235,7 @@ class SqlBuilder extends Component
*/ */
public function count(): string public function count(): string
{ {
return $this->_selectPrefix(['COUNT(*) as row_count']) . $this->_prefix(); return $this->_selectPrefix(['COUNT(*)']) . $this->make();
} }
@@ -256,18 +253,11 @@ class SqlBuilder extends Component
* @return string * @return string
* @throws * @throws
*/ */
private function _prefix(): string private function make(): string
{ {
$select = ''; $select = $this->makeCondition();
if (($condition = $this->conditionToString()) != '') { $select .= $this->makeGroup();
$select .= " WHERE $condition"; $select .= $this->makeOrder();
}
if ($this->query->group != "") {
$select .= ' GROUP BY ' . $this->query->group;
}
if (count($this->query->order) > 0) {
$select .= ' ORDER BY ' . implode(',', $this->query->order);
}
return $select; return $select;
} }
@@ -275,7 +265,7 @@ class SqlBuilder extends Component
* @param array $select * @param array $select
* @return string * @return string
*/ */
private function _selectPrefix(array $select = ['*']): string private function makeSelect(array $select = ['*']): string
{ {
$select = "SELECT " . implode(',', $select) . " FROM " . $this->query->from; $select = "SELECT " . implode(',', $select) . " FROM " . $this->query->from;
if ($this->query->alias != "") { if ($this->query->alias != "") {
@@ -288,6 +278,43 @@ class SqlBuilder extends Component
} }
/**
* @return string
*/
private function makeGroup(): string
{
if ($this->query->group != "") {
return ' GROUP BY ' . $this->query->group;
}
return '';
}
/**
* @return string
*/
private function makeOrder(): string
{
if (count($this->query->order) > 0) {
return ' ORDER BY ' . implode(',', $this->query->order);
}
return '';
}
/**
* @return string
*/
private function makeCondition(): string
{
$condition = $this->where($this->query->where);
if (empty($condition)) {
return '';
}
return ' WHERE ' . $condition;
}
/** /**
* @param false $isCount * @param false $isCount
* @return string * @return string
@@ -313,12 +340,53 @@ class SqlBuilder extends Component
/** /**
* @param array $where
* @return string * @return string
* @throws
*/ */
private function conditionToString(): string private function where(array $where): string
{ {
return $this->where($this->query->where); if (count($where) < 1) {
return '';
}
$_tmp = [];
foreach ($where as $key => $value) {
$_tmp[] = $this->resolveCondition($key, $value);
}
return implode(' AND ', $_tmp);
} }
/**
* @param $field
* @param $condition
* @return string
*/
private function resolveCondition($field, $condition): string
{
if (is_string($field)) {
$this->query->pushParam($condition);
return $field . ' = ?';
} else if (is_string($condition)) {
return $condition;
} else {
return implode(' AND ', $this->_hashMap($condition));
}
}
/**
* @param $condition
* @return array
*/
private function _hashMap($condition): array
{
$_array = [];
foreach ($condition as $key => $value) {
$this->query->pushParam($value);
$_array[] = $key . '= ?';
}
return $_array;
}
} }
-200
View File
@@ -1,200 +0,0 @@
<?php
declare(strict_types=1);
namespace Database\Traits;
use Database\ActiveQuery;
use Database\Base\ConditionClassMap;
use Database\Condition\HashCondition;
use Database\Condition\OrCondition;
use Database\Query;
use Exception;
use JetBrains\PhpStorm\Pure;
use Kiri\Exception\NotFindClassException;
use Kiri;
use ReflectionException;
/**
* Trait Builder
* @package Database\Traits
*/
trait Builder
{
/**
* @param $alias
* @return string
*/
private function builderAlias($alias): string
{
return " AS " . $alias;
}
/**
* @param $table
* @return string
* @throws
*/
private function builderFrom($table): string
{
if ($table instanceof ActiveQuery) {
$table = '(' . $table->toSql() . ')';
}
return " FROM " . $table;
}
/**
* @param $join
* @return string
*/
#[Pure] private function builderJoin($join): string
{
if (!empty($join)) {
return ' ' . implode(' ', $join);
}
return '';
}
/**
* @param string $select
* @return string
*/
#[Pure] private function builderSelect(string $select = "*"): string
{
return "SELECT " . $select;
}
/**
* @param string $group
* @return string
*/
private function builderGroup(string $group): string
{
if ($group != '') {
return '';
}
return ' GROUP BY ' . $group;
}
/**
* @param $order
* @return string
*/
#[Pure] private function builderOrder($order): string
{
if (!empty($order)) {
return ' ORDER BY ' . implode(',', $order);
} else {
return '';
}
}
/**
* @param ActiveQuery|Query $query
* @return string
*/
#[Pure] private function builderLimit(ActiveQuery|Query $query): string
{
if ($query->limit > 0) {
return ' LIMIT ' . $query->offset . ',' . $query->limit;
} else {
return '';
}
}
/**
* @param array $where
* @return string
*/
private function where(array $where): string
{
if (count($where) < 1) {
return '';
}
return implode(' AND ', $where);
}
/**
* @param $field
* @param $condition
* @param $_tmp
* @return string
* @throws
*/
private function resolveCondition($field, $condition, $_tmp): string
{
if (is_string($field)) {
$this->query->pushParam($condition);
return $field . ' = ?';
} else if (is_string($condition)) {
return $condition;
} else {
return $this->_arrayMap($condition, $_tmp);
}
}
/**
* @param $condition
* @param $array
* @return string
* @throws
*/
private function _arrayMap($condition, $array): string
{
if (!isset($condition[0])) {
return implode(' AND ', $this->_hashMap($condition));
}
$stroppier = strtoupper($condition[0]);
if (str_contains($stroppier, 'OR')) {
if (!is_string($condition[2])) {
$condition[2] = $this->_hashMap($condition[2]);
}
$builder = Kiri::getDi()->get(OrCondition::class);
$builder->setValue($condition[2]);
$builder->setColumn($condition[1]);
$builder->oldParams = $array;
} else if (isset(ConditionClassMap::$conditionMap[$stroppier])) {
$defaultConfig = ConditionClassMap::$conditionMap[$stroppier];
$class = $defaultConfig['class'];
unset($defaultConfig['class']);
$builder = Kiri::getDi()->make($class, [], $defaultConfig);
$builder->setValue($condition[2]);
$builder->setColumn($condition[1]);
} else {
$builder = Kiri::getDi()->get(HashCondition::class);
$builder->setValue($condition);
}
$array[] = $builder->builder();
return implode(' AND ', $array);
}
/**
* @param $condition
* @return array
*/
private function _hashMap($condition): array
{
$_array = [];
foreach ($condition as $key => $value) {
$this->query->pushParam($value);
$_array[] = $key . '= ?';
}
return $_array;
}
}
+113 -126
View File
@@ -11,8 +11,8 @@ namespace Database\Traits;
use Closure; use Closure;
use Database\ActiveQuery;
use Database\Base\ActiveQueryInterface; use Database\Base\ActiveQueryInterface;
use Database\Command;
use Database\ISqlBuilder; use Database\ISqlBuilder;
use Database\ModelInterface; use Database\ModelInterface;
use Database\Query; use Database\Query;
@@ -25,20 +25,22 @@ use Kiri\Abstracts\Component;
* Trait QueryTrait * Trait QueryTrait
* @package Database\Traits * @package Database\Traits
*/ */
class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder abstract class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
{ {
protected array $where = []; public array $where = [];
protected array $select = []; public array $select = ['*'];
protected array $join = []; public array $join = [];
protected array $order = []; public array $order = [];
protected int $offset = 0; public int $offset = 0;
protected int $limit = 0; public int $limit = 0;
protected string $group = ''; public string $group = '';
protected string $from = ''; public string $from = '';
protected string $alias = 't1'; public string $alias = 't1';
protected array $filter = []; protected array $filter = [];
protected bool $lock = false; protected bool $lock = false;
protected SqlBuilder $builder; protected SqlBuilder $builder;
public array $params = [];
private array $_alias = ['t1'];
/** /**
@@ -46,21 +48,18 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
*/ */
protected ModelInterface|string|null $modelClass; protected ModelInterface|string|null $modelClass;
/** /**
* clear * Comply constructor.
* @throws
*/ */
public function clear(): void public function __construct($model = null)
{ {
$this->where = []; if (!is_null($model)) {
$this->select = []; $this->modelClass = $model;
$this->join = []; }
$this->order = []; $this->builder = SqlBuilder::builder($this);
$this->offset = 0; parent::__construct();
$this->limit = 500;
$this->group = '';
$this->from = '';
$this->alias = 't1';
$this->filter = [];
} }
@@ -179,6 +178,7 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
public function alias(string $alias = 't1'): static public function alias(string $alias = 't1'): static
{ {
$this->alias = $alias; $this->alias = $alias;
$this->_alias = [$alias];
return $this; return $this;
} }
@@ -190,7 +190,7 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
public function from(string|Closure $tableName): static public function from(string|Closure $tableName): static
{ {
if ($tableName instanceof Closure) { if ($tableName instanceof Closure) {
$tableName = call_user_func($tableName, $this->makeNewSqlGenerate()); $tableName = call_user_func($tableName, $this->queryInstance());
} }
$this->from = $tableName; $this->from = $tableName;
return $this; return $this;
@@ -199,64 +199,90 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
/** /**
* @param string $tableName * @param string $tableName
* @param string $alias * @param string $alias
* @param null $on * @param array $on
* @param array|null $param * @param array $param
* @return $this * @return $this
* $query->join([$tableName, ['userId'=>'uuvOd']], $param) * $query->join([$tableName, ['userId'=>'uuvOd']], $param)
* $query->join([$tableName, ['userId'=>'uuvOd'], $param]) * $query->join([$tableName, ['userId'=>'uuvOd'], $param])
* $query->join($tableName, ['userId'=>'uuvOd',$param]) * $query->join($tableName, ['userId'=>'uuvOd',$param])
*/ */
private function join(string $tableName, string $alias, $on = NULL, array $param = NULL): static private function join(string $tableName, string $alias, array $on, array $param = []): static
{ {
if (empty($on)) { if (empty($on)) {
return $this; return $this;
} }
$this->_alias[] = $alias;
$join[] = $tableName . ' AS ' . $alias; $join[] = $tableName . ' AS ' . $alias;
$join[] = 'ON ' . $this->onCondition($alias, $on); $join[] = 'ON ' . $this->onCondition($on);
if (empty($join)) { if (empty($join)) {
return $this; return $this;
} }
$this->join[] = implode(' ', $join); $this->join[] = implode(' ', $join);
if (!empty($param)) { if (!empty($param)) {
$this->addParams($param); $this->bindParams($param);
} }
return $this; return $this;
} }
/** /**
* @param $alias * @param array $params
* @param $on * @return void
*/
public function bindParams(array $params): void
{
foreach ($params as $param) {
$this->pushParam($param);
}
}
/**
* @param array $condition
* @return string * @return string
*/ */
private function onCondition($alias, $on): string private function onCondition(array $condition): string
{ {
$array = []; $array = [];
foreach ($on as $key => $item) { foreach ($condition as $key => $item) {
if (!str_contains($item, '.')) { if (is_numeric($item) || !$this->isAliasField($item)) {
$this->addParam($key, $item); $array[] = $key . '= ?';
$this->pushParam($item);
} else { } else {
$explode = explode('.', $item);
if (isset($explode[1]) && ($explode[0] == $alias || $this->alias == $explode[0])) {
$array[] = $key . '=' . $item; $array[] = $key . '=' . $item;
} else {
$this->addParam($key, $item);
}
} }
} }
return implode(' AND ', $array); return implode(' AND ', $array);
} }
/**
* @param string $value
* @return bool
*/
private function isAliasField(string $value): bool
{
foreach ($this->_alias as $alias) {
if (str_starts_with($value, $alias . '.')) {
return true;
}
}
return false;
}
/** /**
* @param string $tableName * @param string $tableName
* @param string $alias * @param string $alias
* @param $onCondition * @param array $onCondition
* @param null $param * @param array $param
* @return $this * @return $this
* @throws * @throws Exception
*/ */
public function leftJoin(string $tableName, string $alias, $onCondition, $param = NULL): static public function leftJoin(string $tableName, string $alias, array $onCondition, array $param = []): static
{ {
if (class_exists($tableName)) { if (class_exists($tableName)) {
$model = Kiri::getDi()->get($tableName); $model = Kiri::getDi()->get($tableName);
@@ -269,14 +295,14 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
} }
/** /**
* @param $tableName * @param string $tableName
* @param $alias * @param string $alias
* @param $onCondition * @param array $onCondition
* @param null $param * @param array $param
* @return $this * @return $this
* @throws * @throws Exception
*/ */
public function rightJoin($tableName, $alias, $onCondition, $param = NULL): static public function rightJoin(string $tableName, string $alias, array $onCondition, array $param = []): static
{ {
if (class_exists($tableName)) { if (class_exists($tableName)) {
$model = Kiri::getDi()->get($tableName); $model = Kiri::getDi()->get($tableName);
@@ -289,14 +315,14 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
} }
/** /**
* @param $tableName * @param string $tableName
* @param $alias * @param string $alias
* @param $onCondition * @param array $onCondition
* @param null $param * @param array $param
* @return $this * @return $this
* @throws * @throws Exception
*/ */
public function innerJoin($tableName, $alias, $onCondition, $param = NULL): static public function innerJoin(string $tableName, string $alias, array $onCondition, array $param = []): static
{ {
if (class_exists($tableName)) { if (class_exists($tableName)) {
$model = Kiri::getDi()->get($tableName); $model = Kiri::getDi()->get($tableName);
@@ -332,8 +358,8 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
/** /**
* @param string $lngField * @param string $lngField
* @param string $latField * @param string $latField
* @param int $lng1 * @param int|float $lng1
* @param int $lat1 * @param int|float $lat1
* *
* @return $this * @return $this
*/ */
@@ -389,29 +415,20 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
} }
/** /**
* @param array|string $column * @param array $column
* *
* @return $this * @return $this
*/ */
public function select(array|string $column = '*'): static public function select(array $column = ['*']): static
{ {
if ($column == '*') { $this->select = $column;
$this->select = [$column];
} else {
if (!is_array($column)) {
$column = explode(',', $column);
}
foreach ($column as $val) {
$this->select[] = $val;
}
}
return $this; return $this;
} }
/** /**
* @return $this * @return $this
*/ */
public function orderRand(): static public function rand(): static
{ {
$this->order[] = 'RAND()'; $this->order[] = 'RAND()';
return $this; return $this;
@@ -510,29 +527,13 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
} }
/**
* @param $value
* @return ActiveQuery
* @throws
*/
public function makeNewQuery($value): ActiveQuery
{
$activeQuery = new ActiveQuery($this->modelClass);
call_user_func($value, $activeQuery);
if (empty($activeQuery->from)) {
$activeQuery->from($activeQuery->modelClass->getTable());
}
return $activeQuery;
}
/** /**
* @return Query * @return Query
* @throws * @throws
*/ */
public function makeNewSqlGenerate(): Query public function queryInstance(): Query
{ {
return Kiri::createObject(['class' => Query::class]); return new Query();
} }
@@ -588,43 +589,13 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
return $this; return $this;
} }
/**
* @param array|null $params
*
* @return $this
*/
public function bindParams(?array $params = []): static
{
if ($params === null) {
return $this;
}
foreach ($params as $param) {
$this->attributes[] = $param;
}
return $this;
}
/**
* @param string $key
* @param mixed $value
* @return $this
*/
public function bindParam(string $key, mixed $value): static
{
if (is_string($value)) {
$value = addslashes($value);
}
$this->attributes[$key] = $value;
return $this;
}
/** /**
* @param mixed $value * @param mixed $value
* @return $this * @return $this
*/ */
public function pushParam(mixed $value): static public function pushParam(mixed $value): static
{ {
$this->attributes[] = $value; $this->params[] = $value;
return $this; return $this;
} }
@@ -670,13 +641,13 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
*/ */
public function makeClosureFunction(Closure|array $closure): string public function makeClosureFunction(Closure|array $closure): string
{ {
$generate = $this->makeNewSqlGenerate(); $generate = $this->queryInstance();
if ($closure instanceof Closure) { if ($closure instanceof Closure) {
call_user_func($closure, $generate); call_user_func($closure, $generate);
} else { } else {
$generate->addArray($closure); $generate->addArray($closure);
} }
return $generate->getSql(); return $generate->build();
} }
@@ -766,12 +737,28 @@ class QueryTrait extends Component implements ActiveQueryInterface, ISqlBuilder
/** /**
* @return $this * @param string $querySql
* @param array $params
* @return Command
*/ */
public function oneLimit(): static public function buildCommand(string $querySql, array $params = []): Command
{ {
$this->limit = 1; $connection = $this->modelClass->getConnection();
return $this; if (count($params) > 0) {
$this->bindParams($params);
}
return $connection->createCommand($querySql, $this->params);
} }
/**
* @return string
* @throws
*/
public function build(): string
{
return $this->builder->get();
}
} }