laravel通过访问器转换时间

  • 内容
  • 评论
  • 相关

做项目过程中,很多时候早期设计的数据库的字段类型满足不了当前的需求,但又需要用到该字段的值,比如楼主就遇到这么一个问题。资源表里面有一个duration字段记录资源的时长,类型是varchar,格式是00:00:00。

现在有一个需求,需要将这些资源注入到第三方审核,而第三方接口duration这个字段需要提交的是分钟。换而言之就是,注入第三方接口的时候,duration需要由00:00:00格式转换成分钟。

最简单的方法就是在向第三方注入数据的时候,写个方法将duration的格式转换一下就可以了。但是,这种做法会使得代码越来越凌乱,如同牛皮癣一样这里一块那里一块。我们应该思考是不是有更好的办法,使得代码整洁而且能够实现效果?

我们可以利用laravel的特性trait和模型model中的访问器结合起来去实现。trait能够注入到多个无关的类中,提供代码复用。所以我们就利用这个特性,把这个转换功能写在trait里面。具体trait实现如下:

<?php

namespace Eloquents\Traits\Commons;

use Carbon\Carbon;

trait DurationStrToIntTrait
{
    // const DURATION_UNIT = '秒';  设置时间转换单位

    public function getDurationIntAttribute()
    {
        $value = $this->attributes['duration'];
        if (empty($value)) {
            return 0;
        }
        $time = Carbon::createFromTimeString($value);
        $unit = ['时' => 3600, '分' => 60, '秒' => 1][$this->getDurationUnit()] ?? 0;
        return $unit ? intval(ceil($time->secondsSinceMidnight()/$unit)) : $unit;
    }

    private function getDurationUnit()
    {
        return defined('static::DURATION_UNIT') ? static::DURATION_UNIT : '秒';
    }

    public function setDurationAttribute($value)
    {
        if (is_numeric($value)) {
            $str = ['时' => 'honr', '分'=> 'minute', '秒' => 'second'][$this->getDurationUnit()];
            $value = with(today()->{$str}($value))->toTimeString();
        }
        $this->attributes['duration'] = $value;
    }
}

我们可以看见getDurationIntAttribute()这个方法是实现格式转换的,由00:00:00转换成“时”或“分”或“秒”。其实如果有用过laravel访问器的同学都会发现,getDurationIntAttribute()这个方法其实就是访问器获取duration_int这个属性。回过头来,刚才说了,数据库是只有duration这个字段,而且格式是00:00:00。因此,这时候我们需要额外加一个属性来存放这个转换格式后的时长,我这里叫做duration_int。现在我们来看看模型那边怎么定义:

<?php

namespace Eloquents\Saloon;

use Eloquents\Base\ModelAutoIncrement;
use Eloquents\Saloon\SaloonMaterial;
use Eloquents\Traits\Commons\DurationStrToIntTrait;

class SaloonMaterialResource extends ModelAutoIncrement
{
    use DurationStrToIntTrait;

    const DURATION_UNIT = '分';

    protected $appends = ['duration_int'];

    // 关联客厅幼儿园素材
    public function material()
    {
        return $this->morphOne(SaloonMaterial::class, 'resourcable');
    }
}

我们可以看见,模型里面引用了DurationStrToIntTrait,定义了常量DURATION_UNIT为分钟,并且使用$appends增加了属性duration_int。所以到这里我们就清晰的看见了,楼主在模型中增加了属性来存放转换格式后时长的值,并且把模型的访问器放到了trait里面,然后在trait里面做转换处理。那么往后凡是通过这个模型查询出来的结果,都额外有一个duration_int的属性提供使用而不会影响原本duration这个字段的格式了(因为这个字段还在其他功能中使用)。