Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

throw Uncastable results in "Cannot throw objects that do not implement Throwable" from a custom Cast #685

Closed
olexoliinyk0 opened this issue Mar 7, 2024 · 3 comments · Fixed by #697

Comments

@olexoliinyk0
Copy link
Contributor

✏️ Describe the bug
Per docs it says you need to throw Uncastable::create(); to tell the package to try other casts, but the real result is that for PHP 8.2 it throws an \Error with the message Cannot throw objects that do not implement Throwable. Not to mention every static analysis tells me it's wrong.

↪️ To Reproduce
Create a custom cast -> use it so that it definitely throws Uncastable.

use Spatie\LaravelData\Attributes\WithCast;
use Spatie\LaravelData\Casts\Cast;
use Spatie\LaravelData\Casts\Uncastable;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Support\Creation\CreationContext;
use Spatie\LaravelData\Support\DataProperty;

class StringCast implements Cast
{
    public function cast(DataProperty $property, mixed $value, array $properties, CreationContext $context): string
    {
        throw Uncastable::create();
        return 'anything';
    }
}

class PostData extends Data
{
    public function __construct(
        #[WithCast(StringCast::class)]
        public string $myString,
    ) {
    }
}

// here it throws the \Error
$postData = PostData::from([
    'myString' => 'hello',
]);

✅ Expected behavior
The Uncastable class to implement \Thorwable.

By the way how to achieve this "other casts" thing? I've tried to add another #[WithCast(...)] but it results in an \Error with message Attribute "Spatie\LaravelData\Attributes\WithCast" must not be repeated.

🖥️ Versions

Laravel: v10.22.0
Laravel Data: 4.1.0
PHP: 8.2.4

@TomAdam
Copy link

TomAdam commented Mar 12, 2024

This looks to be a documentation issue. If you look at the included casts they all return Uncastable::create() in this situation.

@olexoliinyk0
Copy link
Contributor Author

@TomAdam thanks a lot! That was probably it.

Maybe you also know how to provide multiple casts for the property as they mention in the "try other casts (if available)" part there?

@olexoliinyk0
Copy link
Contributor Author

olexoliinyk0 commented Mar 13, 2024

If someone else also wanders about "try other casts (if available)" part, so far I've only found out that if you return Uncastable from your local/custom Cast, it then will proceed with all the default casts (that are auto-found near the property definition).

This means that if you have something like this (as in the initial message's example):

#[WithCast(StringCast::class)] 
public StringDto|string $myString,

and the StringCast returns Uncastable, it will behave as if that WithCast was never there -- will try to auto-cast it to StringDto (if an array is passed) or to string (if anything else is passed).

UPD: It turns out it will not go to the string if you pass anything else - it will try to find a normalizer for how to transform the passed value to the StringDto and fail if there's no matching.

So writing public SomeDto|any-other-type $myProp; does not make much sense, as it only will try to cast it to the listed DTO type..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants