0

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 that 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 a 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 defined 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.

1 Answer 1

0

Spent hours looking around for this issue! It turns out I didn't have the following header set in my request in my REST client:

Accept: application/json

Worse thing of all is that all my other requests had it but most of them were a modified copy of one that had the header, I manually created the one that was failing reason why I had forgotten about it

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.