I'm trying to write cleaner Laravel code and recently started using DTOs. Here's a version of my store() method in ProductController:
public function store(Request $request)
{
$validatedData = $request->validate([
'name' => 'required|string|max:255',
'alloy' => 'required|string|max:50',
'density' => 'required|numeric|min:0',
'imperial_spec_sheet' => 'nullable|file|mimes:pdf|max:10240',
'metric_spec_sheet' => 'nullable|file|mimes:pdf|max:10240',
]);
if ($request->hasFile('imperial_spec_sheet'))
{
$validatedData['imperial_spec_sheet'] = $request->file('imperial_spec_sheet')->store('spec_sheets', 'public');
}
if ($request->hasFile('metric_spec_sheet'))
{
$validatedData['metric_spec_sheet'] = $request->file('metric_spec_sheet')->store('spec_sheets', 'public');
}
$dto = new ProductData(
$validatedData['name'],
$validatedData['alloy'],
$validatedData['density'],
$validatedData['imperial_spec_sheet'] ?? null,
$validatedData['metric_spec_sheet'] ?? null,
);
$product = $this->productService->store($dto);
return $this->success($product, 'Product created successfully');
}
It works well, but something feels off. If I add a new field to the products table, I have to update the validation rules,the DTO constructor and everywhere I populate data with the DTO structure (Like in my controller here). And if I reuse this logic elsewhere (say, in a CLI command or job), I have to re-duplicate the validation or extract it.
Is there a more conventional or scalable way to handle this kind of structure? I know I can use Form Requests for the validation, but then I’d have to extract the DTO creation logic too. Curious how others handle this cleanly.