370 lines
10 KiB
PHP
370 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* Created by PhpStorm.
|
|
* User: whwyy
|
|
* Date: 2018/3/30 0030
|
|
* Time: 14:39
|
|
*/
|
|
declare(strict_types=1);
|
|
|
|
namespace Database;
|
|
|
|
|
|
use Database\Base\ActiveQueryInterface;
|
|
use Exception;
|
|
|
|
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
|
|
*
|
|
* @mixin ActiveQueryInterface
|
|
*/
|
|
class Model extends Base\Model
|
|
{
|
|
|
|
|
|
/**
|
|
* @param string $column
|
|
* @param int|float $value
|
|
* @return ModelInterface|false
|
|
* @throws
|
|
*/
|
|
public function increment(string $column, int|float $value): bool|ModelInterface
|
|
{
|
|
if (!$this->mathematics([$column => $value], '+')) {
|
|
return false;
|
|
}
|
|
$this->{$column} += $value;
|
|
return $this->refresh();
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $column
|
|
* @param int|float $value
|
|
* @return ModelInterface|false
|
|
* @throws
|
|
*/
|
|
public function decrement(string $column, int|float $value): bool|ModelInterface
|
|
{
|
|
if (!$this->mathematics([$column => $value], '-')) {
|
|
return false;
|
|
}
|
|
$this->{$column} -= $value;
|
|
return $this->refresh();
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $columns
|
|
* @return ModelInterface|false
|
|
* @throws
|
|
*/
|
|
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
|
|
*/
|
|
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
|
|
* @return bool|static
|
|
* @throws
|
|
*/
|
|
public static function findOrCreate(array $condition, array $attributes): bool|static
|
|
{
|
|
$model = static::instance();
|
|
$connection = $model->getConnection()->beginTransaction();
|
|
try {
|
|
/** @var static $select */
|
|
$select = $model::query()->where($condition)->first();
|
|
if ($select === null) {
|
|
$select = $model::populate(array_merge($condition, $attributes));
|
|
if (!$select->save()) {
|
|
throw new Exception('保存失败: ' . $model->getLastError());
|
|
}
|
|
}
|
|
$connection->commit();
|
|
return $select;
|
|
} catch (\Throwable $throwable) {
|
|
$connection->rollback();
|
|
return \Kiri::getLogger()->failure($throwable);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $condition
|
|
* @param array $attributes
|
|
* @return bool|static
|
|
* @throws
|
|
*/
|
|
public static function createOrUpdate(array $condition, array $attributes = []): bool|static
|
|
{
|
|
$model = static::instance();
|
|
$connection = $model->getConnection()->beginTransaction();
|
|
try {
|
|
/** @var static $select */
|
|
$select = static::query()->where($condition)->first();
|
|
if (empty($select)) {
|
|
$select = static::populate($condition);
|
|
}
|
|
$select->attributes = $attributes;
|
|
if (!$select->save()) {
|
|
throw new Exception('保存失败: ' . $model->getLastError());
|
|
}
|
|
$connection->commit();
|
|
return $select;
|
|
} catch (\Throwable $throwable) {
|
|
$connection->rollback();
|
|
return \Kiri::getLogger()->failure($throwable);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param array $arguments
|
|
* @return Model|ActiveQuery
|
|
*/
|
|
public static function __callStatic(string $name, array $arguments): static|ActiveQuery
|
|
{
|
|
// TODO: Change the autogenerated stub
|
|
if (method_exists(static::class, $name)) {
|
|
return call_user_func(static::class . '::' . $name, ...$arguments);
|
|
} else {
|
|
return static::query()->{$name}(...$arguments);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $columns
|
|
* @param string $action
|
|
* @return array|bool|int|string|null
|
|
*/
|
|
private function mathematics(array $columns, string $action): int|bool|array|string|null
|
|
{
|
|
$condition = [$this->getPrimary() => $this->getPrimaryValue()];
|
|
$activeQuery = static::query()->where($condition);
|
|
$create = SqlBuilder::builder($activeQuery)->mathematics($columns, $action);
|
|
if (is_bool($create)) {
|
|
return false;
|
|
}
|
|
return $activeQuery->buildCommand($create)->exec();
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $params
|
|
* @return ModelInterface|bool
|
|
* @throws
|
|
*/
|
|
public function update(array $params): static|bool
|
|
{
|
|
if (!$this->validator($this->rules(), $params) || !$this->beforeSave($this)) {
|
|
return FALSE;
|
|
}
|
|
return $this->updateInternal(...$this->arrayIntersect($params));
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $data
|
|
* @return bool
|
|
* @throws
|
|
*/
|
|
public static function inserts(array $data): bool
|
|
{
|
|
if (empty($data)) {
|
|
return trigger_print_error('Insert data empty.', 'mysql');
|
|
}
|
|
return static::query()->insert($data);
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
* @throws
|
|
*/
|
|
public function delete(): bool
|
|
{
|
|
if (!$this->beforeDelete()) {
|
|
return false;
|
|
}
|
|
if ($this->hasPrimary()) {
|
|
$result = static::deleteByCondition(['id' => $this->getPrimaryValue()]);
|
|
} else {
|
|
$result = static::deleteByCondition($this->_attributes);
|
|
}
|
|
return $this->afterDelete($result);
|
|
}
|
|
|
|
|
|
/**
|
|
* @return array
|
|
* @throws
|
|
*/
|
|
public function toArray(): array
|
|
{
|
|
$data = $this->_attributes;
|
|
foreach ($data as $key => $datum) {
|
|
$method = 'get' . ucfirst($key) . 'Attribute';
|
|
if (!method_exists($this, $method)) {
|
|
continue;
|
|
}
|
|
$data[$key] = $this->{$method}($datum);
|
|
}
|
|
return $this->withs($data);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param array $data
|
|
* @return array
|
|
*/
|
|
private function withs(array $data): array
|
|
{
|
|
$with = $this->getWith();
|
|
foreach ($with as $value) {
|
|
$join = $this->withRelate($value);
|
|
if ($join instanceof \Arrayable) {
|
|
$join = $join->toArray();
|
|
}
|
|
$data[$value] = $join;
|
|
}
|
|
return $data;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $modelName
|
|
* @param string $foreignKey
|
|
* @param string $localKey
|
|
* @return string
|
|
* @throws Exception
|
|
*/
|
|
private function _hasBase(string $modelName, string $foreignKey, string $localKey): string
|
|
{
|
|
if (($value = $this->{$localKey}) === null) {
|
|
throw new Exception("Need join table primary key.");
|
|
}
|
|
|
|
$relation = di(Relation::class);
|
|
|
|
$primaryKey = str_replace('\\\\', '_', $modelName) . '_' . $foreignKey . '_' . $value;
|
|
if (!$relation->hasIdentification($primaryKey)) {
|
|
$relation->bindIdentification($primaryKey, $modelName::query()->where([$foreignKey => $value]));
|
|
}
|
|
return $primaryKey;
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $modelName
|
|
* @param string $foreignKey
|
|
* @param string $localKey
|
|
* @return HasOne|ActiveQuery
|
|
* @throws Exception
|
|
*/
|
|
public function hasOne(string $modelName, string $foreignKey, string $localKey): HasOne|ActiveQuery
|
|
{
|
|
return new HasOne($this->_hasBase($modelName, $foreignKey, $localKey));
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $modelName
|
|
* @param string $foreignKey
|
|
* @param string $localKey
|
|
* @return ActiveQuery|HasCount
|
|
* @throws Exception
|
|
*/
|
|
public function hasCount(string $modelName, string $foreignKey, string $localKey): ActiveQuery|HasCount
|
|
{
|
|
return new HasCount($this->_hasBase($modelName, $foreignKey, $localKey));
|
|
}
|
|
|
|
|
|
/**
|
|
* @param string $modelName
|
|
* @param string $foreignKey
|
|
* @param string $localKey
|
|
* @return ActiveQuery|HasMany
|
|
* @throws Exception
|
|
*/
|
|
public function hasMany(string $modelName, string $foreignKey, string $localKey): ActiveQuery|HasMany
|
|
{
|
|
return new HasMany($this->_hasBase($modelName, $foreignKey, $localKey));
|
|
}
|
|
|
|
/**
|
|
* @param string $modelName
|
|
* @param string $foreignKey
|
|
* @param string $localKey
|
|
* @return ActiveQuery|HasMany
|
|
* @throws Exception
|
|
*/
|
|
public function hasIn(string $modelName, string $foreignKey, string $localKey): ActiveQuery|HasMany
|
|
{
|
|
if (($value = $this->{$localKey}) === null) {
|
|
throw new Exception("Need join table primary key.");
|
|
}
|
|
|
|
$relation = di(Relation::class);
|
|
|
|
$primaryKey = str_replace('\\\\', '_', $modelName) . '_' . $foreignKey . '_' . implode('_', $value);
|
|
if (!$relation->hasIdentification($primaryKey)) {
|
|
$relation->bindIdentification($primaryKey, $modelName::query()->whereIn($foreignKey, $value));
|
|
}
|
|
|
|
return new HasMany($primaryKey);
|
|
}
|
|
|
|
/**
|
|
* @param bool $result
|
|
* @return bool
|
|
*/
|
|
public function afterDelete(bool $result): bool
|
|
{
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
* @throws Exception
|
|
*/
|
|
public function beforeDelete(): bool
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|