Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Update ArrayCaster to accept objects (of the correct class) alongside arrays that get cast like normal #217

Merged
merged 2 commits into from
Jul 15, 2021

Conversation

code-distortion
Copy link
Contributor

@code-distortion code-distortion commented Jun 22, 2021

Hi Brendt and co. Thanks for your work on this useful project.

I came across a situation that would be helpful to me if it were altered slightly. I hope it's something that's helpful for others too…


Usually, I build DTO objects by passing the desired child DTO objects. But sometimes I need these same DTOs to be hydrated from serialised arrays.

When using the ArrayCaster to cast arrays into objects, the elements to be cast must be arrays (otherwise an error is generated).

This PR lets you also pass in objects of the desired type (no cast needed), as well as the original behaviour (which casts the array). This way, both situations are covered.

class Bar extends DataTransferObject
{
    /** @var Foo[] */
    public array $collectionOfFoo;
}

class Baz extends DataTransferObject
{
    /** @var Foo[] */
    #[CastWith(ArrayCaster::class, itemType: Foo::class)]
    public array $collectionOfFoo;
}

class Foo extends DataTransferObject
{
    public string $name;
}
// normal usage without casting
$bar = new Bar(
    collectionOfFoo: [
        new Foo(name: 'a'),
        new Foo(name: 'b'),
        new Foo(name: 'c'),
    ],
);

// normal usage when casting
$bar = new Baz([
    'collectionOfFoo' => [
        ['name' => 'a'],
        ['name' => 'b'],
        ['name' => 'c'],
    ],
]);

// passing objects when Baz is expecting arrays to cast - would normally generate an error
$bar = new Baz(
    collectionOfFoo: [
        new Foo(name: 'a'),
        new Foo(name: 'b'),
        new Foo(name: 'c'),
    ],
);

// a combination of arrays and objects - would normally generate an error
$bar = new Baz([
    'collectionOfFoo' => [
        ['name' => 'a'],
        new Foo(name: 'b'),
        ['name' => 'c'],
    ],
]);

I split the PR up into two commits; The first containing updated tests to show the error, and the second with the updated code. I can squash them if desired.

@code-distortion code-distortion changed the title Update ArrayCaster to accept objects (of the correct class) alongside casting arrays like normal Update ArrayCaster to accept objects (of the correct class) alongside arrays that get cast like normal Jun 23, 2021
…d of arrays to be cast like normal) causes an Error to be thrown
@code-distortion code-distortion marked this pull request as ready for review June 23, 2021 07:04
@daniser
Copy link
Contributor

daniser commented Jun 23, 2021

How about checking that $data is actually array or object of specified type and otherwise throwing an exception?
Like this (taken out into separate method for simplicity):

private function castArray(mixed $value): array
{
    return array_map([$this, 'makeItem'], $value);
}

private function makeItem(mixed $data): mixed
{
    if ($data instanceof $this->itemType) {
        return $data;
    }

    if (is_array($data)) {
        return new $this->itemType(...$data);
    }

    throw new LogicException('Item data must either match specified type or be array.');
}

…t's cast like normal), or an instance of the desired class (that's accepted as-is, and not cast)
@code-distortion
Copy link
Contributor Author

Hi daniser, I like that refactor. I've incorporated it into the PR.

@brendt brendt merged commit e5d31bc into spatie:master Jul 15, 2021
@brendt
Copy link
Contributor

brendt commented Jul 15, 2021

Looks good! Sorry for the delay in merging.

aidan-casey added a commit to aidan-casey/data-transfer-object that referenced this pull request Jul 16, 2021
Rebase to respect spatie#217
aidan-casey added a commit to aidan-casey/data-transfer-object that referenced this pull request Jul 16, 2021
aidan-casey added a commit to aidan-casey/data-transfer-object that referenced this pull request Jul 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
3 participants