Separating Generated Code
Best practices for keeping generated DTOs separate from hand-written source code.
Why Separate Generated Code?
Separating generated code from your source directory offers several benefits:
- Clearer code ownership - Distinguishes auto-generated files from hand-written code
- Easier code review - Reviewers know generated files don't need manual inspection
- Simpler static analysis - Exclude entire directory from phpcs/phpstan
- Git history clarity - Generated file changes are clearly identifiable
- IDE performance - Some IDEs handle generated code differently
Directory Structure
Default Structure
By default, DTOs are generated in src/Dto/:
project/
├── config/
│ └── dto.xml
├── src/
│ ├── Dto/ # Generated DTOs mixed with source
│ │ ├── UserDto.php
│ │ └── OrderDto.php
│ ├── Controller/
│ └── Service/
└── composer.jsonRecommended Structure
Move generated DTOs to a separate generated/ directory:
project/
├── config/
│ └── dto.xml
├── generated/
│ └── Dto/ # Generated DTOs isolated
│ ├── UserDto.php
│ └── OrderDto.php
├── src/
│ ├── Controller/ # Hand-written code only
│ └── Service/
└── composer.jsonSetup
1. Generate to Separate Directory
Use the --src-path option to output to generated/:
vendor/bin/dto generate --src-path=generated/This creates DTOs in generated/Dto/ with namespace App\Dto.
2. Configure Composer Autoloading
Add the generated directory to your composer.json autoload configuration:
{
"autoload": {
"psr-4": {
"App\\": "src/",
"App\\Dto\\": "generated/Dto/"
}
}
}Then regenerate the autoloader:
composer dump-autoload3. Add Composer Script
Add a convenient script for regeneration:
{
"scripts": {
"dto:generate": "dto generate --src-path=generated/",
"dto:check": "dto generate --src-path=generated/ --dry-run"
}
}Now use:
composer dto:generateStatic Analysis Configuration
By design, PHPCS, PHPStan and CO are all excluded already. So nothing to be configured here.
Git Configuration
Option A: Commit Generated Files (Recommended)
Track generated files in version control for faster deployments:
# .gitignore
# Don't ignore generated/ - we want to track these filesBenefits:
- No generation step needed during deployment
- Changes are visible in pull requests
- Works with read-only production filesystems
Option B: Ignore Generated Files
If you prefer runtime generation:
# .gitignore
/generated/Add generation to your deployment script:
composer install
composer dto:generateCustom Namespace
If you prefer a different namespace, adjust both CLI and Composer:
# Generate with custom namespace
vendor/bin/dto generate --src-path=generated/ --namespace=Acme\\Transfer{
"autoload": {
"psr-4": {
"Acme\\Transfer\\": "generated/Dto/"
}
}
}Multiple Applications
For monorepos or multi-module projects:
project/
├── modules/
│ ├── billing/
│ │ ├── config/dto.xml
│ │ └── generated/Dto/
│ └── shipping/
│ ├── config/dto.xml
│ └── generated/Dto/
└── composer.jsonGenerate each module separately:
vendor/bin/dto generate \
--config-path=modules/billing/config/ \
--src-path=modules/billing/generated/ \
--namespace=Billing
vendor/bin/dto generate \
--config-path=modules/shipping/config/ \
--src-path=modules/shipping/generated/ \
--namespace=ShippingComposer autoload:
{
"autoload": {
"psr-4": {
"Billing\\Dto\\": "modules/billing/generated/Dto/",
"Shipping\\Dto\\": "modules/shipping/generated/Dto/"
}
}
}TypeScript and JSON Schema
The same pattern applies to other generators:
# TypeScript to separate directory
vendor/bin/dto typescript --output=generated/types/
# JSON Schema to separate directory
vendor/bin/dto jsonschema --output=generated/schemas/Project structure:
project/
├── config/
│ └── dto.xml
├── generated/
│ ├── Dto/ # PHP DTOs
│ ├── types/ # TypeScript interfaces
│ └── schemas/ # JSON Schema files
└── src/CI/CD Integration
GitHub Actions
# .github/workflows/ci.yml
jobs:
check-dtos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- run: composer install
# Verify DTOs are up to date
- run: composer dto:check
# Or regenerate and check for diff
- run: |
composer dto:generate
git diff --exit-code generated/Pre-commit Hook
#!/bin/sh
# .git/hooks/pre-commit
# Check if DTO config changed
if git diff --cached --name-only | grep -q "config/dto"; then
vendor/bin/dto generate --src-path=generated/ --dry-run --quiet
if [ $? -ne 0 ]; then
echo "DTO configuration changed. Run 'composer dto:generate' and commit the generated files."
exit 1
fi
fiComplete Example
Here's a complete composer.json setup:
{
"name": "acme/my-project",
"autoload": {
"psr-4": {
"App\\": "src/",
"App\\Dto\\": "generated/Dto/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Test\\": "tests/"
}
},
"require": {
"php": ">=8.1",
"php-collective/dto": "^1.0"
},
"scripts": {
"dto:generate": "dto generate --src-path=generated/",
"dto:check": "dto generate --src-path=generated/ --dry-run",
"dto:typescript": "dto typescript --output=generated/types/",
"dto:schema": "dto jsonschema --output=generated/schemas/",
}
}