From e4c7667b472293ab67bb1536c68d0c25ab064dd3 Mon Sep 17 00:00:00 2001 From: Jan Henk Hazelaar Date: Wed, 20 May 2026 11:25:25 +0200 Subject: [PATCH] Add UnitType DTO for list and detail responses UnitTypeResource was returning raw Saloon Response objects, leaving callers to dig into Dutch JSON keys themselves. Introduce a typed UnitType DTO that covers both the list and detail responses, with nullable detail-only fields, so the resource is on par with ProjectResource and consumers get typed access end-to-end. Changelog: added --- src/Data/UnitType.php | 77 +++++++++++++++++++ src/Requests/UnitTypes/GetUnitTypeRequest.php | 7 ++ .../UnitTypes/GetUnitTypesRequest.php | 13 ++++ src/Resources/UnitTypeResource.php | 13 ++-- tests/Data/UnitTypeTest.php | 61 +++++++++++++++ .../UnitTypes/GetUnitTypeRequestTest.php | 63 +++++++++++++++ .../UnitTypes/GetUnitTypesRequestTest.php | 58 ++++++++++++++ tests/Resources/UnitTypeResourceTest.php | 50 +++++++----- 8 files changed, 316 insertions(+), 26 deletions(-) create mode 100644 src/Data/UnitType.php create mode 100644 tests/Data/UnitTypeTest.php diff --git a/src/Data/UnitType.php b/src/Data/UnitType.php new file mode 100644 index 0000000..a7628dd --- /dev/null +++ b/src/Data/UnitType.php @@ -0,0 +1,77 @@ +projectUuid}/projectwoningen/{$this->uuid}/"; } + + public function createDtoFromResponse(Response $response): UnitType + { + return UnitType::fromResponse($response->json('data.object')); + } } diff --git a/src/Requests/UnitTypes/GetUnitTypesRequest.php b/src/Requests/UnitTypes/GetUnitTypesRequest.php index 9295c20..dcef5cd 100644 --- a/src/Requests/UnitTypes/GetUnitTypesRequest.php +++ b/src/Requests/UnitTypes/GetUnitTypesRequest.php @@ -2,8 +2,10 @@ namespace NieuwbouwOffice\PhpSdk\Requests\UnitTypes; +use NieuwbouwOffice\PhpSdk\Data\UnitType; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; class GetUnitTypesRequest extends Request { @@ -15,4 +17,15 @@ public function resolveEndpoint(): string { return "/projects/{$this->projectUuid}/projectwoningen/"; } + + /** + * @return UnitType[] + */ + public function createDtoFromResponse(Response $response): array + { + return array_map( + fn (array $object) => UnitType::fromResponse($object), + $response->json('data.objects'), + ); + } } diff --git a/src/Resources/UnitTypeResource.php b/src/Resources/UnitTypeResource.php index fe72c7f..8a407f1 100644 --- a/src/Resources/UnitTypeResource.php +++ b/src/Resources/UnitTypeResource.php @@ -2,11 +2,11 @@ namespace NieuwbouwOffice\PhpSdk\Resources; +use NieuwbouwOffice\PhpSdk\Data\UnitType; use NieuwbouwOffice\PhpSdk\Requests\UnitTypes\GetUnitTypeRequest; use NieuwbouwOffice\PhpSdk\Requests\UnitTypes\GetUnitTypesRequest; use Saloon\Http\BaseResource; use Saloon\Http\Connector; -use Saloon\Http\Response; class UnitTypeResource extends BaseResource { @@ -17,13 +17,16 @@ public function __construct( parent::__construct($connector); } - public function list(): Response + /** + * @return UnitType[] + */ + public function list(): array { - return $this->connector->send(new GetUnitTypesRequest($this->projectUuid)); + return $this->connector->send(new GetUnitTypesRequest($this->projectUuid))->dto(); } - public function get(string $uuid): Response + public function get(string $uuid): UnitType { - return $this->connector->send(new GetUnitTypeRequest($this->projectUuid, $uuid)); + return $this->connector->send(new GetUnitTypeRequest($this->projectUuid, $uuid))->dto(); } } diff --git a/tests/Data/UnitTypeTest.php b/tests/Data/UnitTypeTest.php new file mode 100644 index 0000000..d63c581 --- /dev/null +++ b/tests/Data/UnitTypeTest.php @@ -0,0 +1,61 @@ +Lange beschrijving

', + count: 22, + price_from: 317600, + price_to: 423300, + lot_area_from: 50, + lot_area_to: 80, + living_area_from: 79, + living_area_to: 79, + volume_from: 200, + volume_to: 240, + bedrooms_from: 2, + bedrooms_to: 2, + rooms_from: 3, + rooms_to: 3, + ownership: 'Eigendom', + tenure: 'Koopwoning', + ); + + expect($unitType->uuid)->toBe('unit-1') + ->and($unitType->is_online)->toBeTrue() + ->and($unitType->title)->toBe('Rug-aan-rug woning') + ->and($unitType->kind)->toBe('Tussenwoning') + ->and($unitType->order)->toBe(1) + ->and($unitType->short_description)->toBe('Korte beschrijving') + ->and($unitType->home_count)->toBe(22) + ->and($unitType->media_count)->toBe(7) + ->and($unitType->plan_part)->toBe('Plandeel A') + ->and($unitType->description)->toBe('Lange beschrijving') + ->and($unitType->description_html)->toBe('

Lange beschrijving

') + ->and($unitType->count)->toBe(22) + ->and($unitType->price_from)->toBe(317600) + ->and($unitType->price_to)->toBe(423300) + ->and($unitType->lot_area_from)->toBe(50) + ->and($unitType->lot_area_to)->toBe(80) + ->and($unitType->living_area_from)->toBe(79) + ->and($unitType->living_area_to)->toBe(79) + ->and($unitType->volume_from)->toBe(200) + ->and($unitType->volume_to)->toBe(240) + ->and($unitType->bedrooms_from)->toBe(2) + ->and($unitType->bedrooms_to)->toBe(2) + ->and($unitType->rooms_from)->toBe(3) + ->and($unitType->rooms_to)->toBe(3) + ->and($unitType->ownership)->toBe('Eigendom') + ->and($unitType->tenure)->toBe('Koopwoning'); +}); diff --git a/tests/Requests/UnitTypes/GetUnitTypeRequestTest.php b/tests/Requests/UnitTypes/GetUnitTypeRequestTest.php index be7bfd2..38b7c10 100644 --- a/tests/Requests/UnitTypes/GetUnitTypeRequestTest.php +++ b/tests/Requests/UnitTypes/GetUnitTypeRequestTest.php @@ -1,7 +1,11 @@ getMethod())->toBe(Method::GET); @@ -18,3 +22,62 @@ expect((new GetUnitTypeRequest('proj-1', 'unit-9'))->resolveEndpoint()) ->toBe('/projects/proj-1/projectwoningen/unit-9/'); }); + +it('creates a UnitType DTO from the response', function () { + $mockClient = new MockClient([ + GetUnitTypeRequest::class => MockResponse::make([ + 'data' => [ + 'object' => [ + 'Projectwoning_UUId' => '631c2a57a0ce5edc989c67d3661f4711', + 'Projectwoning_Online' => '-1', + 'Plandeel' => null, + 'Projectwoning_Titel' => 'Rug-aan-rug woning', + 'Woning_Type' => 'Tussenwoning', + 'Projectwoning_Volgorde' => '1', + 'Projectwoning_Beschrijving_Kort' => null, + 'Projectwoning_Beschrijving_Lang' => null, + 'Projectwoning_Beschrijving_HTML' => null, + 'Projectwoning_Aantal' => '22', + 'Projectwoning_PrijsVan' => '317600', + 'Projectwoning_PrijsTot' => '423300', + 'Projectwoning_KavelOppVan' => null, + 'Projectwoning_KavelOppTot' => null, + 'Projectwoning_WoonOppVan' => '79', + 'Projectwoning_WoonOppTot' => '79', + 'Projectwoning_InhoudVan' => null, + 'Projectwoning_InhoudTot' => null, + 'Projectwoning_SlaapkamersVan' => '2', + 'Projectwoning_SlaapkamersTot' => '2', + 'Eigendom' => 'Eigendom', + 'Huurkoop' => 'Koopwoning', + 'NrWoningen' => '22', + 'NrMedia' => '7', + 'Projectwoning_KamersVan' => null, + 'Projectwoning_KamersTot' => null, + ], + ], + ]), + ]); + + $connector = new NieuwbouwOffice('test-token'); + $connector->withMockClient($mockClient); + + $unitType = $connector->send(new GetUnitTypeRequest('proj-1', '631c2a57a0ce5edc989c67d3661f4711'))->dto(); + + expect($unitType)->toBeInstanceOf(UnitType::class) + ->and($unitType->uuid)->toBe('631c2a57a0ce5edc989c67d3661f4711') + ->and($unitType->is_online)->toBeTrue() + ->and($unitType->title)->toBe('Rug-aan-rug woning') + ->and($unitType->kind)->toBe('Tussenwoning') + ->and($unitType->order)->toBe(1) + ->and($unitType->count)->toBe(22) + ->and($unitType->price_from)->toBe(317600) + ->and($unitType->price_to)->toBe(423300) + ->and($unitType->living_area_from)->toBe(79) + ->and($unitType->bedrooms_from)->toBe(2) + ->and($unitType->rooms_from)->toBeNull() + ->and($unitType->ownership)->toBe('Eigendom') + ->and($unitType->tenure)->toBe('Koopwoning') + ->and($unitType->home_count)->toBe(22) + ->and($unitType->media_count)->toBe(7); +}); diff --git a/tests/Requests/UnitTypes/GetUnitTypesRequestTest.php b/tests/Requests/UnitTypes/GetUnitTypesRequestTest.php index 566d471..619df87 100644 --- a/tests/Requests/UnitTypes/GetUnitTypesRequestTest.php +++ b/tests/Requests/UnitTypes/GetUnitTypesRequestTest.php @@ -1,7 +1,11 @@ getMethod())->toBe(Method::GET); @@ -15,3 +19,57 @@ expect((new GetUnitTypesRequest('proj-1'))->resolveEndpoint()) ->toBe('/projects/proj-1/projectwoningen/'); }); + +it('creates an array of UnitType DTOs from the response', function () { + $mockClient = new MockClient([ + GetUnitTypesRequest::class => MockResponse::make([ + 'data' => [ + 'objects' => [ + [ + 'Projectwoning_UUId' => 'dc28a597e2661a3bf7f84737eb1f38c9', + 'Projectwoning_Online' => '-1', + 'Projectwoning_Titel' => 'Gezinswoning M', + 'Woning_Type' => 'Tussenwoning', + 'Projectwoning_Volgorde' => '2', + 'Projectwoning_Beschrijving_Kort' => null, + 'NrWoningen' => '2', + 'NrMedia' => '6', + ], + [ + 'Projectwoning_UUId' => 'a8caa9dc5a353a938440361e8005d06d', + 'Projectwoning_Online' => '-1', + 'Projectwoning_Titel' => 'Appartement', + 'Woning_Type' => 'Appartement', + 'Projectwoning_Volgorde' => '5', + 'Projectwoning_Beschrijving_Kort' => null, + 'NrWoningen' => '14', + 'NrMedia' => '5', + ], + ], + ], + 'meta' => ['count' => 2], + ]), + ]); + + $connector = new NieuwbouwOffice('test-token'); + $connector->withMockClient($mockClient); + + $unitTypes = $connector->send(new GetUnitTypesRequest('proj-1'))->dto(); + + expect($unitTypes)->toBeArray()->toHaveCount(2) + ->and($unitTypes[0])->toBeInstanceOf(UnitType::class) + ->and($unitTypes[0]->uuid)->toBe('dc28a597e2661a3bf7f84737eb1f38c9') + ->and($unitTypes[0]->is_online)->toBeTrue() + ->and($unitTypes[0]->title)->toBe('Gezinswoning M') + ->and($unitTypes[0]->kind)->toBe('Tussenwoning') + ->and($unitTypes[0]->order)->toBe(2) + ->and($unitTypes[0]->home_count)->toBe(2) + ->and($unitTypes[0]->media_count)->toBe(6) + ->and($unitTypes[0]->price_from)->toBeNull() + ->and($unitTypes[1])->toBeInstanceOf(UnitType::class) + ->and($unitTypes[1]->uuid)->toBe('a8caa9dc5a353a938440361e8005d06d') + ->and($unitTypes[1]->title)->toBe('Appartement') + ->and($unitTypes[1]->kind)->toBe('Appartement') + ->and($unitTypes[1]->order)->toBe(5) + ->and($unitTypes[1]->home_count)->toBe(14); +}); diff --git a/tests/Resources/UnitTypeResourceTest.php b/tests/Resources/UnitTypeResourceTest.php index 6b8c56e..369e47b 100644 --- a/tests/Resources/UnitTypeResourceTest.php +++ b/tests/Resources/UnitTypeResourceTest.php @@ -1,35 +1,41 @@ MockResponse::make([ 'data' => [ - ['uuid' => 'u1', 'name' => 'Type A'], - ['uuid' => 'u2', 'name' => 'Type B'], + 'objects' => [ + [ + 'Projectwoning_UUId' => 'u1', + 'Projectwoning_Titel' => 'Type A', + ], + [ + 'Projectwoning_UUId' => 'u2', + 'Projectwoning_Titel' => 'Type B', + ], + ], ], + 'meta' => ['count' => 2], ]), ]); $connector = new NieuwbouwOffice('test-token'); $connector->withMockClient($mockClient); - $response = $connector->unitTypes('proj-1')->list(); + $unitTypes = $connector->unitTypes('proj-1')->list(); - expect($response)->toBeInstanceOf(Response::class) - ->and($response->json())->toBe([ - 'data' => [ - ['uuid' => 'u1', 'name' => 'Type A'], - ['uuid' => 'u2', 'name' => 'Type B'], - ], - ]); + expect($unitTypes)->toBeArray()->toHaveCount(2) + ->and($unitTypes[0])->toBeInstanceOf(UnitType::class) + ->and($unitTypes[0]->uuid)->toBe('u1') + ->and($unitTypes[1]->uuid)->toBe('u2'); $mockClient->assertSent(function ($request) { return $request instanceof GetUnitTypesRequest @@ -38,24 +44,26 @@ }); }); -it('get() sends a GetUnitTypeRequest with the right project uuid and uuid', function () { +it('get() sends a GetUnitTypeRequest with the right project uuid and uuid and returns a UnitType DTO', function () { $mockClient = new MockClient([ GetUnitTypeRequest::class => MockResponse::make([ - 'uuid' => 'unit-9', - 'name' => 'Type Z', + 'data' => [ + 'object' => [ + 'Projectwoning_UUId' => 'unit-9', + 'Projectwoning_Titel' => 'Type Z', + ], + ], ]), ]); $connector = new NieuwbouwOffice('test-token'); $connector->withMockClient($mockClient); - $response = $connector->unitTypes('proj-1')->get('unit-9'); + $unitType = $connector->unitTypes('proj-1')->get('unit-9'); - expect($response)->toBeInstanceOf(Response::class) - ->and($response->json())->toBe([ - 'uuid' => 'unit-9', - 'name' => 'Type Z', - ]); + expect($unitType)->toBeInstanceOf(UnitType::class) + ->and($unitType->uuid)->toBe('unit-9') + ->and($unitType->title)->toBe('Type Z'); $mockClient->assertSent(function ($request) { return $request instanceof GetUnitTypeRequest