isakzhanov-r/laravel-value-object

v1.0.0 2021-09-02 13:59 UTC

This package is auto-updated.

Last update: 2024-09-29 06:14:30 UTC


README

允许您在 Eloquent 模型中创建值对象,以铸件的形式存储在数据库中,或者将数据表示为对象。

Total Downloads Latest Stable Version Latest Unstable Version License

内容

安装

要获取 Laravel Value Object 包的最新版本,只需使用 Composer 引入项目。

$ composer require isakzhanov-r/laravel-value-object

当然,如果您想的话,可以手动更新 composer.json 中的 require 依赖块并运行 composer update

{
    "require-dev": {
        "isakzhanov-r/laravel-value-object": "^1.0"
    }
}

用法

创建

要使用 Value Object,您需要创建一个继承自抽象 ValueObject 类的类。

use IsakzhanovR\ValueObject\ValueObject;

class Temperature extends ValueObject 
{
    ....
}

ValueObject 类有必填方法用于实现,即 transformrules

use IsakzhanovR\ValueObject\ValueObject;

class Temperature extends ValueObject 
{
    protected function transformInput($value)
    {
        return $value;
    }

    protected function rules(): array
    {
        return [];
    }
}

ValueObject 继承类有两个方法用于创建对象,通过 new FooValueObject($value, $key) 和通过对 create 函数的静态调用。

  $temperature = new Temperature(25);

  $temperature = Temperature::create(25);

如果未将键传递给函数参数,则它将自动从类名生成。

 {
  #key: "temperature"
  -value: 25
 }

 echo $temperature;  // 25  

验证

为了确保在值对象中的数据有效,使用 Illuminate\Validation\ValidatesWhenResolvedTrait 验证特质。同样,此特质也用于 FormRequest。要定义验证器的规则,请使用 rules 方法。

use IsakzhanovR\ValueObject\ValueObject;

class Temperature extends ValueObject 
{
    ....

    protected function rules(): array
    {
        return [
            $this->key => ['required','numeric','between:-100,100']
        ];
    }
}

如果值对象是数组,则其值以相同的方式验证。

use IsakzhanovR\ValueObject\ValueObject;

class Address extends ValueObject 
{
    ....

    protected function rules(): array
    {
        return [
            $this->key              => ['required', 'array'],
            $this->key . '.country' => ['required', 'string'],
            $this->key . '.city'    => ['required', 'string'],
            $this->key . '.street'  => ['required', 'string'],
            $this->key . '.number'  => ['required', 'numeric'],
        ];
    }
}

对于自定义错误消息,请使用 messages 方法。

use IsakzhanovR\ValueObject\ValueObject;

class Temperature extends ValueObject 
{
    ....

    protected function messages(): array
    {
        return [
            $this->key.'.between' => 'The range of tmp temperatures should be from -100 to +100',
        ];
    }
}

您还可以声明一个返回 truefalseauthorize 方法。

在模型中使用

要在 Eloquent 模型中使用 ValueObject,您不需要添加任何内容,只需在 $casts 中指定它即可。

假设您有一个 Eloquent Whether 模型。您可能希望将转换应用于此字段或获取摄氏度、华氏度或开尔文值。如果多个模型使用此类型字段,复制粘贴 getAttribute 函数可能会很困难。

这样的值对象非常有用。让我们看看如何做。首先,我们创建了一个天气模型,它将有一个温度字段转换为温度值对象。

use Illuminate\Database\Eloquent\Model;

class Weather extends Model
{
    ....
    
    protected $casts = [
        ...
        'temperature' => Temperature::class, 
        ...
      ];
    
    ....
}    

我们将添加以下方法到温度对象中

use IsakzhanovR\ValueObject\ValueObject;

class Temperature extends ValueObject 
{
    ....

    public function inCelsius()
    {
        return (float) $this->value();
    }

    public function inKelvin()
    {
        return (float) $this->value() + 273.15;
    }

    public function inFahrenheit()
    {
        return (float) $this->value() * 1.8 + 32;
    }
}

具有值的对象以素数形式存储在数据库中,可以使用以下方式

    $weather = new Weather;
    $weather->temperature = new Temperature(9);
  
    echo $weather->temperature;                 // Prints '9'
    echo $weather->temperature->value();        // Prints '9'
    echo $weather->temperature->inKelvin();     // Prints 282.15
    echo $weather->temperature->inFahrenheit(); // Prints 48.2

要写入模型,必须使用 ValueObject 的实例

    $weather = new Weather;
    $weather->temperature = new Temperature(9);
    $weather->save()
    
//Or

    $temperature = new Temperature(9);
    $weather = Weather::create(compact('temperature'));
    
//Or
    $weather = Weather::create(Temperature::create(9)->toDTO());

您也可以像在 Eloquent 模型中一样使用访问器和修改器。

class Weather extends Model
{
    ....
    
    protected $casts = [
        ...
        'temperature' => Temperature::class, 
        ...
      ];
    
    protected $appends = ['celsius','kelvin','fahrenheit'] 
    
    public function getCelsiusAttribute()
    {
        return $this->temperature->inCelsius();
    }
    
    public function getKelvinAttribute()
    {
        return $this->temperature->inKelvin();
    }
    
    public function getFahrenheitAttribute()
    {
        return $this->temperature->inFahrenheit();
    }
}  

转换数据

数据转换用于对数据进行轻微的操作,例如清除不必要的字符。此方法在验证之前执行。在 Eloquent 模型中,transformInput 函数也会在 getset 方法中执行。

use IsakzhanovR\ValueObject\ValueObject;

class Title extends ValueObject 
{
    protected function transformInput($value)
    {
        $value = trim(e($value));

        return mb_ucfirst(Str::lower($value));
    }
    
    ...
}

序列化和反序列化

有时具有值的对象可能不是那么简单,可能需要多个字段而不是一个。假设我们有一个包含国家、城市、街道和房屋号码的地址字段的 User 模型,此字段在数据库中为 json 类型。然后我们可以定义 User 模型,就像之前一样。

class User extends Model
{
    protected $casts = [
        'address' => Address::class
    ];
}

然后我们将能够按如下方式定义地址值对象

use IsakzhanovR\ValueObject\ValueObject;

class Address extends ValueObject 
{
    protected function transformInput($value)
    {
        return $value;
    }

    protected function rules(): array
    {
        return [
            $this->key              => ['required', 'array'],
            $this->key . '.country' => ['required', 'string'],
            $this->key . '.city'    => ['required', 'string'],
            $this->key . '.street'  => ['required', 'string'],
            $this->key . '.number'  => ['required', 'numeric'],
        ];
    }
}

为了写入数据,您需要将数组转换为JSON字符串。相应地,当从数据库读取此字符串时,您需要将其转换回数组。为此,我们需要使用serializeunserialize静态方法。这些方法只有在声明了它们的情况下才会执行。

class Address Extends ValueObject
{
    ....
    
    public static function unserialize($value)
    {
        return json_decode($value, true);
    }

    public static function serialize($value)
    {
        return json_encode($value);
    }
    
    ....
} 

许可证

此包遵循MIT许可证发布。