Skip to content

数据传输对象

Testing Is Documentation

tests/Support/DtoTest.php

QueryPHP 提供了一个简单的数据传输对象组件。

Uses

php
<?php

use Leevel\Kernel\Utils\Api;

all 获取全部属性数据(下划线属性命名风格)

php
public function testAllUnCamelizeStyle(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => $dtoProp1 = new DtoProp1(),
        'demoObject2Prop' => $dtoProp2 = new DtoProp2(),
        'demoObject3Prop' => $dto2 = new Dto2(['demoStringProp' => 'hello world']),
    ]);

    $data = $dto1->all();
    self::assertSame('foo', $data['demo_string_prop']);
    self::assertSame(1, $data['demo_int_prop']);
    self::assertSame(1.5, $data['demo_float_prop']);
    self::assertTrue($data['demo_true_prop']);
    self::assertFalse($data['demo_false_prop']);
    self::assertSame($dtoProp1, $data['demo_object_prop']);
    self::assertSame($dtoProp2, $data['demo_object2_prop']);
    self::assertSame($dto2, $data['demo_object3_prop']);
    self::assertTrue($data['demo_mixed_prop']);
}

all 获取全部属性数据(驼峰属性命名风格)

php
public function testAllCamelizeStyle(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => $dtoProp1 = new DtoProp1(),
        'demoObject2Prop' => $dtoProp2 = new DtoProp2(),
        'demoObject3Prop' => $dto2 = new Dto2(['demoStringProp' => 'hello world']),
    ]);

    $data = $dto1->all(false);
    self::assertSame('foo', $data['demoStringProp']);
    self::assertSame(1, $data['demoIntProp']);
    self::assertSame(1.5, $data['demoFloatProp']);
    self::assertTrue($data['demoTrueProp']);
    self::assertFalse($data['demoFalseProp']);
    self::assertSame($dtoProp1, $data['demoObjectProp']);
    self::assertSame($dtoProp2, $data['demoObject2Prop']);
    self::assertSame($dto2, $data['demoObject3Prop']);
    self::assertTrue($data['demoMixedProp']);
}

默认忽略丢失的值

php
public function testDefaultIgnoreMissingValues(): void
{
    $dto1 = new Dto1([
        'demo_not_found' => 1,
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    self::assertSame('foo', $dto1->demo_string_prop);
}

strict 从数组或者数据传输对象创建不可变数据传输对象

php
public function testStrict(): void
{
    $this->expectException(\UnexpectedValueException::class);
    $this->expectExceptionMessage(
        'Public properties `demo_not_found` of data transfer object `Tests\\Support\\Fixtures\\Dto1` was not defined.'
    );

    Dto1::strict([
        'demo_not_found' => 1,
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);
}

fromArray 从数组创建数据传输对象

php
public function testFromArray(): void
{
    $dto1 = Dto1::fromArray([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    $data = $dto1
        ->only(['demoIntProp', 'demoObject3Prop'])
        ->toArray()
    ;
    self::assertSame([
        'demo_int_prop' => 1,
        'demo_object3_prop' => [
            'demo_string_prop' => 'hello world',
        ],
    ], $data);
}

only 设置白名单属性

php
public function testOnly(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    $data = $dto1
        ->only(['demoIntProp', 'demoObject3Prop'])
        ->toArray()
    ;
    self::assertSame([
        'demo_int_prop' => 1,
        'demo_object3_prop' => [
            'demo_string_prop' => 'hello world',
        ],
    ], $data);
}

only 设置白名单属性,合并默认白名单属性

php
public function testOnlyWithOnlyPropertys(): void
{
    $dto1 = new DtoToArray([
        'demoStringProp' => 'hello',
        'demoIntProp' => 123456,
        'demoIntOrStringProp' => 45,
    ]);

    $data = $dto1->toArray();
    self::assertSame([
        'demo_int_prop' => 123456,
        'demo_int_or_string_prop' => 45,
    ], $data);

    $data = $dto1->only(['demoStringProp'])->toArray();
    self::assertSame([
        'demo_string_prop' => 'hello',
        'demo_int_prop' => 123456,
        'demo_int_or_string_prop' => 45,
    ], $data);
}

only 设置白名单属性,覆盖默认白名单属性

php
public function testOnlyWithOnlyPropertysOverrideProperty(): void
{
    $dto1 = new DtoToArray([
        'demoStringProp' => 'hello',
        'demoIntProp' => 123456,
        'demoIntOrStringProp' => 45,
    ]);

    $data = $dto1->toArray();
    self::assertSame([
        'demo_int_prop' => 123456,
        'demo_int_or_string_prop' => 45,
    ], $data);

    $data = $dto1->only(['demoStringProp'], true)->toArray();
    self::assertSame([
        'demo_string_prop' => 'hello',
    ], $data);
}

except 设置黑名单属性

php
public function testExcept(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    $data = $dto1
        ->except(
            ['demoIntProp', 'demoObject3Prop', 'demoObjectProp',
                'demoObject3Prop', 'demo_false_prop', 'demo_object2_prop', ]
        )
        ->toArray()
    ;
    self::assertSame([
        'demo_string_prop' => 'foo',
        'demo_float_prop' => 1.5,
        'demo_true_prop' => true,
        'demo_mixed_prop' => true,
    ], $data);
}

except 设置黑名单属性,合并默认黑名单属性

php
public function testExceptWithExceptPropertys(): void
{
    $dto1 = new DtoToArray2([
        'demoStringProp' => 'hello',
        'demoIntProp' => 123456,
        'demoIntOrStringProp' => 45,
    ]);

    $data = $dto1->toArray();
    self::assertSame([
        'demo_string_prop' => 'hello',
    ], $data);

    $data = $dto1->except(['demoStringProp'])->toArray();
    self::assertSame([], $data);
}

except 设置黑名单属性,覆盖默认黑名单属性

php
public function testExceptWithExceptPropertysOverrideProperty(): void
{
    $dto1 = new DtoToArray2([
        'demoStringProp' => 'hello',
        'demoIntProp' => 123456,
        'demoIntOrStringProp' => 45,
    ]);

    $data = $dto1->toArray();
    self::assertSame([
        'demo_string_prop' => 'hello',
    ], $data);

    $data = $dto1->except(['demo_int_prop'], true)->toArray();
    self::assertSame([
        'demo_string_prop' => 'hello',
        'demo_int_or_string_prop' => 45,
    ], $data);
}

withoutNull 设置转换数组时忽略 NULL 值

php
public function testWithoutNull(): void
{
    $dto1 = new DtoToArray3([
        'demoStringProp' => 'hello',
        'demoIntProp' => 123456,
    ]);
    $data = $dto1->toArray();
    self::assertSame([
        'demo_string_prop' => 'hello',
        'demo_int_prop' => 123456,
        'demo_configal_int_prop' => null,
    ], $data);

    $data = $dto1->withoutNull()->toArray();
    self::assertSame([
        'demo_string_prop' => 'hello',
        'demo_int_prop' => 123456,
    ], $data);
}

toArray 对象转数组(下划线属性命名风格)

php
public function testToArrayUnCamelizeStyle(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => $dtoProp2 = new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    $data = $dto1->toArray();
    self::assertSame('foo', $data['demo_string_prop']);
    self::assertSame(1, $data['demo_int_prop']);
    self::assertSame(1.5, $data['demo_float_prop']);
    self::assertTrue($data['demo_true_prop']);
    self::assertFalse($data['demo_false_prop']);
    self::assertSame(['demo1' => 'hello', 'demo2' => 'world'], $data['demo_object_prop']);
    self::assertSame($dtoProp2, $data['demo_object2_prop']);
    self::assertSame(['demo_string_prop' => 'hello world'], $data['demo_object3_prop']);
    self::assertTrue($data['demo_mixed_prop']);
}

toArray.camelizeNamingStyle 对象转数组(驼峰属性命名风格)

php
public function testToArrayCamelizeStyle(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => $dtoProp2 = new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    $data = $dto1->camelizeNamingStyle()->toArray();
    self::assertSame('foo', $data['demoStringProp']);
    self::assertSame(1, $data['demoIntProp']);
    self::assertSame(1.5, $data['demoFloatProp']);
    self::assertTrue($data['demoTrueProp']);
    self::assertFalse($data['demoFalseProp']);
    self::assertSame(['demo1' => 'hello', 'demo2' => 'world'], $data['demoObjectProp']);
    self::assertSame($dtoProp2, $data['demoObject2Prop']);
    self::assertSame(['demoStringProp' => 'hello world'], $data['demoObject3Prop']);
    self::assertTrue($data['demoMixedProp']);
}

toArray 对象转数组带有白名单属性设置

php
public function testToArrayWithOnlyPropertys(): void
{
    $dto1 = new DtoToArray([
        'demoStringProp' => 'hello',
        'demoIntProp' => 123456,
        'demoIntOrStringProp' => 45,
    ]);

    $data = $dto1->toArray();
    self::assertSame([
        'demo_int_prop' => 123456,
        'demo_int_or_string_prop' => 45,
    ], $data);
}

数据传输对象属性数组访问 ArrayAccess.offsetExists 支持

php
public function testOffsetExists(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    self::assertTrue(isset($dto1['demo_string_prop']));
    self::assertTrue(isset($dto1['demoStringProp']));
}

数据传输对象属性数组访问 ArrayAccess.offsetSet 支持

php
public function testOffsetSet(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    $dto1['demo_string_prop'] = 'hello_world';
    self::assertSame('hello_world', $dto1['demo_string_prop']);
    self::assertSame('hello_world', $dto1['demoStringProp']);

    $dto1['demo_string_prop'] = 'hello_world2';
    self::assertSame('hello_world2', $dto1['demo_string_prop']);
    self::assertSame('hello_world2', $dto1['demoStringProp']);
}

数据传输对象属性数组访问 ArrayAccess.offsetGet 支持

php
public function testOffsetGet(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    self::assertSame('foo', $dto1['demo_string_prop']);
    self::assertSame('foo', $dto1['demoStringProp']);
}

数据传输对象属性数组访问 ArrayAccess.offsetUnset 支持

php
public function testOffsetUnset(): void
{
    $this->expectException(\TypeError::class);
    $this->expectExceptionMessage(
        'Cannot assign null to property Tests\Support\Fixtures\Dto1::$demoStringProp of type string'
    );

    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    self::assertSame('foo', $dto1['demo_string_prop']);
    self::assertSame('foo', $dto1['demoStringProp']);

    unset($dto1['demo_string_prop']);
}

数据传输对象属性访问魔术方法 __isset 支持

php
public function testMagicIsset(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);
    self::assertTrue(isset($dto1->demo_string_prop));
    self::assertTrue(isset($dto1->demo_int_prop));
}

数据传输对象属性访问魔术方法 __set 支持

php
public function testMagicSet(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);

    self::assertSame('foo', $dto1->demo_string_prop);
    $dto1->demo_string_prop = 'hello';
    self::assertSame('hello', $dto1->demo_string_prop);
}

数据传输对象属性访问魔术方法 __get 支持

php
public function testMagicGet(): void
{
    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);
    self::assertSame('foo', $dto1->demo_string_prop);
}

实体属性访问魔术方法 __unset 支持

php
public function testMagicUnset(): void
{
    $this->expectException(\TypeError::class);
    $this->expectExceptionMessage(
        'Cannot assign null to property Tests\Support\Fixtures\Dto1::$demoStringProp of type string'
    );

    $dto1 = new Dto1([
        'demo_string_prop' => 'foo',
        'demoIntProp' => 1,
        'demoFloatProp' => 1.5,
        'demoObjectProp' => new DtoProp1(),
        'demoObject2Prop' => new DtoProp2(),
        'demoObject3Prop' => new Dto2(['demoStringProp' => 'hello world']),
    ]);
    $dto1->demo_string_prop = null;
}

初始化默认值填充方法

php
namespace Tests\Support\Fixtures;

use Leevel\Support\Dto;

class Dto3 extends Dto
{
    public string $demoStringProp;

    protected function demoStringPropDefaultValue(): string
    {
        return 'hello world';
    }
}
php
public function test2(): void
{
    $dto = new Dto3();
    self::assertSame('hello world', $dto->demoStringProp);
}

初始化转换数据方法

php
namespace Tests\Support\Fixtures;

use Leevel\Support\Dto;

class Dto4 extends Dto
{
    public string $demoStringProp;

    protected function demoStringPropTransformValue(int $value): string
    {
        return (string) $value;
    }
}
php
public function test3(): void
{
    $dto = new Dto4(['demoStringProp' => 123456]);
    self::assertSame('123456', $dto->demoStringProp);
}

初始化内置转换数据方法

php
namespace Tests\Support\Fixtures;

use Leevel\Support\Dto;

class Dto6 extends Dto
{
    public int $int1 = 0;
    public string $string2 = '';
    public float $float3 = 0.0;
    public bool $bool4 = true;
    public array $array5 = [];
}
php
public function test5(): void
{
    $dto = new Dto6([
        'int1' => 'string',
        'string2' => 123456,
        'float3' => 'world',
        'bool4' => 0,
        'array5' => 'hello',
    ]);
    self::assertSame(0, $dto->int1);
    self::assertSame('123456', $dto->string2);
    self::assertSame(0.0, $dto->float3);
    self::assertFalse($dto->bool4);
    self::assertSame(['hello'], $dto->array5);
}