Laravel模型实现EPOCH时间转换为用户所属时区的问题
问题根因
你的方案不生效核心是3个问题:
scan()方法返回的是原始数组而非File模型实例,访问器仅在模型实例读取属性时生效,原始数组不会触发访问器逻辑- 字段名是大写
EPOCH,和访问器getEpochAttribute命名不匹配,Laravel默认蛇形属性转换规则下会识别为小写字段,大小写敏感的ORM包会直接跳过访问器 - 时区查询逻辑写在访问器里,每次调用都要查询3次数据库,性能损耗极大
修复方案
步骤1:修改查询逻辑,将scan结果转为模型实例
// 原代码直接返回原始数组,不会触发模型的属性转换 // $collection = collect(File::filterBetween('EPOCH', $date)->filter('SUI','=',$asset->mac_id)->scan()); // 替换为手动将原始结果转为模型实例 $rawResults = File::filterBetween('EPOCH', $date)->filter('SUI','=',$asset->mac_id)->scan(); $collection = collect($rawResults)->map(fn($item) => new File($item));
步骤2:用自定义Cast实现时区转换(比访问器更规范、可复用)
首先创建自定义Cast类app/Casts/UserTimezoneDatetime.php:
<?php namespace App\Casts; use Carbon\Carbon; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Support\Facades\Auth; use App\Models\ExtraField; use App\Models\ExtraFieldValue; class UserTimezoneDatetime implements CastsAttributes { // 缓存当前请求的用户时区,避免重复查询数据库 protected static ?string $userTimezone = null; protected function getTimezone(): string { if (self::$userTimezone) { return self::$userTimezone; } $user = Auth::user(); if (!$user) { return self::$userTimezone = 'America/Sao_Paulo'; } $extraField = ExtraField::where('description', 'TimeZone')->first(); if (!$extraField) { return self::$userTimezone = 'America/Sao_Paulo'; } $timezoneValue = ExtraFieldValue::where('extra_field_id', $extraField->id) ->where('user_id', $user->id) ->value('value'); return self::$userTimezone = $timezoneValue ?: 'America/Sao_Paulo'; } // 读取属性时的转换逻辑 public function get($model, string $key, $value, array $attributes) { if (is_null($value)) { return null; } // EPOCH为秒级时间戳直接转Carbon实例,存储统一用UTC避免混乱 $date = is_numeric($value) ? Carbon::createFromTimestamp($value, 'UTC') : Carbon::parse($value, 'UTC'); return $date->setTimezone($this->getTimezone())->format('Y-m-d H:i:s'); } // 写入属性时的转换逻辑,转回UTC时间戳 public function set($model, string $key, $value, array $attributes) { if (is_null($value)) { return null; } return Carbon::parse($value, $this->getTimezone())->setTimezone('UTC')->getTimestamp(); } }
步骤3:修改File模型配置
删掉原有的getEpochAttribute访问器,替换为自定义Cast,同时关闭蛇形属性转换适配大写字段:
class File extends BaseDynamo { protected $table = 'Dados'; protected $primaryKey = 'SUI'; protected $sortKey = ['EPOCH','desc']; // 关闭蛇形属性转换,适配大写的字段名 public static $snakeAttributes = false; protected $casts = [ 'EPOCH' => \App\Casts\UserTimezoneDatetime::class, ]; }
验证效果
修改后执行查询即可自动获取用户时区的时间:
$my_datetime = $collection->whereIn($variable->key, [0,1])->pluck('EPOCH')->last();
内容的提问来源于stack exchange,提问作者Marcius Leandro
相关产品推荐
相关产品推荐

