I'm working on a project in which I'm using Spatie's laravel-data package (https://spatie.be/docs/laravel-data/v2/introduction) to manage Data Transfer Objects (DTOs). I've got some DTOs which are currently working fine until I got to this one in which I'm facing 2 issues:
1 So far I've been able to use the DTO as parameter in my controller and the request gets converted to the DTO correctly such as:
public function store(ProductData $data) : ProductsResource
{
$product = UpsertProductAction::execute($data);
return new ProductsResource($product->load('category'));
}
Now I've got a custom route for a customer entity. The route is define like this:
Route::post('customers/individuals', [ CustomersController::class, 'addIndividual' ]);
and the addIndividual
method in my controller is:
public function addIndividual(IndividualData $data)
{
echo "here";
print_r($data);
dd("watup");
}
but when I hit the route from my REST client I get nothing back. While debugging it doesn't seem to hit the controller. I get a 200 OK response and Laravel's main page and that's it.
But if I modify my method to be:
public function addIndividual(Request $request)
{
$data = IndividualData::from($request->all());
dd($data);
}
Now I see the dd
being hit.
What's going on here?
Here's my Data class:
<?php
namespace Domain\Customer\DataTransferObjects;
use Illuminate\Validation\Rule;
use Spatie\LaravelData\Data;
class IndividualData extends Data
{
public function __construct(
public readonly ?string $id,
public readonly string $identification,
public readonly string $identification_type,
public readonly string $first_name,
public readonly ?string $middle_name,
public readonly string $last_name,
public readonly ?string $second_last_name,
public readonly ?string $primary_phone_number,
public readonly ?string $primary_phone_number_type,
) {}
public static function rules() : array
{
return [
'identification' => [
'required',
'string',
],
'identification_type' => [
'required',
'string',
],
'first_name' => [
'required',
'string',
],
'middle_name' => [
'string',
'nullable',
'sometimes'
],
'last_name' => [
'required',
'string',
],
'second_last_name' => [
'string',
'nullable',
'sometimes'
],
'primary_phone_number' => [
'string',
'nullable',
'sometimes'
],
'primary_phone_number_type' => [
'string',
'nullable',
'sometimes'
],
];
}
}
2 The second question is related to the first one. So with previous methods such as
public function store(ProductData $data) : ProductsResource
{
$product = UpsertProductAction::execute($data);
return new ProductsResource($product->load('category'));
}
if there's some data that was declared as required in ProductData
and not passed then I get a nice error back like:
{
"message": "The barcode field is required. (and 1 more error)",
"errors": {
"barcode": [
"The barcode field is required."
],
"name": [
"The name has already been taken."
]
}
}
But now, if I simply use the request and inject it to the Data object:
$data = IndividualData::from($request->all());
and there's required data missing from the request then I'm getting back a 500 error with a message such as:
Could not create `Domain\Customer\DataTransferObjects\IndividualData`: the constructor requires 9 parameters, 8 given.Parameters given: id, identification_type, first_name, middle_name, last_name, second_last_name, primary_phone_number, primary_phone_number_type.
If I wrapped in a try/catch
statement the injection of the request to create the DTO:
try {
$data = IndividualData::from($request->all());
$dto = $data;
$customer = UpsertCustomerAction::execute($data);
} catch (\Exception $exception) {
return $exception;
}
then I'm getting:
ArgumentCountError: Domain\Customer\DataTransferObjects\IndividualData::__construct(): Argument #2 ($identification) not passed in /Users/hansgruber/Desktop/webdev/projects/dundermifflin-be/src/Domain/Customer/DataTransferObjects/IndividualData.php:10 Stack trace: #0 /Users/hansgruber/Desktop/webdev/projects/dundermifflin-be/vendor/spatie/laravel-data/src/Resolvers/DataFromArrayResolver.php(57): Domain\Customer\DataTransferObjects\IndividualData->__construct(NULL, NULL, 'CC', 'Inigo', NULL, 'Montoya', 'Arias', '6464654635', 'cel') #1 /Users/hansgruber/Desktop/webdev/projects/dundermifflin-be/vendor/spatie/laravel-data/src/Resolvers/DataFromArrayResolver.php(38): Spatie\LaravelData\Resolvers\DataFromArrayResolver->createData(Object(Spatie\LaravelData\Support\DataClass), Object(Illuminate\Support\Collection)) #2 /Users/hansgruber/Desktop/webdev/projects/dundermifflin-be/vendor/laravel/framework/src/Illuminate/Collections/Traits/EnumeratesValues.php(731): Spatie\LaravelData\Resolvers\DataFromArrayResolver->Spatie\LaravelData\Resolvers{closure}(Object(Illuminate\Support\Collection)) #3 /Users/hansgruber/Desktop/webdev/projects/dundermifflin-be/vendor/spatie/laravel-data/src/Resolvers/DataFromArrayResolver.php(38): Illuminate\Support\Collection->pipe(Object(Closure)) #4 /Users/hansgruber/Desktop/webdev/projects/dundermifflin-be/vendor/spatie/laravel-data/src/Resolvers/DataFromSomethingResolver.php(45): Spatie\LaravelData\Resolvers\DataFromArrayResolver->execute('Domain\Customer...', Object(Illuminate\Support\Collection)) #5
why I'm not longer getting the nicely formatted errors?
Thanks.