Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Command Validator Laravel Package

cerbero/command-validator

Cerbero Command Validator brings Laravel-style validation to Artisan console commands. Define rules for arguments and options, validate input before your command runs, and show clear errors—making CLI tools safer and easier to maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package with Composer:

composer require cerbero/command-validator

Add the ValidatesInput trait to any Artisan command and declare a rules() method. Rule keys are the bare argument and option names from $signature, without braces, dashes, or =:

use Cerbero\CommandValidator\ValidatesInput;
use Illuminate\Console\Command;

class Deploy extends Command
{
    use ValidatesInput;

    protected $signature = 'app:deploy {environment} {--branch=} {--force}';

    protected $description = 'Deploy the application';

    protected function rules(): array
    {
        return [
            'environment' => ['required', 'string', 'in:staging,production'],
            'branch' => ['nullable', 'string', 'max:255'],
            'force' => ['boolean'],
        ];
    }

    public function handle(): int
    {
        // Only reached when validation passes.
        return self::SUCCESS;
    }
}

Look first at the package README and src/ValidatesInput.php to confirm the exact hook methods supported by your installed version. Then inspect the rules() method in one of your commands and run it manually:

php artisan app:deploy invalid-environment

The first practical use case is guarding a command that performs an irreversible side effect — deployments, imports, external API calls, shell commands — so malformed arguments and options fail before handle() runs.

Implementation Patterns

Basic argument and option validation. Treat rules() like a FormRequest rules array. Arguments map directly to their names; options map to the option name without leading dashes:

protected function rules(): array
{
    return [
        'path' => ['required', 'string'],
        'format' => ['required', 'string', 'in:csv,json,xml'],
        'chunk' => ['nullable', 'integer', 'min:1', 'max:10000'],
        'dry-run' => ['boolean'],
    ];
}

Conditional rules. Use Laravel’s Rule objects and closures for rules that depend on other input, rather than branching in handle():

use Illuminate\Validation\Rule;

protected function rules(): array
{
    return [
        'environment' => ['required', Rule::in(['staging', 'production'])],
        'confirm' => [
            Rule::requiredIf(fn () => $this->argument('environment') === 'production'),
            'boolean',
        ],
        'branch' => ['nullable', 'string', 'required_with:tag'],
    ];
}

Custom messages and attributes. Override messages() and attributes() to make console errors readable, especially when option names contain hyphens:

protected function messages(): array
{
    return [
        'environment.in' => 'The environment must be staging or production.',
        'confirm.required' => 'Production deployments require --confirm.',
    ];
}

protected function attributes(): array
{
    return [
        'dry-run' => 'dry run mode',
        'branch' => 'git branch',
    ];
}

Array and repeatable options. For $signature entries like {--tag=*}, validate the option as an array and use wildcard rules:

protected $signature = 'app:tag {--tag=*}';

protected function rules(): array
{
    return [
        'tag' => ['array'],
        'tag.*' => ['string', 'max:50'],
    ];
}

Shared validation across commands. Move common rules into a base command class or a reusable trait. This is useful for a family of commands that all accept the same connection, tenant, or environment options:

trait ValidatesTenantInput
{
    protected function tenantRules(): array
    {
        return [
            'tenant' => ['required', 'string', 'exists:tenants,slug'],
            'connection' => ['nullable', 'string'],
        ];
    }
}

Then merge tenantRules() into each command’s rules().

Validation in orchestrated calls. Commands invoked from other commands or from Artisan::call() still validate. This means a parent command can rely on the child command rejecting bad input, but it should check the returned exit code:

$exitCode = Artisan::call('app:import', ['file' => $path]);

if ($exitCode !== 0) {
    $this->error('Import failed validation or execution.');
}

Testing validated commands. Use Laravel’s command assertions. Since validation failure prevents handle() from running, assert both the exit code and any absence of side effects:

$this->artisan('app:deploy', ['environment' => 'invalid'])
    ->expectsOutputToContain('environment')
    ->assertFailed();

$this->artisan('app:deploy', ['environment' => 'staging'])
    ->assertSuccessful();

For commands with external dependencies, bind fakes in the container and assert they were not called when validation fails.

Gotchas and Tips

  • Use bare names as keys. {environment} is 'environment', --branch= is 'branch', and --dry-run is 'dry-run'. Including braces, dashes, or = causes the rule to be ignored because the key does not match the input name.

  • Option types are not what you expect. A value option such as {--limit=10} arrives as a string, while a boolean flag such as {--force} arrives as true/false. Use integer/boolean rules accordingly and normalize in handle() after validation.

  • required with defaults. An option with a default is not null, so required may pass even when the user did not explicitly provide it. Use required only when the absence of the option should fail; otherwise use nullable or sometimes.

  • Wildcard keys require the option to be an array. tag.* rules only apply when {--tag=*} is declared. A plain --tag= option with tag.* rules will not behave as intended.

  • handle() is skipped on failure. Validation failures abort before your command logic. This is the point of the package, but it means cleanup code in handle() will not run, and failures surface as a non-zero exit code rather than a thrown exception. Assert exit codes in CI and tests.

  • Nested command calls propagate exit codes. When calling a validated command from another command or from a queue worker, check the return value. Do not assume an exception is thrown for invalid input.

  • Conflict with execute() overrides. The trait intercepts command execution to validate input. If you override execute() or rely on custom command lifecycle hooks, verify validation still runs, and prefer handle() for command logic.

  • Normalize before validating when needed. There is no full FormRequest-style pipeline guarantee across package versions. If you need to trim strings, cast values, or merge defaults, use prepareForValidation only if your installed version supports it; otherwise normalize inside rule closures or before dispatching the command.

  • Console output is the primary error channel. Users see validation messages in the terminal, not an HTTP-style JSON response. Keep messages() concise and include the option name, because users may not know whether branch came from an argument or an option.

  • Debugging mismatched keys. When validation appears to do nothing, dump the actual input names:

    dump($this->input->getArguments(), $this->input->getOptions());
    

    Compare those keys against rules(). A typo in either place silently disables that rule.

  • CI and non-interactive runs. In CI, always pass --no-interaction and explicit options. Do not rely on interactive prompts to supply values that validation expects.

  • Package maturity and maintenance. This is a small, focused package with a low dependent count. Pin the version in composer.json, keep a smoke test for each validated command, and be prepared to replace the trait with a small custom execute() wrapper or a manual Validator::make() call if the package becomes incompatible with a future Laravel release.

  • Extension points are limited to the command class. The natural customization points are rules(), messages(), and attributes(). For more complex workflows, compose Laravel Rule objects and custom rule classes rather than expecting the package to provide a full validation pipeline.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console