本文主要是介绍hyperf 二十九 修改器 二,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
教程:Hyperf
属性类型转换
Hyperf\Database\Model\Concerns\HasAttributes::casts被HasAttributes::setAttribute()、HasAttributes::getAttribute()调用,执行类型转换。
HasAttributes::casts为数组类型可设置基本类型或类的实例。默认设置主键id为int。
内置的基本强制转换类型:
- json:'array', 'json', 'object', 'collection'
- 日期:'date', 'datetime'
- 整数:'int'\integer
- 浮点数: real\float\double
- 数字:decimal
- 字符串:string
- 布尔:bool/boolean
- 自定义时间:custom_datetime
- 时间戳:timestamp
decimal 可设置小数位数,格式decimal:小数位数,通过number_format()实现。
一 自定义类型转换
通过继承接口(implements)Hyperf\Contract\CastsAttributes,定义好后使用其类名称将其附加到HasAttributes::casts。
1.1 值对象类型转换
将值转换成对象。通过get()获取时设置为对象、set()设置时将值设置到对象中。
执行save()之前需要设置相应的对象。官网例子中是先获取在设置,这样获取之后数据结构中自带类。
更改数据结构中类对象值后,因为缓存原因,其类的值不会通过HasAttributes::getAttributes()获得刷新,使用HasAttributes::syncAttributes()通过缓存和属性值合并可获取设置后的更新数据。
1.2 入站类型转换
设置set()实现入站类型转换。
1.3 类型参数转换
使用“:”设置参数。
protected $casts = ['secret' => Hash::class.':sha256',];
1.4 测试
获取
#App\Controlle\TestController
public function testmodifier() {$r = Article::query()->find(2);$article = $r->article;$istop = $r->is_top;var_dump($istop);$r->article->intro = "test1";$info1 = $r->getAttributes();$info2 = $r->syncAttributes()->getAttributes();$r->intro = "test2";$info3 = $r->getAttributes();var_dump($article, $info1, $info2, $info3);
}
#App1\Model\Article
protected $casts = ['id' => 'integer','created_at' => 'datetime','updated_at' => 'datetime','is_top' => 'boolean','article' => ArticleCasts::class,];
#App1\Attributes\Article
namespace App1\Attributes;class Article {public $title;public $content;public $intro;public function __construct($title, $intro, $content) {$this->title = $title;$this->content = $content;$this->intro = $intro;}
}
#App1\Casts\ArticleCasts
namespace App1\Casts;use App1\Attributes\Article;
use Hyperf\Contract\CastsAttributes;class ArticleCasts implements CastsAttributes {/*** 将取出的数据进行转换*/public function get($model, $key, $value, $attributes): Article {return new Article($attributes['title'],$attributes['intro'],$attributes['content']);}/*** 转换成将要进行存储的值*/public function set($model, $key, $value, $attributes) {return ['title' => $value->title,'intro' => $value->intro,'content' => $value->content,];}
}
测试结果
bool(false)
object(App1\Attributes\Article)#1213 (3) {["title"]=>string(5) "test2"["content"]=>NULL["intro"]=>string(5) "test1"
}
array(10) {["id"]=>int(2)["user_id"]=>int(1)["title"]=>string(5) "test2"["created_at"]=>string(19) "2024-01-13 10:06:04"["updated_at"]=>string(19) "2024-01-13 10:06:06"["deleted_at"]=>NULL["pv_num"]=>int(0)["intro"]=>NULL["content"]=>NULL["is_top"]=>int(0)
}
array(10) {["id"]=>int(2)["user_id"]=>int(1)["title"]=>string(5) "test2"["created_at"]=>string(19) "2024-01-13 10:06:04"["updated_at"]=>string(19) "2024-01-13 10:06:06"["deleted_at"]=>NULL["pv_num"]=>int(0)["intro"]=>string(5) "test1"["content"]=>NULL["is_top"]=>int(0)
}
array(10) {["id"]=>int(2)["user_id"]=>int(1)["title"]=>string(5) "test2"["created_at"]=>string(19) "2024-01-13 10:06:04"["updated_at"]=>string(19) "2024-01-13 10:06:06"["deleted_at"]=>NULL["pv_num"]=>int(0)["intro"]=>string(5) "test2"["content"]=>NULL["is_top"]=>int(0)
}
写入
#App\Controlle\TestController
public function testmodifier() {$r = Article::query()->find(2);$r->is_top = 1;$r->article->intro = "test2";$attr = $r->getAttributes();var_dump($attr);$r->save();$attr = $r->getAttributes();var_dump($attr);
}
测试结果
array(10) {["id"]=>int(2)["user_id"]=>int(1)["title"]=>string(5) "test2"["created_at"]=>string(19) "2024-01-13 10:06:04"["updated_at"]=>string(19) "2024-03-25 09:47:00"["deleted_at"]=>NULL["pv_num"]=>int(0)["intro"]=>string(5) "test1"["content"]=>NULL["is_top"]=>int(1)
}
array(10) {["id"]=>int(2)["user_id"]=>int(1)["title"]=>string(5) "test2"["created_at"]=>string(19) "2024-01-13 10:06:04"["updated_at"]=>string(16) "2024-03-25 09:48"["deleted_at"]=>NULL["pv_num"]=>int(0)["intro"]=>string(5) "test2"["content"]=>NULL["is_top"]=>int(1)
}
1.5 源码
转换类型
#Hyperf\Database\Model\Concerns\HasAttributes
public function getAttribute($key){if (!$key) {return;}// If the attribute exists in the attribute array or has a "get" mutator we will// get the attribute's value. Otherwise, we will proceed as if the developers// are asking for a relationship's value. This covers both types of values.if (array_key_exists($key, $this->getAttributes())|| $this->hasGetMutator($key)|| $this->isClassCastable($key)) {return $this->getAttributeValue($key);}// Here we will determine if the model base class itself contains this given key// since we don't want to treat any of those methods as relationships because// they are all intended as helper methods and none of these are relations.if (method_exists(self::class, $key)) {return;}return $this->getRelationValue($key);}
protected function castAttribute($key, $value){$castType = $this->getCastType($key);if (is_null($value) && in_array($castType, static::$primitiveCastTypes)) {return $value;}switch ($castType) {case 'int':case 'integer':return (int) $value;case 'real':case 'float':case 'double':return $this->fromFloat($value);case 'decimal':return $this->asDecimal($value, explode(':', $this->getCasts()[$key], 2)[1]);case 'string':return (string) $value;case 'bool':case 'boolean':return (bool) $value;case 'object':return $this->fromJson($value, true);case 'array':case 'json':return $this->fromJson($value);case 'collection':return new BaseCollection($this->fromJson($value));case 'date':return $this->asDate($value);case 'datetime':case 'custom_datetime':return $this->asDateTime($value);case 'timestamp':return $this->asTimestamp($value);}if ($this->isClassCastable($key)) {return $this->getClassCastableAttributeValue($key, $value);}return $value;}
protected function asDecimal($value, $decimals){return number_format((float) $value, (int) $decimals, '.', '');}
protected function isDateCastable($key){return $this->hasCast($key, ['date', 'datetime']);}
protected function isJsonCastable($key){return $this->hasCast($key, ['array', 'json', 'object', 'collection']);}
public function getAttributeValue($key){return $this->transformModelValue($key, $this->getAttributeFromArray($key));}
protected function transformModelValue($key, $value){// If the attribute has a get mutator, we will call that then return what// it returns as the value, which is useful for transforming values on// retrieval from the model to a form that is more useful for usage.if ($this->hasGetMutator($key)) {return $this->mutateAttribute($key, $value);}// If the attribute exists within the cast array, we will convert it to// an appropriate native PHP type dependent upon the associated value// given with the key in the pair. Dayle made this comment line up.if ($this->hasCast($key)) {return $this->castAttribute($key, $value);}// If the attribute is listed as a date, we will convert it to a DateTime// instance on retrieval, which makes it quite convenient to work with// date fields without having to create a mutator for each property.if ($value !== null&& \in_array($key, $this->getDates(), false)) {return $this->asDateTime($value);}return $value;}
数据更新
#Hyperf\Database\Model\Concerns\HasAttributes
public function syncAttributes(){$this->mergeAttributesFromClassCasts();return $this;}
protected function mergeAttributesFromClassCasts(){foreach ($this->classCastCache as $key => $value) {if ($value instanceof Synchronized && $value->isSynchronized()) {continue;}$caster = $this->resolveCasterClass($key);$this->attributes = array_merge($this->attributes,$caster instanceof CastsInboundAttributes? [$key => $value]: $this->normalizeCastClassResponse($key, $caster->set($this, $key, $value, $this->attributes)));}}
二 数组和json转换
数据库存json,数据库字段为text或json,数据获取时转变为数组。
获取时使用 Hyperf\Database\Model\Model::__get()调用Hyperf\Database\Model\Concerns\HasAttributes::getAttribute()。
设置时使用Model::__set()调用HasAttributes::setAttribute()。
判断是否可为json数据,则使用json_encode()转化为json数据保存。
根据源码,HasAttributes::casts数组中设置字段类型为array, json, object, collection中的类型,可为json字符串保存。
2.1 测试
#App\Controller\TestController
public function testmodifier() { $article = Article::query()->find(2);$options = $article->content;var_dump($options);if (empty($options)) {$article->content = ['intro' => 'test', 'content' => 'test1'];$article->save();$options = $article->content;}var_dump($options);
}
测试结果
NULL
array(2) {["intro"]=>string(4) "test"["content"]=>string(5) "test1"
}
select content from articles where id=2{"intro":"test","content":"test1"}
2.2 源码
getAttribute()源码详见1.5源码。
#Hyperf\Database\Model\Concerns\HasAttributes
public function setAttribute($key, $value) {// First we will check for the presence of a mutator for the set operation// which simply lets the developers tweak the attribute as it is set on// the model, such as "json_encoding" an listing of data for storage.if ($this->hasSetMutator($key)) {return $this->setMutatedAttributeValue($key, $value);}// If an attribute is listed as a "date", we'll convert it from a DateTime// instance into a form proper for storage on the database tables using// the connection grammar's date format. We will auto set the values.if ($value && $this->isDateAttribute($key)) {$value = $this->fromDateTime($value);}if ($this->isClassCastable($key)) {$this->setClassCastableAttribute($key, $value);return $this;}if ($this->isJsonCastable($key) && !is_null($value)) {$value = $this->castAttributeAsJson($key, $value);}// If this attribute contains a JSON ->, we'll set the proper value in the// attribute's underlying array. This takes care of properly nesting an// attribute in the array's value in the case of deeply nested items.if (Str::contains($key, '->')) {return $this->fillJsonAttribute($key, $value);}$this->attributes[$key] = $value;return $this;}
protected function isJsonCastable($key) {return $this->hasCast($key, ['array', 'json', 'object', 'collection']);}
public function hasCast($key, $types = null) {if (array_key_exists($key, $this->getCasts())) {return $types ? in_array($this->getCastType($key), (array) $types, true) : true;}return false;}
public function getCasts() {if ($this->getIncrementing()) {return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);}return $this->casts;}
protected function castAttributeAsJson($key, $value) {$value = $this->asJson($value);if ($value === false) {throw JsonEncodingException::forAttribute($this,$key,json_last_error_msg());}return $value;}
三 Date类型转换
在设置的时候,若不是Hyperf\Database\Model\Model::CREATED_AT,Model::UPDATED_AT,即字段不为created_at或updated_at,且设置参数如日期格式,则不会调用转换。
因为判断是否可转换的时候被过滤掉。像官网设置created_at不会被转换,可能和版本有关系。
再说获取。
直接获取对应属性,即运行Hyperf\Database\Model\Model::__get()。 转换类型会被判断为custom_datetime,转换为Carbon类。
但是设置的格式未被解析,还是需要手动设置格式。使用Hyperf\Database\Model\Concerns\HasAttributes::getAttributes()获取,直接获取HasAttributes::attributes属性。
这两种对与设置的格式都不会解析,返回都是根据HasAttributes::dateFormat,其默认值都是Y-m-d H:i:s。
使用HasAttributes::attributesToArray(),会调用HasAttributes::addCastAttributesToArray(),会判断是否为custom_datetime。
是custom_datetime类型,则会调用php系统类自带函数format(),通过解析参数,获得格式化数据。
其实和自己使用format(),运行原理一样,效果也一样。
3.1 测试
#App\Controller\TestController
public function testmodifier() {$pr = PushRecode::query()->find(1);$pr->push_time = date('Y-m-d H:i:s');var_dump($pr->push_time);$pr = $pr->syncAttributes();var_dump($pr->push_time->format('Y-m-d H:00:00'));$pr->created_at = date('Y-m-d H:i:s');$info = $pr->syncAttributes()->getAttributes();var_dump($info);$info = $pr->syncAttributes()->attributesToArray();var_dump($info);
}
测试结果
object(Carbon\Carbon)#1151 (19) {["endOfTime":protected]=>bool(false)["startOfTime":protected]=>bool(false)["constructedObjectId":protected]=>string(32) "00000000607fdc84000000003fa11939"["localMonthsOverflow":protected]=>NULL["localYearsOverflow":protected]=>NULL["localStrictModeEnabled":protected]=>NULL["localHumanDiffOptions":protected]=>NULL["localToStringFormat":protected]=>NULL["localSerializer":protected]=>NULL["localMacros":protected]=>NULL["localGenericMacros":protected]=>NULL["localFormatFunction":protected]=>NULL["localTranslator":protected]=>NULL["dumpProperties":protected]=>array(3) {[0]=>string(4) "date"[1]=>string(13) "timezone_type"[2]=>string(8) "timezone"}["dumpLocale":protected]=>NULL["dumpDateProperties":protected]=>NULL["date"]=>string(26) "2024-04-11 09:30:03.000000"["timezone_type"]=>int(3)["timezone"]=>string(3) "UTC"
}
string(19) "2024-04-11 09:00:00"
array(4) {["id"]=>int(1)["is_push"]=>int(1)["push_time"]=>string(19) "2024-04-11 09:30:03"["created_at"]=>string(19) "2024-04-11 09:30:03"
}
array(4) {["id"]=>int(1)["is_push"]=>int(1)["push_time"]=>string(10) "2024-04-11"["created_at"]=>string(10) "2024-04-11"
}
3.2 源码
Hyperf\Database\Model\Model::getAttribute()内容详见1.5源码。
Hyperf\Database\Model\Model::setAttribute()内容详见2.2源码。
#Hyperf\Database\Model\Concerns\HasAttributes
protected function isClassCastable($key) {return array_key_exists($key, $this->getCasts())&& class_exists($class = $this->parseCasterClass($this->getCasts()[$key]))&& !in_array($class, static::$primitiveCastTypes);}
protected function parseCasterClass($class) {return strpos($class, ':') === false? $class: explode(':', $class, 2)[0];}
protected static $primitiveCastTypes = ['array','bool','boolean','collection','custom_datetime','date','datetime','decimal','double','float','int','integer','json','object','real','string','timestamp',];
public function getCasts() {if ($this->getIncrementing()) {return array_merge([$this->getKeyName() => $this->getKeyType()], $this->casts);}return $this->casts;}public function getAttributes() {return $this->attributes;}protected function isDateAttribute($key) {return in_array($key, $this->getDates(), true)|| $this->isDateCastable($key);}
protected function isDateCastable($key) {return $this->hasCast($key, ['date', 'datetime']);}public function getAttributeValue($key) {return $this->transformModelValue($key, $this->getAttributeFromArray($key));}
protected function transformModelValue($key, $value) {// If the attribute has a get mutator, we will call that then return what// it returns as the value, which is useful for transforming values on// retrieval from the model to a form that is more useful for usage.if ($this->hasGetMutator($key)) {return $this->mutateAttribute($key, $value);}// If the attribute exists within the cast array, we will convert it to// an appropriate native PHP type dependent upon the associated value// given with the key in the pair. Dayle made this comment line up.if ($this->hasCast($key)) {return $this->castAttribute($key, $value);}// If the attribute is listed as a date, we will convert it to a DateTime// instance on retrieval, which makes it quite convenient to work with// date fields without having to create a mutator for each property.if ($value !== null&& \in_array($key, $this->getDates(), false)) {return $this->asDateTime($value);}return $value;}
protected function castAttribute($key, $value) {$castType = $this->getCastType($key);if (is_null($value) && in_array($castType, static::$primitiveCastTypes)) {return $value;}switch ($castType) {case 'int':case 'integer':return (int) $value;case 'real':case 'float':case 'double':return $this->fromFloat($value);case 'decimal':return $this->asDecimal($value, explode(':', $this->getCasts()[$key], 2)[1]);case 'string':return (string) $value;case 'bool':case 'boolean':return (bool) $value;case 'object':return $this->fromJson($value, true);case 'array':case 'json':return $this->fromJson($value);case 'collection':return new BaseCollection($this->fromJson($value));case 'date':return $this->asDate($value);case 'datetime':case 'custom_datetime':return $this->asDateTime($value);case 'timestamp':return $this->asTimestamp($value);}if ($this->isClassCastable($key)) {return $this->getClassCastableAttributeValue($key, $value);}return $value;}
protected function getCastType($key) {if ($this->isCustomDateTimeCast($this->getCasts()[$key])) {return 'custom_datetime';}if ($this->isDecimalCast($this->getCasts()[$key])) {return 'decimal';}return trim(strtolower($this->getCasts()[$key]));}
protected function asDateTime($value) {// If this value is already a Carbon instance, we shall just return it as is.// This prevents us having to re-instantiate a Carbon instance when we know// it already is one, which wouldn't be fulfilled by the DateTime check.if ($value instanceof Carbon || $value instanceof CarbonInterface) {return Carbon::instance($value);}// If the value is already a DateTime instance, we will just skip the rest of// these checks since they will be a waste of time, and hinder performance// when checking the field. We will just return the DateTime right away.if ($value instanceof DateTimeInterface) {return Carbon::parse($value->format('Y-m-d H:i:s.u'),$value->getTimezone());}// If this value is an integer, we will assume it is a UNIX timestamp's value// and format a Carbon object from this timestamp. This allows flexibility// when defining your date fields as they might be UNIX timestamps here.if (is_numeric($value)) {return Carbon::createFromTimestamp($value);}// If the value is in simply year, month, day format, we will instantiate the// Carbon instances from that format. Again, this provides for simple date// fields on the database, while still supporting Carbonized conversion.if ($this->isStandardDateFormat($value)) {return Carbon::instance(Carbon::createFromFormat('Y-m-d', $value)->startOfDay());}$format = $this->getDateFormat();// Finally, we will just assume this date is in the format used by default on// the database connection and use that format to create the Carbon object// that is returned back out to the developers after we convert it here.if (Carbon::hasFormat($value, $format)) {return Carbon::createFromFormat($format, $value);}return Carbon::parse($value);}
protected function isCustomDateTimeCast($cast) {return strncmp($cast, 'date:', 5) === 0|| strncmp($cast, 'datetime:', 9) === 0;}
根据源码,判断array_key_exists($key, $this->getCasts())、class_exists($class = $this->parseCasterClass($this->getCasts()[$key]))应为true,!in_array($class, static::$primitiveCastTypes)为false,所以isClassCastable()为false。
isDateAttribute()结果应该也为false。
之后执行getAttributeValue(),获取castType为custom_datetime,最终转换为Carbon\Carbon类。
#Hyperf\Database\Model\Concerns\HasAttributes
public function attributesToArray() {// If an attribute is a date, we will cast it to a string after converting it// to a DateTime / Carbon instance. This is so we will get some consistent// formatting while accessing attributes vs. arraying / JSONing a model.$attributes = $this->addDateAttributesToArray($attributes = $this->getArrayableAttributes());$attributes = $this->addMutatedAttributesToArray($attributes,$mutatedAttributes = $this->getMutatedAttributes());// Next we will handle any casts that have been setup for this model and cast// the values to their appropriate type. If the attribute has a mutator we// will not perform the cast on those attributes to avoid any confusion.$attributes = $this->addCastAttributesToArray($attributes,$mutatedAttributes);// Here we will grab all of the appended, calculated attributes to this model// as these attributes are not really in the attributes array, but are run// when we need to array or JSON the model for convenience to the coder.foreach ($this->getArrayableAppends() as $key) {$attributes[$key] = $this->mutateAttributeForArray($key, null);}return $attributes;}
protected function addCastAttributesToArray(array $attributes, array $mutatedAttributes) {foreach ($this->getCasts() as $key => $value) {if (!array_key_exists($key, $attributes) || in_array($key, $mutatedAttributes)) {continue;}// Here we will cast the attribute. Then, if the cast is a date or datetime cast// then we will serialize the date for the array. This will convert the dates// to strings based on the date format specified for these Model models.$attributes[$key] = $this->castAttribute($key,$attributes[$key]);// If the attribute cast was a date or a datetime, we will serialize the date as// a string. This allows the developers to customize how dates are serialized// into an array without affecting how they are persisted into the storage.if ($attributes[$key]&& ($value === 'date' || $value === 'datetime')) {$attributes[$key] = $this->serializeDate($attributes[$key]);}if ($attributes[$key] && $this->isCustomDateTimeCast($value)) {$attributes[$key] = $attributes[$key]->format(explode(':', $value, 2)[1]);}if ($attributes[$key] instanceof Arrayable) {$attributes[$key] = $attributes[$key]->toArray();}}return $attributes;}
官网打不开了……后续内容等官网好了再说吧……
这篇关于hyperf 二十九 修改器 二的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!