ServiceProvider::optimizes(), you can hook your generation and clearing commands into Laravel’s optimize and optimize:clear.
This page assumes you are familiar with the package development basics and examines the implementation in Laravel Framework v13.35.0. It covers the contract for registration and operation, not the format of cache files.
Separate command registration from task registration
commands() registers command classes that can be called from Artisan. optimizes() is a separate step that registers the names of already executable commands as optimization tasks. Calling only the latter does not register the command classes.
The following example assumes your package already implements CacheMetadataCommand and ClearMetadataCommand, with $signature values of courier:cache and courier:clear-cache respectively.
optimizes() are nullable, so you can register only generation or only clearing. However, always tell your users which step invalidates the cache you generate.
The registration key is also a user-facing contract
ServiceProvider stores generation commands in the static array $optimizeCommands and clearing commands in $optimizeClearCommands. In both cases, key becomes the array key.
If you omit key, a name is derived from the provider’s class name. For example, CourierServiceProvider becomes courier. Because only the class name is used, providers with the same name in different namespaces can collide.
If you register again under the same key, the command for that side is overwritten by the later value. To register multiple tasks, specify a different key for each. Also avoid the keys of Laravel’s built-in tasks, such as config and routes, because identical string keys are overwritten when the built-in tasks and package tasks are combined.
Package tasks run after the built-in tasks
In the implementation examined here, both commands spread the package registration array into the built-in task array and then call each task in order. As long as you use a non-colliding key, package tasks are appended after the built-in tasks.
Given this order, your generation command should only generate data your package owns, rather than rebuilding other built-in caches. Don’t use
optimizes() as an API for controlling dependency order between multiple packages. If you need a strict order, list the dedicated commands explicitly.
Exclude tasks by key or command name
The--except option of both commands accepts comma-separated values. Leading and trailing whitespace is trimmed from each value, and any task whose key or command name matches is excluded.
cache:clear. cache is a task key, not a package-specific name.
Exclusions apply only to that run. They are not a setting that disables the provider’s registration or automatically deletes package caches created earlier.
Distinguish task FAIL from the parent command’s exit code
OptimizeCommand and OptimizeClearCommand call each task with callSilently() and pass whether the exit code is 0 to the task display. Because normal child command output is not shown, run the dedicated command directly to investigate the cause.
In Laravel v13.35.0, neither handle() method returns a child command’s non-zero exit code as the parent’s return value. Even if FAIL appears on screen, the loop continues, and if no exception is thrown, the parent command exits with 0. Thrown exceptions, on the other hand, are rethrown by the task display component, so they don’t follow the same continue-on-failure behavior.
If package generation is a mandatory deployment requirement, use a procedure that lets you check the child command’s exit code directly. For example, the following excludes the package task from the batch run and runs it directly once after the built-in tasks.
courier:cache failure in the exit code, but it does not aggregate non-zero exits from built-in tasks. For deployments that must strictly detect built-in task failures too, run the required commands individually and check their exit codes.
Design and verify caches that survive updates
Beyond registration, decide the responsibilities of your commands and the code that reads the cache.- Generation produces the same state from the same input no matter how many times it runs, and never activates incomplete data after a partial failure.
- Clearing completes successfully even when no cache exists, and never deletes the user’s published config or persistent data.
- A command that fails to generate reports an error and returns non-zero. The reading side also never treats a corrupted cache as valid unconditionally.
- When you change the cache format, tell users they need to regenerate it, and consider restarting long-running processes.
Related pages
Package config merging and caching
Review how filling in published config relates to rebuilding the config cache.
Testing Laravel packages with Orchestra Testbench
Register providers and Artisan commands in your test environment.