Skip to content

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.json

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.json

Setup ​

1. Generate to Separate Directory ​

Use the --src-path option to output to generated/:

bash
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:

json
{
    "autoload": {
        "psr-4": {
            "App\\": "src/",
            "App\\Dto\\": "generated/Dto/"
        }
    }
}

Then regenerate the autoloader:

bash
composer dump-autoload

3. Add Composer Script ​

Add a convenient script for regeneration:

json
{
    "scripts": {
        "dto:generate": "dto generate --src-path=generated/",
        "dto:check": "dto generate --src-path=generated/ --dry-run"
    }
}

Now use:

bash
composer dto:generate

Static Analysis Configuration ​

By design, PHPCS, PHPStan and CO are all excluded already. So nothing to be configured here.

Git Configuration ​

Track generated files in version control for faster deployments:

gitignore
# .gitignore
# Don't ignore generated/ - we want to track these files

Benefits:

  • 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
# .gitignore
/generated/

Add generation to your deployment script:

bash
composer install
composer dto:generate

Custom Namespace ​

If you prefer a different namespace, adjust both CLI and Composer:

bash
# Generate with custom namespace
vendor/bin/dto generate --src-path=generated/ --namespace=Acme\\Transfer
json
{
    "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.json

Generate each module separately:

bash
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=Shipping

Composer autoload:

json
{
    "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:

bash
# 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 ​

yaml
# .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 ​

bash
#!/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
fi

Complete Example ​

Here's a complete composer.json setup:

json
{
    "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/",
    }
}

Released under the MIT License.