2021-08-11 15:03:28 +08:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* Created by PhpStorm.
|
|
|
|
|
* User: whwyy
|
|
|
|
|
* Date: 2018/3/30 0030
|
|
|
|
|
* Time: 14:39
|
|
|
|
|
*/
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2021-09-28 18:57:30 +08:00
|
|
|
namespace Database;
|
2021-08-11 15:03:28 +08:00
|
|
|
|
2021-09-28 18:54:07 +08:00
|
|
|
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.');
|
|
|
|
|
|
2021-08-11 15:03:28 +08:00
|
|
|
|
|
|
|
|
use Annotation\Inject;
|
|
|
|
|
use ArrayAccess;
|
2021-08-17 16:22:18 +08:00
|
|
|
use Closure;
|
2021-08-11 15:03:28 +08:00
|
|
|
use Database\Mysql\Columns;
|
|
|
|
|
use Database\Traits\HasBase;
|
|
|
|
|
use Exception;
|
|
|
|
|
use JetBrains\PhpStorm\Pure;
|
|
|
|
|
use Kiri\Abstracts\Component;
|
|
|
|
|
use Kiri\Application;
|
|
|
|
|
use Kiri\Events\EventDispatch;
|
|
|
|
|
use Kiri\Exception\NotFindClassException;
|
|
|
|
|
use Kiri\Kiri;
|
2021-08-31 17:18:21 +08:00
|
|
|
use Kiri\ToArray;
|
2021-08-17 16:22:18 +08:00
|
|
|
use ReflectionException;
|
2021-08-11 15:03:28 +08:00
|
|
|
use validator\Validator;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class BOrm
|
|
|
|
|
*
|
|
|
|
|
* @package Kiri\Abstracts
|
|
|
|
|
*
|
|
|
|
|
* @property bool $isCreate
|
|
|
|
|
* @property Application $container
|
|
|
|
|
* @property EventDispatch $eventDispatch
|
2021-09-29 01:46:27 +08:00
|
|
|
* @property array $attributes
|
|
|
|
|
* @property array $oldAttributes
|
2021-08-11 15:03:28 +08:00
|
|
|
*/
|
2021-09-28 18:54:07 +08:00
|
|
|
abstract class Model extends Component implements ModelInterface, ArrayAccess, ToArray
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
|
|
|
|
|
2021-09-28 18:54:07 +08:00
|
|
|
const GET = 'get';
|
2021-08-11 15:03:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
const SET = 'set';
|
|
|
|
|
|
|
|
|
|
/** @var array */
|
|
|
|
|
protected array $_attributes = [];
|
|
|
|
|
|
|
|
|
|
/** @var array */
|
|
|
|
|
protected array $_oldAttributes = [];
|
|
|
|
|
|
|
|
|
|
/** @var array */
|
|
|
|
|
protected array $_relate = [];
|
|
|
|
|
|
|
|
|
|
/** @var null|string */
|
|
|
|
|
protected ?string $primary = NULL;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
private array $_annotations = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var bool
|
|
|
|
|
*/
|
|
|
|
|
protected bool $isNewExample = TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
protected array $actions = [];
|
|
|
|
|
|
|
|
|
|
|
2021-09-28 18:54:07 +08:00
|
|
|
/**
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
2021-08-31 17:18:21 +08:00
|
|
|
protected string $table = '';
|
|
|
|
|
|
|
|
|
|
|
2021-09-28 18:54:07 +08:00
|
|
|
/**
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
2021-08-31 17:18:21 +08:00
|
|
|
protected string $connection = 'db';
|
2021-08-11 15:03:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var Relation|null
|
|
|
|
|
*/
|
|
|
|
|
#[Inject(Relation::class)]
|
|
|
|
|
protected ?Relation $_relation;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @var array
|
|
|
|
|
*/
|
|
|
|
|
private array $_with = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return Application
|
|
|
|
|
*/
|
|
|
|
|
#[Pure] protected function getContainer(): Application
|
|
|
|
|
{
|
|
|
|
|
return Kiri::app();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-17 16:22:18 +08:00
|
|
|
/**
|
|
|
|
|
* @param string $name
|
|
|
|
|
* @param mixed $value
|
|
|
|
|
*/
|
2021-09-28 18:54:07 +08:00
|
|
|
private function _setter(string $name, mixed $value): void
|
2021-08-17 16:22:18 +08:00
|
|
|
{
|
|
|
|
|
$method = di(Setter::class)->getSetter(static::class, $name);
|
|
|
|
|
if (!empty($method)) {
|
|
|
|
|
$value = $this->{$method}($value);
|
|
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
$this->_attributes[$name] = $value;
|
2021-08-17 16:22:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $name
|
|
|
|
|
* @param $value
|
|
|
|
|
* @return mixed
|
|
|
|
|
* @throws NotFindClassException
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
*/
|
|
|
|
|
private function _getter(string $name, $value): mixed
|
|
|
|
|
{
|
|
|
|
|
$data = di(Getter::class)->getGetter(static::class, $name);
|
|
|
|
|
if (empty($data)) {
|
|
|
|
|
return $this->_relater($name, $value);
|
|
|
|
|
}
|
|
|
|
|
return $this->{$data}($value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $name
|
|
|
|
|
* @param $value
|
|
|
|
|
* @return mixed
|
|
|
|
|
* @throws NotFindClassException
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function _relater(string $name, $value): mixed
|
|
|
|
|
{
|
|
|
|
|
$data = di(Relate::class)->getRelate(static::class, $name);
|
|
|
|
|
if (!empty($data)) {
|
|
|
|
|
$data = $this->{$data}();
|
|
|
|
|
if ($data instanceof HasBase) {
|
|
|
|
|
return $data->get();
|
|
|
|
|
}
|
|
|
|
|
return $data;
|
|
|
|
|
}
|
|
|
|
|
return $this->_decode($name, $value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-11 15:03:28 +08:00
|
|
|
/**
|
|
|
|
|
* @return EventDispatch
|
|
|
|
|
*/
|
|
|
|
|
protected function getEventDispatch(): EventDispatch
|
|
|
|
|
{
|
|
|
|
|
return Kiri::getDi()->get(EventDispatch::class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $data
|
2021-09-28 18:54:07 +08:00
|
|
|
* @return ModelInterface
|
2021-08-11 15:03:28 +08:00
|
|
|
*/
|
|
|
|
|
public function setWith($data): static
|
|
|
|
|
{
|
|
|
|
|
if (empty($data)) {
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
$this->_with = $data;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array|null
|
|
|
|
|
*/
|
|
|
|
|
public function getWith(): array|null
|
|
|
|
|
{
|
|
|
|
|
return $this->_with;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* object init
|
|
|
|
|
*/
|
|
|
|
|
public function clean()
|
|
|
|
|
{
|
|
|
|
|
$this->_attributes = [];
|
|
|
|
|
$this->_oldAttributes = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param Relation $relation
|
|
|
|
|
*/
|
|
|
|
|
public function setRelation(Relation $relation)
|
|
|
|
|
{
|
|
|
|
|
$this->_relation = $relation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function init()
|
|
|
|
|
{
|
|
|
|
|
$an = Kiri::app()->getAnnotation();
|
|
|
|
|
$an->injectProperty($this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getActions(): array
|
|
|
|
|
{
|
|
|
|
|
return $this->actions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function getIsCreate(): bool
|
|
|
|
|
{
|
|
|
|
|
return $this->isNewExample === TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param bool $bool
|
|
|
|
|
* @return $this
|
|
|
|
|
*/
|
|
|
|
|
public function setIsCreate(bool $bool = FALSE): static
|
|
|
|
|
{
|
|
|
|
|
$this->isNewExample = $bool;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2021-08-31 17:18:21 +08:00
|
|
|
* @return string
|
2021-08-11 15:03:28 +08:00
|
|
|
* @throws Exception
|
|
|
|
|
* get last exception or other error
|
|
|
|
|
*/
|
2021-08-31 17:18:21 +08:00
|
|
|
public function getLastError(): string
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
|
|
|
|
return Kiri::app()->getLogger()->getLastError('mysql');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function hasPrimary(): bool
|
|
|
|
|
{
|
|
|
|
|
if ($this->primary !== NULL) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
$primary = $this->getColumns()->getPrimaryKeys();
|
2021-08-11 15:03:28 +08:00
|
|
|
if (!empty($primary)) {
|
|
|
|
|
return $this->primary = is_array($primary) ? current($primary) : $primary;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function isAutoIncrement(): bool
|
|
|
|
|
{
|
|
|
|
|
return $this->getAutoIncrement() !== null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function getAutoIncrement(): int|string|null
|
|
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
return $this->getColumns()->getAutoIncrement();
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return null|string
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function getPrimary(): ?string
|
|
|
|
|
{
|
|
|
|
|
if (!$this->hasPrimary()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return $this->primary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return int|null
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function getPrimaryValue(): ?int
|
|
|
|
|
{
|
|
|
|
|
if (!$this->hasPrimary()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return $this->getAttribute($this->primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $param
|
|
|
|
|
* @param null $db
|
2021-09-28 18:54:07 +08:00
|
|
|
* @return Model|null
|
2021-08-11 15:03:28 +08:00
|
|
|
* @throws NotFindClassException
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function findOne($param, $db = NULL): static|null
|
|
|
|
|
{
|
|
|
|
|
if (is_bool($param)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (is_numeric($param)) {
|
|
|
|
|
$param = static::getPrimaryCondition($param);
|
|
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
return static::query()->where($param)->first();
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $param
|
|
|
|
|
* @return array
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private static function getPrimaryCondition($param): array
|
|
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
$primary = static::makeNewInstance()->getColumns()->getPrimaryKeys();
|
2021-08-11 15:03:28 +08:00
|
|
|
if (empty($primary)) {
|
|
|
|
|
throw new Exception('Primary key cannot be empty.');
|
|
|
|
|
}
|
|
|
|
|
if (is_array($primary)) {
|
|
|
|
|
$primary = current($primary);
|
|
|
|
|
}
|
|
|
|
|
return [$primary => $param];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param null $field
|
2021-09-28 18:54:07 +08:00
|
|
|
* @return ModelInterface|null
|
2021-08-11 15:03:28 +08:00
|
|
|
* @throws Exception
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2021-09-28 18:54:07 +08:00
|
|
|
public static function max($field = null): ?ModelInterface
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
$columns = static::makeNewInstance()->getColumns();
|
2021-08-11 15:03:28 +08:00
|
|
|
if (empty($field)) {
|
|
|
|
|
$field = $columns->getFirstPrimary();
|
|
|
|
|
}
|
|
|
|
|
$columns = $columns->get_fields();
|
|
|
|
|
if (!isset($columns[$field])) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
$first = static::query()->max($field)->first();
|
2021-08-11 15:03:28 +08:00
|
|
|
if (empty($first)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return $first[$field];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-28 18:54:07 +08:00
|
|
|
* @param string|int $param
|
|
|
|
|
* @return Model|null
|
|
|
|
|
* @throws NotFindClassException
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function find(string|int $param): ?static
|
|
|
|
|
{
|
|
|
|
|
$columns = duplicate(static::class)->getPrimary();
|
|
|
|
|
if (empty($columns)) {
|
|
|
|
|
$columns = static::makeNewInstance()->getColumns()->getFirstPrimary();
|
|
|
|
|
}
|
|
|
|
|
return static::query()->where([$columns => $param])->first();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return static
|
|
|
|
|
*/
|
|
|
|
|
private static function makeNewInstance(): static
|
|
|
|
|
{
|
|
|
|
|
return duplicate(static::class);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $condition
|
|
|
|
|
* @return static|null
|
|
|
|
|
* @throws NotFindClassException
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
* @throws Exception
|
2021-08-11 15:03:28 +08:00
|
|
|
*/
|
2021-09-28 18:54:07 +08:00
|
|
|
public static function first($condition): ?static
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
return static::query()->where($condition)->first();
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return ActiveQuery
|
|
|
|
|
*/
|
|
|
|
|
public static function query(): ActiveQuery
|
|
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
return new ActiveQuery(new static());
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-28 18:54:07 +08:00
|
|
|
* @return Connection
|
2021-08-11 15:03:28 +08:00
|
|
|
*/
|
2021-09-28 18:54:07 +08:00
|
|
|
public function getConnection(): Connection
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
return Kiri::app()->get('db')->get($this->connection);
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param null $condition
|
|
|
|
|
* @param array $attributes
|
|
|
|
|
*
|
|
|
|
|
* @param bool $if_condition_is_null
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2021-08-17 16:22:18 +08:00
|
|
|
protected static function deleteByCondition($condition = NULL, array $attributes = [], bool $if_condition_is_null = false): bool
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
|
|
|
|
if (empty($condition)) {
|
|
|
|
|
if (!$if_condition_is_null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
return static::query()->delete();
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
$model = static::query()->ifNotWhere($if_condition_is_null)->where($condition);
|
2021-08-11 15:03:28 +08:00
|
|
|
if (!empty($attributes)) {
|
|
|
|
|
$model->bindParams($attributes);
|
|
|
|
|
}
|
|
|
|
|
return $model->delete();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function getAttributes(): array
|
|
|
|
|
{
|
|
|
|
|
return $this->toArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getOldAttributes(): array
|
|
|
|
|
{
|
|
|
|
|
return $this->_oldAttributes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @param $value
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
public function setAttribute($name, $value): mixed
|
|
|
|
|
{
|
|
|
|
|
return $this->_attributes[$name] = $value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @param $value
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
public function setOldAttribute($name, $value): mixed
|
|
|
|
|
{
|
|
|
|
|
return $this->_oldAttributes[$name] = $value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $param
|
|
|
|
|
* @return $this
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function setAttributes(array $param): static
|
|
|
|
|
{
|
|
|
|
|
if (empty($param)) {
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
$this->_attributes = array_merge($this->_attributes, $param);
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $param
|
|
|
|
|
* @return $this
|
|
|
|
|
*/
|
|
|
|
|
public function setOldAttributes($param): static
|
|
|
|
|
{
|
|
|
|
|
if (empty($param) || !is_array($param)) {
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
foreach ($param as $key => $val) {
|
|
|
|
|
$this->setOldAttribute($key, $val);
|
|
|
|
|
}
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $attributes
|
|
|
|
|
* @param $param
|
|
|
|
|
* @return $this|bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function insert($param, $attributes): bool|static
|
|
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
[$sql, $param] = SqlBuilder::builder(static::query())->insert($param);
|
|
|
|
|
$dbConnection = $this->getConnection()->createCommand($sql, $param);
|
2021-08-11 15:03:28 +08:00
|
|
|
if (!($lastId = (int)$dbConnection->save(true, $this))) {
|
|
|
|
|
throw new Exception('保存失败.');
|
|
|
|
|
}
|
|
|
|
|
$lastId = $this->setPrimary($lastId, $param);
|
|
|
|
|
|
|
|
|
|
$this->refresh()->afterSave($attributes, $param);
|
|
|
|
|
|
|
|
|
|
return $lastId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $lastId
|
|
|
|
|
* @param $param
|
|
|
|
|
* @return static
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function setPrimary($lastId, $param): static
|
|
|
|
|
{
|
|
|
|
|
if ($this->isAutoIncrement()) {
|
|
|
|
|
$this->setAttribute($this->getAutoIncrement(), (int)$lastId);
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$this->hasPrimary()) {
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$primary = $this->getPrimary();
|
|
|
|
|
if (!isset($param[$primary]) || empty($param[$primary])) {
|
|
|
|
|
$this->setAttribute($primary, (int)$lastId);
|
|
|
|
|
}
|
|
|
|
|
return $this->setAttributes($param);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $fields
|
|
|
|
|
* @param $condition
|
|
|
|
|
* @param $param
|
|
|
|
|
* @return $this|bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function updateInternal($fields, $condition, $param): bool|static
|
|
|
|
|
{
|
|
|
|
|
if (empty($param)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if ($this->hasPrimary()) {
|
|
|
|
|
$condition = [$this->getPrimary() => $this->getPrimaryValue()];
|
|
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
$generate = SqlBuilder::builder(static::query()->where($condition))->update($param);
|
2021-08-11 15:03:28 +08:00
|
|
|
if (is_bool($generate)) {
|
|
|
|
|
return $generate;
|
|
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
$command = $this->getConnection()->createCommand($generate[0], $generate[1]);
|
2021-08-11 15:03:28 +08:00
|
|
|
if ($command->save(false, $this)) {
|
|
|
|
|
return $this->refresh()->afterSave($fields, $param);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param null $data
|
|
|
|
|
* @return bool|$this
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function save($data = NULL): static|bool
|
|
|
|
|
{
|
|
|
|
|
if (!is_null($data)) {
|
|
|
|
|
$this->_attributes = merge($this->_attributes, $data);
|
|
|
|
|
}
|
|
|
|
|
if (!$this->validator($this->rules()) || !$this->beforeSave($this)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
[$change, $condition, $fields] = $this->separation();
|
|
|
|
|
if (!$this->isNewExample) {
|
|
|
|
|
return $this->updateInternal($fields, $condition, $change);
|
|
|
|
|
}
|
|
|
|
|
return $this->insert($change, $fields);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $rule
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function validator(array $rule): bool
|
|
|
|
|
{
|
|
|
|
|
if (empty($rule)) return true;
|
|
|
|
|
$validate = $this->resolve($rule);
|
|
|
|
|
if (!$validate->validation()) {
|
|
|
|
|
return $this->addError($validate->getError(), 'mysql');
|
|
|
|
|
} else {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $rule
|
|
|
|
|
* @return Validator
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function resolve($rule): Validator
|
|
|
|
|
{
|
|
|
|
|
$validate = Validator::getInstance();
|
|
|
|
|
$validate->setParams($this->_attributes);
|
|
|
|
|
$validate->setModel($this);
|
|
|
|
|
foreach ($rule as $val) {
|
|
|
|
|
$field = array_shift($val);
|
|
|
|
|
if (empty($val)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$validate->make($field, $val);
|
|
|
|
|
}
|
|
|
|
|
return $validate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $name
|
|
|
|
|
* @return null
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function getAttribute(string $name)
|
|
|
|
|
{
|
|
|
|
|
if ($this->hasAnnotation($name)) {
|
|
|
|
|
return $this->runAnnotation($name, $this->_attributes[$name]);
|
|
|
|
|
}
|
|
|
|
|
return $this->_attributes[$name] ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $name
|
|
|
|
|
* @param mixed $value
|
|
|
|
|
* @param string $type
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
protected function runAnnotation(string $name, mixed $value, string $type = self::GET): mixed
|
|
|
|
|
{
|
|
|
|
|
return call_user_func($this->_annotations[$type][$name], $value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function separation(): array
|
|
|
|
|
{
|
|
|
|
|
$_tmp = [];
|
|
|
|
|
$condition = [];
|
|
|
|
|
foreach ($this->_attributes as $key => $val) {
|
|
|
|
|
$oldValue = $this->_oldAttributes[$key] ?? null;
|
|
|
|
|
if ($val === $oldValue) {
|
|
|
|
|
$condition[$key] = $val;
|
|
|
|
|
} else {
|
|
|
|
|
$_tmp[$key] = $val;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return [$_tmp, $condition, array_keys($_tmp)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $columns
|
|
|
|
|
* @param $format
|
|
|
|
|
* @param $key
|
|
|
|
|
* @param $value
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
public function toFormat($columns, $format, $key, $value): mixed
|
|
|
|
|
{
|
|
|
|
|
if (isset($format[$key])) {
|
|
|
|
|
return $columns->encode($value, $columns->clean($format[$key]));
|
|
|
|
|
}
|
|
|
|
|
return $value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @param $value
|
|
|
|
|
*/
|
|
|
|
|
public function setRelate($name, $value)
|
|
|
|
|
{
|
|
|
|
|
$this->_relate[$name] = $value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function hasRelate($name): bool
|
|
|
|
|
{
|
|
|
|
|
return isset($this->_relate[$name]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $relates
|
|
|
|
|
*/
|
|
|
|
|
public function setRelates(array $relates)
|
|
|
|
|
{
|
|
|
|
|
if (empty($relates)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
foreach ($relates as $key => $val) {
|
|
|
|
|
$this->setRelate($key, $val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getRelates(): array
|
|
|
|
|
{
|
|
|
|
|
return $this->_relate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return Relation|null
|
|
|
|
|
*/
|
|
|
|
|
public function getRelation(): ?Relation
|
|
|
|
|
{
|
|
|
|
|
return $this->_relation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @return array|string|null
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function getRelate($name): null|array|string
|
|
|
|
|
{
|
2021-08-17 16:22:18 +08:00
|
|
|
return di(Relate::class)->getRelate(static::class, $name);
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $attribute
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function has($attribute): bool
|
|
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
return static::makeNewInstance()->getColumns()->hasField($attribute);
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**ƒ
|
|
|
|
|
* @return string
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2021-09-28 18:54:07 +08:00
|
|
|
public function getTable(): string
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
$connection = static::getConnection();
|
2021-08-11 15:03:28 +08:00
|
|
|
|
2021-09-28 18:54:07 +08:00
|
|
|
$tablePrefix = $connection->tablePrefix;
|
|
|
|
|
if (empty($this->table)) {
|
2021-08-11 15:03:28 +08:00
|
|
|
throw new Exception('You need add static method `tableName` and return table name.');
|
|
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
$table = trim($this->table, '{{%}}');
|
|
|
|
|
if (!empty($tablePrefix) && !str_starts_with($this->table, $tablePrefix)) {
|
|
|
|
|
$table = $tablePrefix . $this->table;
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
2021-09-28 18:54:07 +08:00
|
|
|
return '`' . $connection->database . '`.' . $table;
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $attributes
|
|
|
|
|
* @param $changeAttributes
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function afterSave($attributes, $changeAttributes): bool
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $model
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function beforeSave($model): bool
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-09-28 18:54:07 +08:00
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function rules(): array
|
|
|
|
|
{
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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();
|
|
|
|
|
}
|
2021-08-11 15:03:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2021-09-28 18:54:07 +08:00
|
|
|
* @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
|
2021-08-11 15:03:28 +08:00
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2021-09-28 18:54:07 +08:00
|
|
|
public function decrements(array $columns): bool|static
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
if (!$this->mathematics($columns, '-')) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
foreach ($columns as $key => $attribute) {
|
|
|
|
|
$this->$key -= $attribute;
|
|
|
|
|
}
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $condition
|
|
|
|
|
* @param array $attributes
|
|
|
|
|
* @return bool|ModelInterface
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
* @throws NotFindClassException
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function findOrCreate(array $condition, array $attributes = []): bool|static
|
|
|
|
|
{
|
|
|
|
|
$logger = Kiri::app()->getLogger();
|
|
|
|
|
|
|
|
|
|
/** @var static $select */
|
|
|
|
|
$select = static::query()->where($condition)->first();
|
|
|
|
|
if (!empty($select)) {
|
|
|
|
|
return $select;
|
|
|
|
|
}
|
|
|
|
|
if (empty($attributes)) {
|
|
|
|
|
return $logger->addError(FIND_OR_CREATE_MESSAGE, 'mysql');
|
|
|
|
|
}
|
|
|
|
|
$select = duplicate(static::class);
|
2021-09-28 18:57:30 +08:00
|
|
|
$select->_attributes = $attributes;
|
2021-09-28 18:54:07 +08:00
|
|
|
if (!$select->save()) {
|
|
|
|
|
return $logger->addError($select->getLastError(), 'mysql');
|
|
|
|
|
}
|
|
|
|
|
return $select;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $condition
|
|
|
|
|
* @param array $attributes
|
|
|
|
|
* @return bool|static
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function createOrUpdate(array $condition, array $attributes = []): bool|static
|
|
|
|
|
{
|
|
|
|
|
$logger = Kiri::app()->getLogger();
|
|
|
|
|
if (empty($attributes)) {
|
|
|
|
|
return $logger->addError(FIND_OR_CREATE_MESSAGE, 'mysql');
|
|
|
|
|
}
|
|
|
|
|
/** @var static $select */
|
|
|
|
|
$select = static::query()->where($condition)->first();
|
|
|
|
|
if (empty($select)) {
|
|
|
|
|
$select = duplicate(static::class);
|
|
|
|
|
}
|
2021-09-28 18:57:30 +08:00
|
|
|
$select->_attributes = $attributes;
|
2021-09-28 18:54:07 +08:00
|
|
|
if (!$select->save()) {
|
|
|
|
|
return $logger->addError($select->getLastError(), 'mysql');
|
|
|
|
|
}
|
|
|
|
|
return $select;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $action
|
|
|
|
|
* @param $columns
|
|
|
|
|
* @param null|array $condition
|
|
|
|
|
* @return array|bool|int|string|null
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function mathematics($columns, $action, ?array $condition = null): int|bool|array|string|null
|
|
|
|
|
{
|
|
|
|
|
if (empty($condition)) {
|
|
|
|
|
$condition = [$this->getPrimary() => $this->getPrimaryValue()];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$activeQuery = static::query()->where($condition);
|
|
|
|
|
$create = SqlBuilder::builder($activeQuery)->mathematics($columns, $action);
|
|
|
|
|
if (is_bool($create)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return $this->getConnection()->createCommand($create[0], $create[1])->exec();
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
2021-09-28 18:54:07 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $fields
|
|
|
|
|
* @return ModelInterface|bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function update(array $fields): static|bool
|
|
|
|
|
{
|
|
|
|
|
return $this->save($fields);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $data
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function inserts(array $data): bool
|
|
|
|
|
{
|
|
|
|
|
$result = false;
|
|
|
|
|
if (empty($data)) {
|
|
|
|
|
error('Insert data empty.', 'mysql');
|
|
|
|
|
} else {
|
|
|
|
|
$result = static::query()->batchInsert($data);
|
|
|
|
|
}
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function delete(): bool
|
|
|
|
|
{
|
|
|
|
|
$conditions = $this->_oldAttributes;
|
|
|
|
|
if (empty($conditions)) {
|
|
|
|
|
return $this->addError("Delete condition do not empty.", 'mysql');
|
|
|
|
|
}
|
|
|
|
|
$primary = $this->getPrimary();
|
|
|
|
|
|
|
|
|
|
if (!empty($primary)) {
|
|
|
|
|
$conditions = [$primary => $this->getAttribute($primary)];
|
|
|
|
|
}
|
|
|
|
|
return static::deleteByCondition($conditions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param mixed $condition
|
|
|
|
|
* @param array $attributes
|
|
|
|
|
*
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws NotFindClassException
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public static function updateAll(mixed $condition, array $attributes = []): bool
|
|
|
|
|
{
|
|
|
|
|
$condition = static::query()->where($condition);
|
|
|
|
|
return $condition->batchUpdate($attributes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $condition
|
|
|
|
|
* @return array|Collection
|
|
|
|
|
* @throws NotFindClassException
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
*/
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $method
|
|
|
|
|
* @return mixed
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function resolveObject($method): mixed
|
|
|
|
|
{
|
|
|
|
|
$resolve = $this->{$this->getRelate($method)}();
|
|
|
|
|
if ($resolve instanceof HasBase) {
|
|
|
|
|
$resolve = $resolve->get();
|
|
|
|
|
}
|
|
|
|
|
if ($resolve instanceof ToArray) {
|
|
|
|
|
return $resolve->toArray();
|
|
|
|
|
} else if (is_object($resolve)) {
|
|
|
|
|
return get_object_vars($resolve);
|
|
|
|
|
} else {
|
|
|
|
|
return $resolve;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function toArray(): array
|
|
|
|
|
{
|
|
|
|
|
$data = $this->_attributes;
|
|
|
|
|
$lists = di(Getter::class)->getGetter(static::class);
|
|
|
|
|
foreach ($lists as $key => $item) {
|
|
|
|
|
$data[$key] = $this->{$item}($data[$key] ?? null);
|
|
|
|
|
}
|
|
|
|
|
return array_merge($data, $this->runRelate());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function runRelate(): array
|
|
|
|
|
{
|
|
|
|
|
$relates = [];
|
|
|
|
|
if (empty($with = $this->getWith())) {
|
|
|
|
|
return $relates;
|
|
|
|
|
}
|
|
|
|
|
foreach ($with as $val) {
|
|
|
|
|
$relates[$val] = $this->resolveObject($val);
|
|
|
|
|
}
|
|
|
|
|
return $relates;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $modelName
|
|
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
|
|
|
|
* @return HasOne|ActiveQuery
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function hasOne(string $modelName, $foreignKey, $localKey): HasOne|ActiveQuery
|
|
|
|
|
{
|
|
|
|
|
if (($value = $this->getAttribute($localKey)) === null) {
|
|
|
|
|
throw new Exception("Need join table primary key.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$relation = $this->getRelation();
|
|
|
|
|
|
|
|
|
|
return new HasOne($modelName, $foreignKey, $value, $relation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $modelName
|
|
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
|
|
|
|
* @return ActiveQuery|HasCount
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function hasCount($modelName, $foreignKey, $localKey): ActiveQuery|HasCount
|
|
|
|
|
{
|
|
|
|
|
if (($value = $this->getAttribute($localKey)) === null) {
|
|
|
|
|
throw new Exception("Need join table primary key.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$relation = $this->getRelation();
|
|
|
|
|
|
|
|
|
|
return new HasCount($modelName, $foreignKey, $value, $relation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $modelName
|
|
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
|
|
|
|
* @return ActiveQuery|HasMany
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function hasMany($modelName, $foreignKey, $localKey): ActiveQuery|HasMany
|
|
|
|
|
{
|
|
|
|
|
if (($value = $this->getAttribute($localKey)) === null) {
|
|
|
|
|
throw new Exception("Need join table primary key.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$relation = $this->getRelation();
|
|
|
|
|
|
|
|
|
|
return new HasMany($modelName, $foreignKey, $value, $relation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $modelName
|
|
|
|
|
* @param $foreignKey
|
|
|
|
|
* @param $localKey
|
|
|
|
|
* @return ActiveQuery|HasMany
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function hasIn($modelName, $foreignKey, $localKey): ActiveQuery|HasMany
|
|
|
|
|
{
|
|
|
|
|
if (($value = $this->getAttribute($localKey)) === null) {
|
|
|
|
|
throw new Exception("Need join table primary key.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$relation = $this->getRelation();
|
|
|
|
|
|
|
|
|
|
return new HasMany($modelName, $foreignKey, $value, $relation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function afterDelete(): bool
|
|
|
|
|
{
|
|
|
|
|
if (!$this->hasPrimary()) {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
$value = $this->getPrimaryValue();
|
|
|
|
|
if (empty($value)) {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function beforeDelete(): bool
|
|
|
|
|
{
|
|
|
|
|
if (!$this->hasPrimary()) {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
$value = $this->getPrimaryValue();
|
|
|
|
|
if (empty($value)) {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-08-11 15:03:28 +08:00
|
|
|
/**
|
|
|
|
|
* @return static
|
|
|
|
|
*/
|
|
|
|
|
public function refresh(): static
|
|
|
|
|
{
|
|
|
|
|
$this->_oldAttributes = $this->_attributes;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @param $value
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function __set($name, $value)
|
|
|
|
|
{
|
|
|
|
|
if (method_exists($this, 'set' . ucfirst($name))) {
|
|
|
|
|
$this->{'set' . ucfirst($name)}($value);
|
|
|
|
|
} else {
|
2021-08-17 16:22:18 +08:00
|
|
|
$this->_setter($name, $value);
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @return mixed
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function __get($name): mixed
|
|
|
|
|
{
|
|
|
|
|
$method = 'get' . ucfirst($name);
|
|
|
|
|
if (method_exists($this, $method)) {
|
|
|
|
|
return $this->{$method}();
|
|
|
|
|
}
|
2021-08-17 16:22:18 +08:00
|
|
|
$value = $this->_attributes[$name] ?? null;
|
2021-08-11 15:03:28 +08:00
|
|
|
|
2021-08-17 16:22:18 +08:00
|
|
|
return $this->_getter($name, $value);
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @param $value
|
|
|
|
|
* @return mixed
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function _decode($name, $value): mixed
|
|
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
return $this->getColumns()->_decode($name, $value);
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
private function with($name): mixed
|
|
|
|
|
{
|
|
|
|
|
$data = $this->{$this->_relate[$name]}();
|
|
|
|
|
if ($data instanceof HasBase) {
|
|
|
|
|
return $data->get();
|
|
|
|
|
}
|
|
|
|
|
return $data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $type
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
protected function getAnnotation(string $type = self::GET): array
|
|
|
|
|
{
|
|
|
|
|
return $this->_annotations[$type] ?? [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @param string $type
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected function hasAnnotation($name, string $type = self::GET): bool
|
|
|
|
|
{
|
|
|
|
|
if (!isset($this->_annotations[$type])) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return isset($this->_annotations[$type][$name]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $item
|
|
|
|
|
* @param $data
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
protected function resolveAttributes($item, $data): array
|
|
|
|
|
{
|
|
|
|
|
return call_user_func($item, $data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $name
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function __isset($name): bool
|
|
|
|
|
{
|
|
|
|
|
return isset($this->_attributes[$name]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $call
|
|
|
|
|
* @return mixed
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function resolveClass($call): mixed
|
|
|
|
|
{
|
|
|
|
|
if ($call instanceof HasOne) {
|
|
|
|
|
return $call->get();
|
|
|
|
|
} else if ($call instanceof HasMany) {
|
|
|
|
|
return $call->get();
|
|
|
|
|
} else {
|
|
|
|
|
return $call;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param mixed $offset
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function offsetExists(mixed $offset): bool
|
|
|
|
|
{
|
|
|
|
|
return isset($this->_attributes[$offset]) || isset($this->_oldAttributes[$offset]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param mixed $offset
|
|
|
|
|
* @return mixed
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function offsetGet(mixed $offset): mixed
|
|
|
|
|
{
|
|
|
|
|
return $this->__get($offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param mixed $offset
|
|
|
|
|
* @param mixed $value
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function offsetSet(mixed $offset, mixed $value)
|
|
|
|
|
{
|
|
|
|
|
$this->__set($offset, $value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param mixed $offset
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
public function offsetUnset(mixed $offset)
|
|
|
|
|
{
|
|
|
|
|
if (!isset($this->_attributes[$offset])
|
|
|
|
|
&& !isset($this->_oldAttributes[$offset])) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
unset($this->_attributes[$offset]);
|
|
|
|
|
unset($this->_oldAttributes[$offset]);
|
|
|
|
|
if (isset($this->_relate)) {
|
|
|
|
|
unset($this->_relate[$offset]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function unset(): array
|
|
|
|
|
{
|
|
|
|
|
$fields = func_get_args();
|
|
|
|
|
$fields = array_shift($fields);
|
|
|
|
|
if (!is_array($fields)) {
|
|
|
|
|
$fields = explode(',', $fields);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$array = array_combine($fields, $fields);
|
|
|
|
|
|
|
|
|
|
return array_diff_assoc($array, $this->_attributes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return Columns
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2021-09-28 18:54:07 +08:00
|
|
|
public function getColumns(): Columns
|
2021-08-11 15:03:28 +08:00
|
|
|
{
|
2021-09-28 18:54:07 +08:00
|
|
|
return $this->getConnection()->getSchema()->getColumns()
|
|
|
|
|
->table($this->getTable());
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array $data
|
|
|
|
|
* @return static
|
|
|
|
|
* @throws
|
|
|
|
|
*/
|
|
|
|
|
public static function populate(array $data): static
|
|
|
|
|
{
|
|
|
|
|
$model = duplicate(static::class);
|
|
|
|
|
$model->_attributes = $data;
|
|
|
|
|
$model->_oldAttributes = $data;
|
|
|
|
|
$model->setIsCreate(false);
|
|
|
|
|
return $model;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-17 16:22:18 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $method
|
|
|
|
|
* @param $value
|
|
|
|
|
* @return Closure
|
|
|
|
|
*/
|
|
|
|
|
protected function dispatcher($method, $value): Closure
|
|
|
|
|
{
|
|
|
|
|
return function () use ($method, $value) {
|
|
|
|
|
return $this->{$method}($value);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-28 18:54:07 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $name
|
|
|
|
|
* @param array $arguments
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
public static function __callStatic(string $name, array $arguments)
|
|
|
|
|
{
|
|
|
|
|
return (new static())->{$name}(...$arguments);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 15:03:28 +08:00
|
|
|
}
|