文章目录
前言
model->created_at 是 Illuminate\Support\Carbon 对象
model->toArray()['created_at'] 是字符串(国际标准时间)
model->created_at 为啥是 Carbon对象
- model->created_at 触发魔术方法 __get()
namespace Illuminate\Database\Eloquent;
use ArrayAccess;
use Illuminate\Contracts\Broadcasting\HasBroadcastChannel;
use Illuminate\Contracts\Queue\QueueableCollection;
use Illuminate\Contracts\Queue\QueueableEntity;
use Illuminate\Contracts\Routing\UrlRoutable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\CanBeEscapedWhenCastToString;
use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Database\ConnectionResolverInterface as Resolver;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\Concerns\AsPivot;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection as BaseCollection;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\ForwardsCalls;
use JsonSerializable;
use LogicException;
abstract class Model implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToString, HasBroadcastChannel, Jsonable, JsonSerializable, QueueableEntity, UrlRoutable
{
use Concerns\HasAttributes,
Concerns\HasEvents,
Concerns\HasGlobalScopes,
Concerns\HasRelationships,
Concerns\HasTimestamps,
Concerns\HidesAttributes,
Concerns\GuardsAttributes,
ForwardsCalls;
public function __get($key)
{
return $this->getAttribute($key);
}
}
2.调用 HasAttributes的getAttribute方法,getAttributeValue方法 取得value值
- 判断模型timestamps属性,false 返回原始value
- 然后判断字段key是否在模型的static::CREATED_AT | static::UPDATED_AT
- 这两个值默认是 create_at | updated_at 可以通过模型重新设置这两个属性
- 在的话就使用transformModelValue将 value转换成 Carbon 对象
namespace Illuminate\Database\Eloquent\Concerns;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use DateTimeInterface;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\InvalidCastException;
use Illuminate\Database\Eloquent\JsonEncodingException;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\LazyLoadingViolationException;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection as BaseCollection;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Str;
use InvalidArgumentException;
use LogicException;
use ReflectionClass;
use ReflectionMethod;
use ReflectionNamedType;
trait 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->attributes) ||
array_key_exists($key, $this->casts) ||
$this->hasGetMutator($key) ||
$this->hasAttributeMutator($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);
}
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);
} elseif ($this->hasAttributeGetMutator($key)) {
return $this->mutateAttributeMarkedAttribute($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;
}
public function getDates()
{
if (! $this->usesTimestamps()) {
return $this->dates;
}
$defaults = [
$this->getCreatedAtColumn(),
$this->getUpdatedAtColumn(),
];
return array_unique(array_merge($this->dates, $defaults));
}
public function getDates()
{
if (! $this->usesTimestamps()) {
return $this->dates;
}
$defaults = [
$this->getCreatedAtColumn(),
$this->getUpdatedAtColumn(),
];
return array_unique(array_merge($this->dates, $defaults));
}
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 CarbonInterface) {
return Date::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 Date::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 Date::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 Date::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.
try {
$date = Date::createFromFormat($format, $value);
} catch (InvalidArgumentException $e) {
$date = false;
}
return $date ?: Date::parse($value);
}
}
model->toArray()[‘created_at’] 为啥是国际标准时间
1.toArray() 调用了 Illuminate/Database/Eloquent/Concerns/HasAttributes.php 的 attributesToArray方法
2.attributesToArray 调用 addDateAttributesToArray
- addDateAttributesToArray 中判断 判断模型timestamps属性,false 跳过处理
- 判断 key 是否在 static::CREATED_AT | static::UPDATED_AT中,不在 跳过处理
- 将value处理成Carbon对象
3.通过serializeDate(Carbon) 将Carbon处理成国际标准时间
可以通过重写 serializeDate 来转换toArray返回的时间格式(Y-m-d H:i:s)
namespace App\Models\Auth;
use App\Models\Traits\RichSoftDeletes;
use DateTimeInterface;
class User extends \Illuminate\Database\Eloquent\Model
{
use RichSoftDeletes;
protected $fillable = ['name', 'mobile', 'password', 'creator', 'updater'];
protected $hidden = ['mobile'];
protected $casts = [
'id' => 'string',
'creator' => 'boolean',
];
public function toArray()
{
$res = parent::toArray();
$values = array_values($res);
$keys = array_keys($res);
$keys = array_map(function ($key) {
return ucfirst($key);
}, $keys);
return array_combine($keys, $values);
}
public function serializeDate(DateTimeInterface $date)
{
return $date->format('Y-m-d H:i:s');
}
}