大学生网站开发接单,全球最大的网站建设外包网,给个网站做填空题,新网站网页收录教程#xff1a;Hyperf
一 修改器和访问器
根据教程#xff0c;可设置相关函数,如set属性名Attribute()、get属性名Attribute()#xff0c;设置和获取属性。这在thinkphp中也常见。
修改器#xff1a;set属性名Attribute()#xff1b;访问器#xff1a;get属性名Attri…教程Hyperf
一 修改器和访问器
根据教程可设置相关函数,如set属性名Attribute()、get属性名Attribute()设置和获取属性。这在thinkphp中也常见。
修改器set属性名Attribute()访问器get属性名Attribute()。
1.1 原理
模型的父类Hyperf\Database\Model\Model定义__set()、_get()、__isset()、__unset()函数。
设置属性调用__set()获取属性调用_get()。
__set()调用set属性名Attribute()和格式化数据。先通过set属性名Attribute()获取值再判断是否为日期格式化日期数据。若设置字段类型会根据设定的字段类型匹配对应的类返回对应类。会判断是否为json数据返回json格式字符换。若调用的对应字符串含有“-”则将该对应类对象格式化为json字符串返回。
1.2 测试
#App\Controller\Test
public function testmodifier() {$result Article::query()-find(2)-toArray();var_dump($result);$article Article::firstOrCreate([title test4],[user_id 2]);$result Article::query()-where([title test4])-first()-toArray();var_dump($result);}
#App1\Model\Article
class Article extends Model implements CacheableInterface {use Cacheable;use SoftDeletes;/*** The table associated with the model.** var string*/protected $table articles;/*** The attributes that are mass assignable.** var array*/protected $fillable [title, user_id]; //允许批量赋值/*** The attributes that should be cast to native types.** var array*/protected $casts [id integer, created_at datetime, updated_at datetime];public function setTitleAttribute($value) {$this-attributes[title] . $value;}public function getTitleAttribute($value) {return 标题 . $value;}
} 测试结果
array(7) {[id]int(2)[user_id]int(1)[title]string(14) 标题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)
}array(7) {[id]int(10)[user_id]int(2)[title]string(15) 标题test4[created_at]string(19) 2024-03-19 08:07:24[updated_at]string(19) 2024-03-19 08:07:24[deleted_at]NULL[pv_num]int(0)
} 数据保存使用Hyperf\Database\Model\Builder::firstOrCreate()。firstOrNew()仅在对象中增加数据未保存进数据库这是和firstOrCreate()的区别。
过程中创建Hyperf\Database\Model\Model类对象是__construct()会调用Model::fill()。Model::fill()使用Model::isFillable()调用Model::fillable属性结果为true才能设置属性否则报错。
因为在Article::setTitleAttribute()对传入的属性增加数据。根据测试代码查询的使用也应该加上“”。
也是因为使用Builder::firstOrCreate()和Article::setTitleAttribute()修改传入属性设置查询数据时不会查询到相应数据因为查询值有差异。
tp中也遇到过相似情况。解决方法对查询条件中数据也进行数据的换装保证修改方式和保存之前的数据方式一样。
1.3 源码
#App1\Model\Article use Hyperf\DbConnection\Model\Model;class Article extends Model implements CacheableInterface {use Cacheable;use SoftDeletes;
}#Hyperf\DbConnection\Model\Modeluse Hyperf\Database\Model\Model as BaseModel;class Model extends BaseModel
{use HasContainer;use HasRepository;
}
#Hyperf\Database\Model\Modelabstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializable,
CompressInterface {use Concerns\HasAttributes;use Concerns\HasEvents;use Concerns\HasGlobalScopes;use Concerns\HasRelationships;use Concerns\HasTimestamps;use Concerns\HidesAttributes;use Concerns\GuardsAttributes;/*** Dynamically retrieve attributes on the model.** param string $key*/public function __get($key) {return $this-getAttribute($key);}/*** Dynamically set attributes on the model.** param string $key* param mixed $value*/public function __set($key, $value) {$this-setAttribute($key, $value);}/*** Determine if an attribute or relation exists on the model.** param string $key* return bool*/public function __isset($key) {return $this-offsetExists($key);}/*** Unset an attribute on the model.** param string $key*/public function __unset($key) {$this-offsetUnset($key);}}
# Hyperf\Database\Model\Concerns\HasAttributes/*** Set a given attribute on the model.** param string $key* param mixed $value*/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, well convert it from a DateTime// instance into a form proper for storage on the database tables using// the connection grammars 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 -, well set the proper value in the// attributes underlying array. This takes care of properly nesting an// attribute in the arrays value in the case of deeply nested items.if (Str::contains($key, -)) {return $this-fillJsonAttribute($key, $value);}$this-attributes[$key] $value;return $this;}/*** Set the value of an attribute using its mutator.** param string $key* param mixed $value*/protected function setMutatedAttributeValue($key, $value){return $this-{set . Str::studly($key) . Attribute}($value);}/*** Convert a DateTime to a storable string.** param mixed $value* return null|string*/public function fromDateTime($value){return empty($value) ? $value : $this-asDateTime($value)-format($this-getDateFormat());}/*** Get the format for database stored dates.** return string*/public function getDateFormat(){return $this-dateFormat ?: $this-getConnection()-getQueryGrammar()-getDateFormat();}
/*** Set the value of a class castable attribute.** param string $key* param mixed $value*/protected function setClassCastableAttribute($key, $value){$caster $this-resolveCasterClass($key);if (is_null($value)) {$this-attributes array_merge($this-attributes, array_map(function () {},$this-normalizeCastClassResponse($key, $caster-set($this,$key,$this-{$key},$this-attributes))));} else {$this-attributes array_merge($this-attributes,$this-normalizeCastClassResponse($key, $caster-set($this,$key,$value,$this-attributes)));}if ($caster instanceof CastsInboundAttributes || !is_object($value)) {unset($this-classCastCache[$key]);} else {$this-classCastCache[$key] $value;}}/*** Cast the given attribute to JSON.** param string $key* param mixed $value* return string*/protected function castAttributeAsJson($key, $value){$value $this-asJson($value);if ($value false) {throw JsonEncodingException::forAttribute($this,$key,json_last_error_msg());}return $value;}/*** Set a given JSON attribute on the model.** param string $key* param mixed $value* return $this*/public function fillJsonAttribute($key, $value){[$key, $path] explode(-, $key, 2);$this-attributes[$key] $this-asJson($this-getArrayAttributeWithValue($path,$key,$value));return $this;}
二 日期转化及时间格式化
模型会将 created_at 和 updated_at 字段转换为 Carbon\Carbon 实例它继承了 PHP 原生的 DateTime 类并提供了各种有用的方法。可以通过设置模型的 $dates 属性来添加其他日期属性。
2.1 原理
调用Model::_get()、Model::_set()时会判断字段类型为日期则转换为Carbon\Carbon类对象。可以设置日期格式。
$date为日期类型字段$dateFormat为日期格式字符串都在Hyperf\Database\Model\Concerns\HasAttributes中设置也是由其转换数据类型。
HasAttributes::castAttribute()处理各种字段类型HasAttributes::asDate()执行日期类型转换HasAttributes::getDateFormat()获取日期格式。
日期类型默认包括created_at 、updated_at。日期默认格式Y-m-d H:i:s。
2.2 测试 #App1\Model\Article protected $dateFormat Y-m-d H:i;public function setTitleAttribute($value) {$this-attributes[title] $value;}public function getTitleAttribute($value) {return $value;}
#App\Controller\TestController
public function testmodifier() {$article Article::firstOrCreate([title test4],[user_id 2]);var_dump($article-toArray());} 测试结果
array(7) {[id]int(11)[user_id]int(2)[title]string(5) test4[created_at]string(16) 2024-03-22 09:04[updated_at]string(16) 2024-03-22 09:04[deleted_at]NULL[pv_num]int(0)
} 测试可见 数据库中时间格式还是h:i:s仅获取的时候是h:i格式。
Model::CREATED_AT、Model::UPDATED_AT使用Carbon::now()获取时间并没有使用$dateFormat属性。
2.3 源码
#Hyperf\Database\Model\Model
public function __get($key) {return $this-getAttribute($key);}
public function __set($key, $value) {$this-setAttribute($key, $value);}/*** 新增时使用** param \Hyperf\Database\Model\Builder $query* return bool*/protected function performInsert(Builder $query) {if ($event $this-fireModelEvent(creating)) {if ($event instanceof StoppableEventInterface $event-isPropagationStopped()) {return false;}}// First well need to create a fresh query instance and touch the creation and// update timestamps on this model, which are maintained by us for developer// convenience. After, we will just continue saving these model instances.if ($this-usesTimestamps()) {$this-updateTimestamps();}// If the model has an incrementing key, we can use the insertGetId method on// the query builder, which will give us back the final inserted ID for this// table from the database. Not all tables have to be incrementing though.$attributes $this-getAttributes();if ($this-getIncrementing()) {$this-insertAndSetId($query, $attributes);}// If the table isnt incrementing well simply insert these attributes as they// are. These attribute arrays must contain an id column previously placed// there by the developer as the manually determined key for these models.else {if (empty($attributes)) {return true;}$query-insert($attributes);}// We will go ahead and set the exists property to true, so that it is set when// the created event is fired, just in case the developer tries to update it// during the event. This will allow them to do so and run an update here.$this-exists true;$this-wasRecentlyCreated true;$this-fireModelEvent(created);return true;}
/*** 修改时使用** param \Hyperf\Database\Model\Builder $query* return bool*/protected function performUpdate(Builder $query) {// If the updating event returns false, we will cancel the update operation so// developers can hook Validation systems into their models and cancel this// operation if the model does not pass validation. Otherwise, we update.if ($event $this-fireModelEvent(updating)) {if ($event instanceof StoppableEventInterface $event-isPropagationStopped()) {return false;}}// First we need to create a fresh query instance and touch the creation and// update timestamp on the model which are maintained by us for developer// convenience. Then we will just continue saving the model instances.if ($this-usesTimestamps()) {$this-updateTimestamps();}// Once we have run the update operation, we will fire the updated event for// this model instance. This will allow developers to hook into these after// models are updated, giving them a chance to do any special processing.$dirty $this-getDirty();if (count($dirty) 0) {$this-setKeysForSaveQuery($query)-update($dirty);$this-syncChanges();$this-fireModelEvent(updated);}return true;}public function save(array $options []): bool {$this-mergeAttributesFromClassCasts();$query $this-newModelQuery();// If the saving event returns false well bail out of the save and return// false, indicating that the save failed. This provides a chance for any// listeners to cancel save operations if validations fail or whatever.if ($saving $this-fireModelEvent(saving)) {if ($saving instanceof StoppableEventInterface $saving-isPropagationStopped()) {return false;}}// If the model already exists in the database we can just update our record// that is already in this database using the current IDs in this where// clause to only update this model. Otherwise, well just insert them.if ($this-exists) {$saved $this-isDirty() ? $this-performUpdate($query) : true;} else {// If the model is brand new, well insert it into our database and set the// ID attribute on the model to the value of the newly inserted rows ID// which is typically an auto-increment value managed by the database.$saved $this-performInsert($query);if (!$this-getConnectionName() $connection $query-getConnection()) {$this-setConnection($connection-getName());}}// If the model is successfully saved, we need to do a few more things once// that is done. We will call the saved method here to run any actions// we need to happen after a model gets successfully saved right here.if ($saved) {$this-finishSave($options);}return $saved;}
#Hyperf\Database\Model\Concerns\HasAttributes/*** Set a given attribute on the model.** param string $key* param mixed $value*/
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, well convert it from a DateTime// instance into a form proper for storage on the database tables using// the connection grammars 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 -, well set the proper value in the// attributes underlying array. This takes care of properly nesting an// attribute in the arrays value in the case of deeply nested items.if (Str::contains($key, -)) {return $this-fillJsonAttribute($key, $value);}$this-attributes[$key] $value;return $this;}public function fromDateTime($value){return empty($value) ? $value : $this-asDateTime($value)-format($this-getDateFormat());}
/*** Get an attribute from the model.** param string $key*/public function getAttribute($key){if (!$key) {return;}// If the attribute exists in the attribute array or has a get mutator we will// get the attributes value. Otherwise, we will proceed as if the developers// are asking for a relationships 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 dont 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);}
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 asDate($value){return $this-asDateTime($value)-startOfDay();}
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 wouldnt 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 timestamps 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);}
public function getDateFormat(){return $this-dateFormat ?: $this-getConnection()-getQueryGrammar()-getDateFormat();}
#Hyperf\Database\Grammar
public function getDateFormat(){return Y-m-d H:i:s;}
#Hyperf\Database\Model\Concerns\HasTimestamps
protected function updateTimestamps(){$time $this-freshTimestamp();if (! is_null(static::UPDATED_AT) ! $this-isDirty(static::UPDATED_AT)) {$this-setUpdatedAt($time);}if (! $this-exists ! is_null(static::CREATED_AT) ! $this-isDirty(static::CREATED_AT)) {$this-setCreatedAt($time);}}
public function setCreatedAt($value){$this-{static::CREATED_AT} $value;return $this;}public function setUpdatedAt($value){$this-{static::UPDATED_AT} $value;return $this;}
public function freshTimestamp(){return Carbon::now();} #Carbon\Traits\Creator
public function __construct($time null, $tz null){if ($time instanceof DateTimeInterface) {$time $this-constructTimezoneFromDateTime($time, $tz)-format(Y-m-d H:i:s.u);}if (is_numeric($time) (!\is_string($time) || !preg_match(/^\d{1,14}$/, $time))) {$time static::createFromTimestampUTC($time)-format(Y-m-d\TH:i:s.uP);}// If the class has a test now set and we are trying to create a now()// instance then override as required$isNow empty($time) || $time now;if (method_exists(static::class, hasTestNow) method_exists(static::class, getTestNow) static::hasTestNow() ($isNow || static::hasRelativeKeywords($time))) {static::mockConstructorParameters($time, $tz);}// Work-around for PHP bug https://bugs.php.net/bug.php?id67127if (!str_contains((string) .1, .)) {$locale setlocale(LC_NUMERIC, 0); // codeCoverageIgnoresetlocale(LC_NUMERIC, C); // codeCoverageIgnore}try {parent::__construct($time ?: now, static::safeCreateDateTimeZone($tz) ?: null);} catch (Exception $exception) {throw new InvalidFormatException($exception-getMessage(), 0, $exception);}$this-constructedObjectId spl_object_hash($this);if (isset($locale)) {setlocale(LC_NUMERIC, $locale); // codeCoverageIgnore}self::setLastErrors(parent::getLastErrors());}public static function now($tz null){return new static(null, $tz);}