Skip to content

Conversation

mabdusshakur
Copy link

Add --dry-run Flag to Artisan Generator Commands

Description

This PR adds a --dry-run flag to all Laravel Artisan generator commands, allowing developers to preview the operations that would be performed without actually creating any files or making any changes to the filesystem.

This feature follows the industry-standard preview approach (not make-and-revert) used by popular tools like NestJS CLI, npm.. etc.

Motivation

Currently, when developers run Artisan generator commands (e.g., php artisan make:model Post -mfsc), files are created immediately without any opportunity to preview what will be generated. This can lead to:

  • Accidental file creation in wrong locations
  • Trial-and-error workflow requiring manual cleanup
  • Difficulty in learning what different command flags do
  • No way to validate command behavior in CI/CD pipelines
  • Hard to document what commands will generate

The --dry-run flag solves these problems by providing a safe preview mode.

Changes

Core Implementation

  1. New DryRunnable Trait (src/Illuminate/Console/Concerns/DryRunnable.php)

    • Provides reusable dry-run functionality for any command
    • Methods:
      • configureDryRun() - Adds the --dry-run option to a command
      • isDryRun() - Checks if command is running in dry-run mode
      • recordDryRunOperation() - Records operations that would be performed
      • displayDryRunOperations() - Displays formatted preview output
      • clearDryRunOperations() - Clears recorded operations
      • getDryRunOperations() - Returns all recorded operations
  2. GeneratorCommand Integration (src/Illuminate/Console/GeneratorCommand.php)

    • Uses DryRunnable trait
    • Modified handle() method to check for dry-run mode
    • New handleDryRun() method for preview logic
    • New handleTestCreationDryRun() method for test file previews
    • Shows file paths, namespaces, classes, and file sizes
    • Optional verbose mode (-v) shows actual file content preview
  3. ModelMakeCommand Integration (src/Illuminate/Foundation/Console/ModelMakeCommand.php)

    • Propagates --dry-run flag to all sub-commands:
      • make:factory
      • make:migration
      • make:seeder
      • make:controller
      • make:request (Store and Update)
      • make:policy
  4. ControllerMakeCommand Integration (src/Illuminate/Routing/Console/ControllerMakeCommand.php)

    • Propagates --dry-run flag when generating related models and requests
  5. MigrateMakeCommand Integration (src/Illuminate/Database/Console/Migrations/MigrateMakeCommand.php)

    • Uses DryRunnable trait
    • Shows migration file details including table name and operation type
  6. MigrateCommand Integration (src/Illuminate/Database/Console/Migrations/MigrateCommand.php)

    • Uses DryRunnable trait
    • --dry-run works as an alias for existing --pretend flag

Commands Supporting Dry-Run

All generator commands that extend GeneratorCommand now support --dry-run:

  • make:model, make:controller, make:migration, make:seeder
  • make:factory, make:policy, make:request, make:resource
  • make:rule, make:event, make:listener, make:job
  • make:mail, make:notification, make:middleware, make:provider
  • make:test, make:command, make:cast, make:channel
  • make:component, make:exception, make:observer, make:scope

Plus: migrate (shows SQL queries via existing --pretend functionality)

Usage Examples

Basic Usage

# Preview creating a model
php artisan make:model Post --dry-run

Output:

⚠ DRY RUN MODE - No changes will be made

ℹ The following 1 operation(s) would be performed:

[1] CREATE ................................................... Would create Model
    └─ Path: /app/Models/Post.php
    └─ Directory: /app/Models
    └─ Namespace: App\Models
    └─ Class: Post
    └─ Size: 342 bytes

ℹ Run the command without --dry-run to execute these operations.

With Multiple Flags

# Preview model with migration, factory, and seeder
php artisan make:model Post -mfs --dry-run

Each sub-command (model, migration, factory, seeder) displays its own dry-run output.

With Verbose Output

# Show file content preview
php artisan make:model Post --dry-run -v

Shows the actual stub content that would be generated.

Existing Files

# When file already exists
php artisan make:model User --dry-run

Shows a "SKIP" operation indicating the file already exists.

Technical Details

Design Decisions

  1. Preview-Only Approach: No files are created or modified in dry-run mode. This is safer and more performant than make-and-revert approaches.

  2. Flag Propagation: When make:model Post -m --dry-run is called, the --dry-run flag is automatically passed to the make:migration sub-command.

  3. Detailed Output: Shows all relevant information (paths, namespaces, classes, file sizes) to help developers understand what will be created.

  4. Consistent Formatting: Uses Laravel's existing component styling for consistent output across all commands.

  5. Non-Invasive: Existing commands continue to work exactly as before. The trait is only activated when the --dry-run flag is used.

Code Quality

  • PSR-2 Compliant: All code follows Laravel's PSR-2 coding standards
  • Native Types: Uses native PHP type hints (string, array, void, bool)
  • PHPDoc: Proper documentation with generic types where applicable (e.g., @return array<int, array{type: string, description: string, details: array}>)
  • Backwards Compatible: No breaking changes to existing functionality
  • Tested: Comprehensive unit and integration tests included

Testing

Test Coverage

  1. DryRunnableTest.php - Unit tests for the DryRunnable trait

    • Tests option configuration
    • Tests [isDryRun()] detection (both true and false cases)
    • Tests operation recording
    • Tests operation clearing
    • Tests formatted output display
  2. DryRunCommandTest.php - Integration tests for generator commands

    • Tests that files are NOT created in dry-run mode
    • Tests flag propagation to sub-commands (-m flag)
    • Tests output formatting and operation details
    • Tests migration command dry-run support

Running Tests

# Run all dry-run tests
php vendor/bin/phpunit tests/Console/Concerns/DryRunnableTest.php
php vendor/bin/phpunit tests/Integration/Console/DryRunCommandTest.php

# Run specific test
php vendor/bin/phpunit --filter testMakeModelWithDryRunDoesNotCreateFiles

Benefits

For Developers

  • ✅ Preview changes before execution
  • ✅ Avoid mistakes and accidental file creation
  • ✅ Learn what commands do without side effects
  • ✅ Validate command behavior in CI/CD
  • ✅ Generate documentation from command output

For Teams

  • ✅ Include dry-run output in PRs
  • ✅ Help onboarding with safe experimentation
  • ✅ Ensure consistent file generation
  • ✅ Test command behavior without cleanup

For Laravel

  • ✅ Modern developer experience
  • ✅ Industry-standard feature (NestJS, Symfony, etc.)
  • ✅ Improved command usability
  • ✅ Better developer tooling

Comparison with Other Frameworks

NestJS

nest generate module users --dry-run

Laravel (This PR)

php artisan make:model User --dry-run

Breaking Changes

None. This is a purely additive feature with no impact on existing functionality.

Future Enhancements

Potential future additions (not in this PR):

  • Add dry-run to cache commands (config:cache, route:cache, etc.)
  • JSON output format for programmatic use
  • Interactive mode with selective execution
  • Detailed diff view for existing files

Checklist

  • Code follows PSR-2 standards
  • Native types used where appropriate
  • PHPDoc blocks are accurate
  • No breaking changes
  • Unit tests added
  • Integration tests added
  • All tests pass
  • Feature is backwards compatible
  • Code is well-documented

Screenshots

image

Additional Notes

This implementation is production-ready and has been tested with various command combinations. The DryRunnable trait is designed to be easily added to any custom Artisan command, making it extensible for package developers and application-specific commands.


Note to Maintainers: This PR implements a frequently requested feature that brings Laravel in line with other modern CLI tools. The implementation is non-invasive, well-tested, and follows all Laravel coding standards.

@taylorotwell
Copy link
Member

Thanks for your pull request to Laravel!

Unfortunately, I'm going to delay merging this code for now. To preserve our ability to adequately maintain the framework, we need to be very careful regarding the amount of code we include.

If applicable, please consider releasing your code as a package so that the community can still take advantage of your contributions!

@mabdusshakur
Copy link
Author

Thanks for reviewing my pull request and for the feedback!
I appreciate your commitment to keeping the framework maintainable.
I’ll look into releasing this feature as a package for community use.
If you have any further suggestions or recommendations, I’d be glad to hear them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants