Skip to main content

Grenke DTOs

app/DataTransferObjects/Grenke/

Alle Daten die zwischen Controller, Action, Service und Grenke-API hin- und herfließen, gehen durch readonly DTOs. Externer Wire-Format ist PascalCase, internes PHP ist camelCase — die DTOs sind der einzige Ort, an dem konvertiert wird.

Übersicht (13 DTOs)

DTOVerwendung
RequestPayloadDataRequest-Body für POST /requests
RequestResponseDataResponse von GET/POST/PATCH /requests/...
PatchRequestPayloadDataRequest-Body für PATCH /requests/{id}
PatchLesseePayloadDataRequest-Body für PATCH /requests/{id}/lessee
LesseeDataenthält addresses[], telephones[], legalForm, …
AddressDataAdress-Datensatz (line1, postCode, city, country, type)
TelephoneDataTelefon (number, type=Phone/Mobile/Fax)
LegalFormDataRechtsform
ThirdPartyIdentifierDataexterne IDs (Steuer-Nr, Handelsregister, …)
FinancingObjectDataLeasing-Objekt (Quantity, ObjectTypeId, Manufacturer, …)
ESignaturePayloadDataRequest-Body für POST .../e-signature
ESignatureConfigurationDataResponse von GET .../e-signature/configuration
ESignatureSigneeDataenthalten in ESignaturePayloadData.signees[]

Anatomie

Jede DTO folgt demselben Muster:

namespace App\DataTransferObjects\Grenke;

class RequestPayloadData
{
public function __construct(
public readonly string $financingId,
public readonly string $productType,
public readonly float $financingAmount,
public readonly LesseeData $lessee,
public readonly array $financingObjects, // FinancingObjectData[]
// … 16 weitere Properties
) {}

public static function fromArray(array $data): self
{
return new self(
financingId: (string) ($data['FinancingId'] ?? ''),
productType: (string) ($data['ProductType'] ?? ''),
financingAmount: (float) ($data['FinancingAmount'] ?? 0),
lessee: LesseeData::fromArray($data['Lessee'] ?? []),
financingObjects: array_map(
fn (array $i) => FinancingObjectData::fromArray($i),
$data['FinancingObjects'] ?? []
),
);
}

public function toArray(): array
{
return [
'FinancingId' => $this->financingId,
'ProductType' => $this->productType,
'FinancingAmount' => $this->financingAmount,
'Lessee' => $this->lessee->toArray(),
'FinancingObjects' => array_map(
fn (FinancingObjectData $i) => $i->toArray(),
$this->financingObjects
),
];
}
}

Verschachtelung

Komplexe DTOs verschachteln andere DTOs:

Beim toArray() werden die verschachtelten DTOs rekursiv aufgelöst — keine Sorge.

Beispiel — kompletter Request

$payload = RequestPayloadData::fromArray([
'FinancingAmount' => 33877.56,
'Period' => 84,
'PaymentFrequency'=> 'Monthly',
'PaymentMethod' => 'DirectDebit',
'Currency' => 'EUR',
'ProductType' => 'ClassicLease',
'HasRepurchase' => true,
'Lessee' => [
'CompanyName' => 'Dr. Bettina Brem & Katrin Schimmelpfennig',
'ExternalId' => 'K562983',
'Email' => 'info@example.com',
'Addresses' => [[
'Line1' => 'Truderinger Str. 330',
'PostCode' => '81825',
'City' => 'München',
'Country' => 'DE',
'Type' => 'main',
]],
'Telephones' => [
['Number' => '+49 89 890566800', 'Type' => 'Phone'],
['Number' => '+49 89 890566808', 'Type' => 'Fax'],
],
'LegalFormId' => 0,
],
'FinancingObjects' => [[
'Quantity' => 2,
'ObjectTypeId' => 13,
'Name' => 'Human Medical Equipment',
'Manufacturer' => 'BLUE SAFETY',
'Details' => 'SAFEWATER 4.2',
'NetPricePerObject'=> 16938.78,
]],
]);

$response = $this->grenkeApiService->postJson('requests', $payload->toArray());
$result = RequestResponseData::fromArray($response);

Eigene DTO schreiben

  1. Datei anlegen: app/DataTransferObjects/Grenke/<Name>Data.php
  2. Konstruktor: ausschließlich public readonly Properties
  3. fromArray(array $data): self mit Defensive-Defaults (?? null, ?? '', ?? 0)
  4. toArray(): array mit dem PascalCase-Wire-Format
  5. Verschachtelte DTOs ebenfalls mit fromArray()/toArray() einbinden

Tipp: vor allem für Response-DTOs ist ?? null wichtig, weil Grenke je nach Status nicht alle Felder mitschickt.