isakzhanov-r / laravel-value-object
Eloquent 的值对象
Requires
- php: ^7.4|^8.0
- ext-json: *
- ext-pdo: *
- illuminate/database: ^7.0|^8.0
- illuminate/support: ^7.0|^8.0
Requires (Dev)
- mockery/mockery: ^0.9|^1.0
- orchestra/testbench: ^3.8|^4.0|^5.0|^6.0
- phpunit/phpunit: ^8.0|^9.0
README
允许您在 Eloquent 模型中创建值对象,以铸件的形式存储在数据库中,或者将数据表示为对象。
内容
安装
要获取 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
类有必填方法用于实现,即 transform
和 rules
。
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', ]; } }
您还可以声明一个返回 true
或 false
的 authorize
方法。
在模型中使用
要在 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
函数也会在 get
和 set
方法中执行。
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字符串。相应地,当从数据库读取此字符串时,您需要将其转换回数组。为此,我们需要使用serialize
和unserialize
静态方法。这些方法只有在声明了它们的情况下才会执行。
class Address Extends ValueObject { .... public static function unserialize($value) { return json_decode($value, true); } public static function serialize($value) { return json_encode($value); } .... }
许可证
此包遵循MIT许可证发布。