2022-01-09 03:49:51 +08:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* Created by PhpStorm.
|
|
|
|
|
* User: whwyy
|
|
|
|
|
* Date: 2018/3/30 0030
|
|
|
|
|
* Time: 14:39
|
|
|
|
|
*/
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Database;
|
|
|
|
|
|
|
|
|
|
|
2023-03-30 20:39:00 +08:00
|
|
|
use Database\Base\Getter;
|
2023-04-10 17:35:59 +08:00
|
|
|
use Database\Traits\HasBase;
|
2022-01-09 03:49:51 +08:00
|
|
|
use Exception;
|
2022-01-12 14:10:32 +08:00
|
|
|
use Kiri;
|
2022-01-18 18:06:24 +08:00
|
|
|
use Kiri\Exception\NotFindClassException;
|
2022-02-23 18:02:28 +08:00
|
|
|
use Kiri\Error\StdoutLoggerInterface;
|
2023-04-10 17:13:24 +08:00
|
|
|
use Psr\Container\ContainerExceptionInterface;
|
|
|
|
|
use Psr\Container\NotFoundExceptionInterface;
|
2022-01-09 03:49:51 +08:00
|
|
|
use ReflectionException;
|
|
|
|
|
|
|
|
|
|
defined('SAVE_FAIL') or define('SAVE_FAIL', 3227);
|
|
|
|
|
defined('FIND_OR_CREATE_MESSAGE') or define('FIND_OR_CREATE_MESSAGE', 'Create a new model, but the data cannot be empty.');
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class Orm
|
|
|
|
|
* @package Database
|
|
|
|
|
*
|
|
|
|
|
* @property $attributes
|
|
|
|
|
* @property-read $oldAttributes
|
|
|
|
|
*/
|
|
|
|
|
class Model extends Base\Model
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $column
|
|
|
|
|
* @param int $value
|
|
|
|
|
* @return ModelInterface|false
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function increment(string $column, int $value): bool|ModelInterface
|
|
|
|
|
{
|
|
|
|
|
if (!$this->mathematics([$column => $value], '+')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$this->{$column} += $value;
|
|
|
|
|
return $this->refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $column
|
|
|
|
|
* @param int $value
|
|
|
|
|
* @return ModelInterface|false
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function decrement(string $column, int $value): bool|ModelInterface
|
|
|
|
|
{
|
|
|
|
|
if (!$this->mathematics([$column => $value], '-')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$this->{$column} -= $value;
|
|
|
|
|
return $this->refresh();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $columns
|
|
|
|
|
* @return ModelInterface|false
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function increments(array $columns): bool|static
|
|
|
|
|
{
|
|
|
|
|
if (!$this->mathematics($columns, '+')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
foreach ($columns as $key => $attribute) {
|
|
|
|
|
$this->$key += $attribute;
|
|
|
|
|
}
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $columns
|
|
|
|
|
* @return ModelInterface|false
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function decrements(array $columns): bool|static
|
|
|
|
|
{
|
|
|
|
|
if (!$this->mathematics($columns, '-')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
foreach ($columns as $key => $attribute) {
|
|
|
|
|
$this->$key -= $attribute;
|
|
|
|
|
}
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $condition
|
|
|
|
|
* @param array $attributes
|
2023-04-10 17:13:24 +08:00
|
|
|
* @return bool|static
|
|
|
|
|
* @throws ContainerExceptionInterface
|
|
|
|
|
* @throws NotFoundExceptionInterface
|
2022-01-09 03:49:51 +08:00
|
|
|
*/
|
|
|
|
|
public static function findOrCreate(array $condition, array $attributes): bool|static
|
|
|
|
|
{
|
2023-04-10 17:13:24 +08:00
|
|
|
return Db::Transaction(function ($condition, $attributes) {
|
|
|
|
|
/** @var static $select */
|
|
|
|
|
$select = static::query()->where($condition)->first();
|
|
|
|
|
if ($select === null) {
|
|
|
|
|
$select = static::populate(array_merge($condition, $attributes))->create();
|
|
|
|
|
}
|
2022-06-22 16:29:42 +08:00
|
|
|
return $select;
|
2023-04-10 17:13:24 +08:00
|
|
|
}, $condition, $attributes);
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $condition
|
|
|
|
|
* @param array $attributes
|
|
|
|
|
* @return bool|static
|
2023-04-10 17:13:24 +08:00
|
|
|
* @throws ContainerExceptionInterface
|
|
|
|
|
* @throws NotFoundExceptionInterface
|
2022-01-09 03:49:51 +08:00
|
|
|
*/
|
|
|
|
|
public static function createOrUpdate(array $condition, array $attributes = []): bool|static
|
|
|
|
|
{
|
2023-04-10 17:13:24 +08:00
|
|
|
return Db::Transaction(function ($condition, $attributes) {
|
|
|
|
|
/** @var static $select */
|
|
|
|
|
$select = static::query()->where($condition)->first();
|
|
|
|
|
if (empty($select)) {
|
|
|
|
|
$select = static::populate($condition);
|
|
|
|
|
}
|
|
|
|
|
return $select->save($attributes);
|
|
|
|
|
}, $condition, $attributes);
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $columns
|
2022-06-22 16:29:42 +08:00
|
|
|
* @param $action
|
2022-01-09 03:49:51 +08:00
|
|
|
* @return array|bool|int|string|null
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2022-06-22 16:29:42 +08:00
|
|
|
private function mathematics($columns, $action): int|bool|array|string|null
|
2022-01-09 03:49:51 +08:00
|
|
|
{
|
2022-06-22 16:29:42 +08:00
|
|
|
$condition = [$this->getPrimary() => $this->getPrimaryValue()];
|
2022-01-09 03:49:51 +08:00
|
|
|
|
|
|
|
|
$activeQuery = static::query()->where($condition);
|
|
|
|
|
$create = SqlBuilder::builder($activeQuery)->mathematics($columns, $action);
|
|
|
|
|
if (is_bool($create)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-04-10 17:13:24 +08:00
|
|
|
return $this->getConnection()->createCommand($create, $activeQuery->attributes)->exec();
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-10 17:13:24 +08:00
|
|
|
* @param array $params
|
2022-01-09 03:49:51 +08:00
|
|
|
* @return ModelInterface|bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2023-04-10 17:13:24 +08:00
|
|
|
public function update(array $params): static|bool
|
2022-01-09 03:49:51 +08:00
|
|
|
{
|
2023-04-10 17:13:24 +08:00
|
|
|
if (!$this->validator($this->rules()) || !$this->beforeSave($this)) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$condition = array_diff_assoc($this->_oldAttributes, $params);
|
|
|
|
|
|
|
|
|
|
$old = array_intersect_key($this->_oldAttributes, $params);
|
|
|
|
|
|
|
|
|
|
return $this->updateInternal($old, $condition, $params);
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $data
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function inserts(array $data): bool
|
|
|
|
|
{
|
|
|
|
|
if (empty($data)) {
|
2022-06-22 16:29:42 +08:00
|
|
|
return error('Insert data empty.', 'mysql');
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
2022-09-07 14:36:32 +08:00
|
|
|
return static::query()->insert($data);
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function delete(): bool
|
|
|
|
|
{
|
2023-04-10 17:13:24 +08:00
|
|
|
if ($this->beforeDelete()) {
|
|
|
|
|
$result = static::deleteByCondition($this->_attributes);
|
|
|
|
|
|
|
|
|
|
return $this->afterDelete($result);
|
|
|
|
|
} else {
|
2022-06-22 16:29:42 +08:00
|
|
|
return false;
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param mixed $condition
|
|
|
|
|
* @param array $attributes
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function updateAll(mixed $condition, array $attributes = []): bool
|
|
|
|
|
{
|
|
|
|
|
$condition = static::query()->where($condition);
|
2022-09-07 14:36:32 +08:00
|
|
|
return $condition->update($attributes);
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $condition
|
|
|
|
|
* @return array|Collection
|
2023-04-10 17:42:12 +08:00
|
|
|
* @throws Exception
|
2022-01-09 03:49:51 +08:00
|
|
|
*/
|
|
|
|
|
public static function get($condition): Collection|array
|
|
|
|
|
{
|
|
|
|
|
return static::query()->where($condition)->all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $condition
|
|
|
|
|
* @param array $attributes
|
|
|
|
|
*
|
|
|
|
|
* @return array|Collection
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function findAll($condition, array $attributes = []): array|Collection
|
|
|
|
|
{
|
|
|
|
|
$query = static::query()->where($condition);
|
|
|
|
|
if (!empty($attributes)) {
|
|
|
|
|
$query->bindParams($attributes);
|
|
|
|
|
}
|
|
|
|
|
return $query->all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function toArray(): array
|
|
|
|
|
{
|
|
|
|
|
$data = $this->_attributes;
|
2023-04-10 17:13:24 +08:00
|
|
|
foreach ($data as $key => $datum) {
|
2023-04-10 11:36:33 +08:00
|
|
|
$method = 'get' . ucfirst($key) . 'Attribute';
|
2023-04-10 17:13:24 +08:00
|
|
|
if (!method_exists($this, $method)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2023-04-10 11:36:33 +08:00
|
|
|
$data[$key] = $this->{$method}($datum);
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
2023-04-10 17:39:36 +08:00
|
|
|
return $this->withs($data);
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-09 03:49:51 +08:00
|
|
|
|
2023-04-10 17:39:36 +08:00
|
|
|
/**
|
|
|
|
|
* @param $data
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
private function withs($data): array
|
|
|
|
|
{
|
2023-04-10 17:13:24 +08:00
|
|
|
$with = $this->getWith();
|
|
|
|
|
foreach ($with as $value) {
|
|
|
|
|
$join = $this->{'get' . ucfirst($value)}();
|
2023-04-10 17:35:59 +08:00
|
|
|
if ($join instanceof HasBase) {
|
|
|
|
|
$join = $join->get();
|
|
|
|
|
}
|
2023-04-10 17:13:24 +08:00
|
|
|
if ($join instanceof Kiri\ToArray) {
|
|
|
|
|
$join = $join->toArray();
|
|
|
|
|
}
|
|
|
|
|
$data[$value] = $join;
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
2023-04-10 17:13:24 +08:00
|
|
|
return $data;
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
2023-04-10 11:36:33 +08:00
|
|
|
|
|
|
|
|
|
2022-01-09 03:49:51 +08:00
|
|
|
/**
|
2023-04-01 17:11:47 +08:00
|
|
|
* @param ModelInterface|string $modelName
|
2022-01-09 03:49:51 +08:00
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
2023-04-01 20:48:03 +08:00
|
|
|
* @return string
|
2022-01-09 03:49:51 +08:00
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2023-04-10 11:36:33 +08:00
|
|
|
private function _hasBase(ModelInterface|string $modelName, $foreignKey, $localKey): string
|
|
|
|
|
{
|
2022-01-09 03:49:51 +08:00
|
|
|
if (($value = $this->{$localKey}) === null) {
|
|
|
|
|
throw new Exception("Need join table primary key.");
|
|
|
|
|
}
|
2023-04-10 11:36:33 +08:00
|
|
|
|
2022-01-09 03:49:51 +08:00
|
|
|
$relation = $this->getRelation();
|
2023-04-10 11:36:33 +08:00
|
|
|
|
2023-04-01 17:11:47 +08:00
|
|
|
$primaryKey = $modelName . $foreignKey . $value;
|
|
|
|
|
if (!$relation->hasIdentification($primaryKey)) {
|
|
|
|
|
$relation->bindIdentification($primaryKey, $modelName::query()->where([$foreignKey => $value]));
|
|
|
|
|
}
|
2023-04-01 20:48:03 +08:00
|
|
|
return $primaryKey;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-01 17:11:47 +08:00
|
|
|
|
2023-04-01 20:48:03 +08:00
|
|
|
/**
|
|
|
|
|
* @param ModelInterface|string $modelName
|
|
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
|
|
|
|
* @return HasOne|ActiveQuery
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function hasOne(ModelInterface|string $modelName, $foreignKey, $localKey): HasOne|ActiveQuery
|
|
|
|
|
{
|
|
|
|
|
return new HasOne($this->_hasBase($modelName, $foreignKey, $localKey));
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-01 17:11:47 +08:00
|
|
|
* @param ModelInterface|string $modelName
|
2022-01-09 03:49:51 +08:00
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
|
|
|
|
* @return ActiveQuery|HasCount
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2023-04-01 17:11:47 +08:00
|
|
|
public function hasCount(ModelInterface|string $modelName, $foreignKey, $localKey): ActiveQuery|HasCount
|
2022-01-09 03:49:51 +08:00
|
|
|
{
|
2023-04-01 20:48:03 +08:00
|
|
|
return new HasCount($this->_hasBase($modelName, $foreignKey, $localKey));
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-01 17:11:47 +08:00
|
|
|
* @param ModelInterface|string $modelName
|
2022-01-09 03:49:51 +08:00
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
|
|
|
|
* @return ActiveQuery|HasMany
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2023-04-01 17:11:47 +08:00
|
|
|
public function hasMany(ModelInterface|string $modelName, $foreignKey, $localKey): ActiveQuery|HasMany
|
2022-01-09 03:49:51 +08:00
|
|
|
{
|
2023-04-01 20:48:03 +08:00
|
|
|
return new HasMany($this->_hasBase($modelName, $foreignKey, $localKey));
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-01 17:11:47 +08:00
|
|
|
* @param ModelInterface|string $modelName
|
2022-01-09 03:49:51 +08:00
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
|
|
|
|
* @return ActiveQuery|HasMany
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2023-04-01 17:11:47 +08:00
|
|
|
public function hasIn(ModelInterface|string $modelName, $foreignKey, $localKey): ActiveQuery|HasMany
|
2022-01-09 03:49:51 +08:00
|
|
|
{
|
|
|
|
|
if (($value = $this->{$localKey}) === null) {
|
|
|
|
|
throw new Exception("Need join table primary key.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$relation = $this->getRelation();
|
|
|
|
|
|
2023-04-01 17:11:47 +08:00
|
|
|
$primaryKey = $modelName . $foreignKey . json_encode($value, JSON_UNESCAPED_UNICODE);
|
|
|
|
|
if (!$relation->hasIdentification($primaryKey)) {
|
|
|
|
|
$relation->bindIdentification($primaryKey, $modelName::query()->whereIn($foreignKey, $value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new HasMany($primaryKey);
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param bool $result
|
2023-04-10 17:13:24 +08:00
|
|
|
* @return bool
|
2022-01-09 03:49:51 +08:00
|
|
|
*/
|
2023-04-10 17:13:24 +08:00
|
|
|
public function afterDelete(bool $result): bool
|
2022-01-09 03:49:51 +08:00
|
|
|
{
|
2023-04-10 17:13:24 +08:00
|
|
|
return $result;
|
2022-01-09 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function beforeDelete(): bool
|
|
|
|
|
{
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|