Added interface Synchronized to optimized mergeAttributesFromClassCasts.

This commit is contained in:
李铭昕 2020-05-18 11:40:37 +08:00
parent f42c90b3a6
commit 021ae675c4
4 changed files with 138 additions and 0 deletions

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Contract;
interface Synchronized
{
/**
* Whether the data has been synchronized.
*/
public function isSynchronized(): bool;
}

View File

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://doc.hyperf.io
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\Database\Model;
use Hyperf\Contract\Synchronized;
use Hyperf\Utils\Contracts\Arrayable;
abstract class CastsValue implements Synchronized, Arrayable
{
/**
* @var Model
*/
protected $model;
/**
* @var array
*/
protected $items;
/**
* @var bool
*/
protected $isSynchronized;
public function __construct(Model $model, $itmes = [])
{
$this->model = $model;
$this->items = $itmes;
}
public function __get($name)
{
return $this->items[$name];
}
public function __set($name, $value)
{
$this->items[$name] = $value;
$this->isSynchronized = false;
$this->model->syncAttributes();
$this->isSynchronized = true;
}
public function isSynchronized(): bool
{
return $this->isSynchronized;
}
public function toArray(): array
{
return $this->items;
}
}

View File

@ -17,6 +17,7 @@ use DateTimeInterface;
use Hyperf\Contract\Castable;
use Hyperf\Contract\CastsAttributes;
use Hyperf\Contract\CastsInboundAttributes;
use Hyperf\Contract\Synchronized;
use Hyperf\Database\Model\JsonEncodingException;
use Hyperf\Database\Model\Relations\Relation;
use Hyperf\Utils\Arr;
@ -1404,6 +1405,10 @@ trait HasAttributes
protected function mergeAttributesFromClassCasts()
{
foreach ($this->classCastCache as $key => $value) {
if ($value instanceof Synchronized && $value->isSynchronized()) {
continue;
}
$caster = $this->resolveCasterClass($key);
$this->attributes = array_merge(

View File

@ -14,7 +14,9 @@ namespace HyperfTest\Database;
use Hyperf\Contract\Castable;
use Hyperf\Contract\CastsAttributes;
use Hyperf\Contract\CastsInboundAttributes;
use Hyperf\Database\Model\CastsValue;
use Hyperf\Database\Model\Model;
use Hyperf\Utils\Arr;
use PHPUnit\Framework\TestCase;
/**
@ -205,6 +207,24 @@ class DatabaseModelCustomCastingTest extends TestCase
CastUsing::$castsAttributes = new UppercaseCaster();
$this->assertSame($method->invokeArgs($model, ['cast_using']), $method->invokeArgs($model, ['cast_using']));
}
public function testIsSynchronized()
{
$model = new TestModelWithCustomCast();
$model->user = $user = new UserInfo($model, ['name' => 'Hyperf', 'gender' => 1]);
$model->syncOriginal();
$attributes = $model->getAttributes();
$this->assertSame(['name' => 'Hyperf', 'gender' => 1], $attributes);
$user->name = 'Nano';
$attributes = $model->getAttributes();
$this->assertSame(['name' => 'Nano', 'gender' => 1], $attributes);
$this->assertSame(['name' => 'Nano'], $model->getDirty());
$this->assertSame(2, UserInfoCaster::$setCount);
$this->assertSame(0, UserInfoCaster::$getCount);
}
}
class TestModelWithCustomCast extends Model
@ -223,6 +243,7 @@ class TestModelWithCustomCast extends Model
*/
protected $casts = [
'address' => AddressCaster::class,
'user' => UserInfoCaster::class,
'password' => HashCaster::class,
'other_password' => HashCaster::class . ':md5',
'uppercase' => UppercaseCaster::class,
@ -286,6 +307,28 @@ class AddressCaster implements CastsAttributes
}
}
class UserInfoCaster implements CastsAttributes
{
public static $setCount = 0;
public static $getCount = 0;
public function get($model, string $key, $value, array $attributes)
{
++self::$getCount;
return new UserInfo($model, Arr::only($attributes, ['name', 'gender']));
}
public function set($model, string $key, $value, array $attributes)
{
++self::$setCount;
return [
'name' => $value->name,
'gender' => $value->gender,
];
}
}
class JsonCaster implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
@ -369,3 +412,11 @@ class Address
$this->lineTwo = $lineTwo;
}
}
/**
* @property string $name
* @property int $gender
*/
class UserInfo extends CastsValue
{
}