Predefined DTOs
The package ships a few ready-made Data objects for common Laravel scenarios.
These live under BradieTilley\Data\Predefined and require Laravel.
ApiResponse
A consistent JSON envelope for API responses. It extends
Illuminate\Http\JsonResponse, so you can return it directly from a controller.
The envelope shape is:
{
"status": 200,
"success": true,
"message": "...",
"data": { },
"errors": [ ]
}
Success responses
use BradieTilley\Data\Predefined\Api\ApiResponse;
return ApiResponse::success('Product loaded', ProductData::from($product));
return ApiResponse::created('Product created', ProductData::from($product)); // 201
->data(...) attaches or replaces the payload fluently:
return ApiResponse::success('Loaded')->data(ProductData::from($product));
Error responses
return ApiResponse::error('Something went wrong'); // 400
return ApiResponse::unauthorized('Not logged in'); // 401
return ApiResponse::forbidden('Not allowed'); // 403
return ApiResponse::notFound('Product not found'); // 404
return ApiResponse::invalid('Validation failed', $errors); // 422
return ApiResponse::conflict('Resource changed'); // 409
Errors accept a field-keyed array; each entry becomes a structured error object:
ApiResponse::invalid('Failed to create product', [
'title' => ['Must be at least 3 characters long'],
]);
// errors => [ ['message' => 'Must be at least 3 characters long', 'field' => 'title'] ]
Pass a Throwable to derive the message (and attach the exception to the
response) automatically:
ApiResponse::error($exception);
From a validator
public function store(Request $request)
{
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
return ApiResponse::validator($validator); // 422 with the error bag
}
// ...
}
Paginator DTOs
LengthAwarePaginatorData and CursorPaginatorData wrap Laravel's paginators into
Data objects. Use Data::paginate() to build one from a paginator instance —
items are hydrated to the calling Data class:
$page = ProductData::paginate($products); // $products is a Laravel paginator
LengthAwarePaginatorData
Maps a LengthAwarePaginator. Serialized shape:
| Property | Source |
|---|---|
total | $paginator->total() |
lastPage | $paginator->lastPage() |
perPage | $paginator->perPage() |
currentPage | $paginator->currentPage() |
items | $paginator->items(), each cast via ProductData::from() when using paginate() |
use BradieTilley\Data\Predefined\LengthAwarePaginatorData;
/** @var LengthAwarePaginatorData $page */
$page = ProductData::paginate(Product::paginate(15));
$page->toArray();
// [
// 'total' => 42,
// 'lastPage' => 3,
// 'perPage' => 15,
// 'currentPage' => 1,
// 'items' => [ /* ProductData::toArray() for each item */ ],
// ]
The class is marked #[Template('T')] for tooling that understands generic item
types.
CursorPaginatorData
Maps a CursorPaginator. Serialized shape:
| Property | Source |
|---|---|
hasMore | $paginator->hasMorePages() |
perPage | $paginator->perPage() |
cursor | $paginator->cursor()?->toArray() ?? [] |
items | $paginator->items(), each cast when using paginate() |
$page = ProductData::paginate(Product::cursorPaginate(15));
$page->toArray();
// [
// 'hasMore' => true,
// 'perPage' => 15,
// 'cursor' => [ /* cursor payload */ ],
// 'items' => [ /* ProductData::toArray() for each item */ ],
// ]
You can also construct the wrappers directly with an optional item type:
use BradieTilley\Data\Predefined\LengthAwarePaginatorData;
new LengthAwarePaginatorData($paginator, ProductData::class);