svb/dto-generator-bundle
A Symfony bundle that generates typed DTO classes from your entities, arrays, or config, complete with constructors, getters, setters, and mapping helpers. A console command lets you scaffold and regenerate DTOs quickly, reducing boilerplate in your service and API layers.
Install and register. Require the bundle with Composer, then confirm registration. Symfony Flex auto-registers bundles whose recipes exist; with 0 stars and no dependents there is likely no Flex recipe, so add it manually to config/bundles.php:
SVB\DtoGeneratorBundle\SvbDtoGeneratorBundle::class => ['all' => true],
Find the actual entry points first. Do not guess the API from the README name. Run:
php bin/console list | grep -i dto
php bin/console debug:config svb_dto_generator # adjust the alias to whatever debug:config suggests
php bin/console config:dump-reference <alias>
The command list and the dumped config reference are the authoritative docs for this package — treat them as the source of truth over any blog post.
Publish/inspect config. If the bundle ships a recipe-less default, dump the reference and place a minimal config in config/packages/. The two settings that almost always matter for a DTO generator are the output namespace and the target directory (e.g. App\Dto → src/Dto). Set those before generating anything so you never have to move files by hand.
First use case: kill a hand-written array shape. Pick one controller or message handler where you currently return array or pass array $data around. Generate a DTO for that payload, wire it in, and stop. This gives you a feel for the generated style (readonly properties? constructor promotion? attributes?) on a low-risk change before you roll it across the codebase.
Confirm autoloading. Generated files land under src/, which is already PSR-4 mapped, so a composer dump-autoload is usually unnecessary — but run it once after your first generation to rule out namespace mismatches.
Generate, then own the code. A generator bundle's output is a scaffold, not a managed artifact. The healthiest pattern is: generate once into src/Dto, commit the result, and thereafter edit the DTO by hand. Reserve regeneration for new DTOs or throwaway experiments. If you find yourself re-running the command over existing files every sprint, you are fighting the tool — either the templates need customising or you should stop using the generator for that class of DTO.
Match the generator to a source of truth. DTO generators are most valuable when they derive from something already defined: a Doctrine entity, a JSON Schema, an OpenAPI spec, or a form. A DTO generated from an entity gives you a typed, framework-free projection for your API layer. A DTO generated from a schema gives you contract-first request objects. Wire the generator into the workflow at the point where the source changes, not as a separate chore — e.g. after adding an entity field, regenerate or hand-extend the corresponding DTO in the same PR.
Layer the DTO between transport and domain. The day-to-day payoff is a hard boundary:
final class CreateOrderRequest
{
public function __construct(
public readonly string $customerId,
public readonly array $lines,
) {}
}
Controller deserialises into the DTO (Symfony's #[MapRequestPayload] or a serialiser), the handler takes the DTO, the domain entity is built inside. Once this shape is habitual, generated DTOs slot in naturally — the generator just saves you typing the constructor.
Customise templates instead of post-editing. If the bundle exposes template overrides via config or a templates/ directory (check config:dump-reference), copy the template into your app and adjust it once: add readonly, add your validation attributes, change the namespace style. Every subsequent generation then produces code that matches your house style, which is the difference between a generator you keep and one you delete after a week.
Pair with validation and serialisation attributes deliberately. Decide once whether DTOs carry #[Assert\*] and #[Groups] attributes, or whether validation lives in form requests / serialiser config. If the generator emits attributes you do not use, strip them in the template — noise in generated code trains reviewers to skim.
Batch generation for bulk migrations. When introducing DTOs to a legacy app, generate a whole directory in one pass, then review the diff as a single unit. Committing generated files in one "scaffold DTOs" commit keeps later hand-edits readable in history.
composer.json and avoid ^ ranges that could pull breaking template changes.bin/console list rather than copying from the README, which may target an older Symfony version..bak files. Test on a scratch DTO before running against a directory of hand-edited files. Commit or stash first — this is the single most common way to lose an afternoon.App\Dto to App\DataTransfer in config later, previously generated files keep the old namespace. Generators rarely migrate existing files. Decide the namespace on day one.php bin/console cache:clear — a stale container is the usual cause of "my config change did nothing."composer dump-autoload. Check composer dump-autoload -o output for classmap warnings.src/ — for a generator this small that is often cheaper than lobbying upstream.composer update far faster than reading changelogs — which, for a bundle this size, may not exist.How can I help you explore Laravel packages today?