Laravel 自定义模型转换

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39711537/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 14:30:54  来源:igfitidea点击:

Laravel custom model casts

laravellaravel-5eloquent

提问by edlouth

Is there a way in Laravel to add your own custom model casts in addition to the built in ones?

除了内置的模型转换之外,Laravel 有没有办法添加您自己的自定义模型转换?

Currently I can use getters and mutators but these end up getting repeated for lots of fields.

目前我可以使用 getter 和 mutator,但这些最终会在很多领域重复使用。

回答by edlouth

So I ended up going down the Traits route to override various Model methods, this turns out not to be for the fainthearted as attribute casting is quite deeply embedded into the way models work.

所以我最终选择了 Traits 路线来覆盖各种 Model 方法,结果证明这不适合胆小的人,因为属性转换已经深深地嵌入到模型的工作方式中。

To make this work for the most general case, i.e. being able to easily add custom casts, would require a fairly major rewrite of Model.

为了使这项工作适用于最一般的情况,即能够轻松添加自定义强制转换,需要对模型进行相当大的重写。

Here is the Trait I wrote to add a time cast:

这是我为添加时间转换而编写的 Trait:

<?php

namespace App\Models;

use Carbon\Carbon;

trait CustomCasts
{
    /**
     * Cast an attribute to a native PHP type.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return mixed
     */
    protected function castAttribute($key, $value)
    {
        if (is_null($value)) {
            return $value;
        }

        switch ($this->getCastType($key)) {
            case 'int':
            case 'integer':
                return (int) $value;
            case 'real':
            case 'float':
            case 'double':
                return (float) $value;
            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':
            case 'datetime':
                return $this->asDateTime($value);
            case 'timestamp':
                return $this->asTimeStamp($value);
            case 'time':
                return $this->asTime($value);
            default:
                return $value;
        }
    }

    protected function asTime($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) {
            return $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 new Carbon(
                $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 (preg_match('/^(\d{1,2}):(\d{2}):(\d{2})$/', $value)) {
            return Carbon::createFromFormat('h:i:s', $value);
        }

        var_dump($value);

        // 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.
        return Carbon::createFromFormat($this->getTimeFormat(), $value);
    }

    /**
     * Get the format for database stored dates.
     *
     * @return string
     */
    protected function getTimeFormat()
    {
        //return $this->timeFormat ?: $this->getConnection()->getQueryGrammar()->getTimeFormat();
        return $this->timeFormat ?: 'h:i:s';
    }

    /**
     * Set a given attribute on the model.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return $this
     */
    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)) {
            $method = 'set'.Str::studly($key).'Attribute';

            return $this->{$method}($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.
        elseif ($value && (in_array($key, $this->getDates()) || $this->isDateCastable($key))) {
            $value = $this->fromDateTime($value);
        }

        elseif ($value && ($this->isTimeCastable($key))) {
            $value = $this->fromTime($value);
        }

        if ($this->isJsonCastable($key) && ! is_null($value)) {
            $value = $this->asJson($value);
        }

        $this->attributes[$key] = $value;

        return $this;
    }

    /**
     * Convert a Carbon Time to a storable string.
     *
     * @param  \Carbon\Carbon|int  $value
     * @return string
     */
    public function fromTime($value)
    {
        $format = $this->getTimeFormat();

        $value = $this->asTime($value);

        return $value->format($format);
    }

    /**
     * Determine whether a value is Date / DateTime castable for inbound manipulation.
     *
     * @param  string  $key
     * @return bool
     */
    protected function isTimeCastable($key)
    {
        return $this->hasCast($key, ['time']);
    }
}

回答by Vladimir Kovic

If someone interested, I've recently created Laravel package dealing with this problematics.

如果有人感兴趣,我最近创建了 Laravel 包来处理这个问题。

You can find the package and instructions here: https://github.com/vkovic/laravel-custom-casts.

您可以在此处找到软件包和说明:https: //github.com/vkovic/laravel-custom-casts