Back

Local overrides

🧭

Is this package for you?

17 reasons, each a before and an after. Select the problems you have and see how many match.

Take the scorecard
πŸ”€

Your machine is not everyone's machine

Putting settings in version control solves the review problem and creates a new one: the file is now shared. You want a longer timeout while debugging, or a local Ollama instead of the hosted model β€” without editing a file your teammates pull.

Drop a subclass in app/Settings/Overrides/, gitignore it, and it is yours alone.

Three steps

1

Subclass the settings class you want to change

The file name must match the class it overrides β€” Overrides/AiSettings.php overrides AiSettings.

// app/Settings/Overrides/AiSettings.php
namespace App\Settings\Overrides;

use App\Settings\AiSettings as BaseAiSettings;

class AiSettings extends BaseAiSettings
{
    public static function production(): static
    {
        return new static(
            provider: 'ollama',
            text_model: 'my-local-llama',
            embeddings_model: 'nomic-embed-text',
            max_tokens: 512,
            temperature: 0.7,
        );
    }
}
2

Keep it out of the repository

The whole point is that nobody else gets it.

# .gitignore
/app/Settings/Overrides
3

Turn it on for your machine only

The flag lives in .env, which is already per-machine.

# .env
ENV_SETTINGS_OVERRIDE=true

Without step 3 the file is inert. An override sitting on disk with the flag off changes nothing β€” verified: the demo still resolved gpt-4o until ENV_SETTINGS_OVERRIDE=true was set.

What the override changes β€” and what it does not

Real output, captured with the override above active in this demo under APP_ENV=production.

Reading it through text_model Why
php artisan env-settings:show my-local-llama Resolves through the container, so the override wins.
envSettings(AiSettings::class) my-local-llama Your application gets the override β€” that is the point.
env-settings:show --all gpt-4o Calls the factories directly, so it shows the committed class.
env-settings:diff gpt-4o Same β€” it compares what the class declares, not your machine.
πŸ”Ž

That split is deliberate, and it is the useful part

show resolves through the container, so it tells you what your application is running. show --all and diff call the factories directly, so they tell you what the committed class declares. One answers “what am I running?”, the other “what will my teammates get?” β€” and you need both when a bug only reproduces on your machine.

Four rules worth knowing

🧩

Override only what you need

The subclass above defines production() and nothing else. Under APP_ENV=development it resolved llama3.2 / 1000 β€” the parent's values, untouched. Environments you do not redeclare are inherited.

🎯

Only the classes you name

PaymentSettings has no override file, so it resolved live / 5 exactly as committed. There is no blanket switch β€” one file overrides one class.

🧬

It must be a real subclass

The resolver checks is_subclass_of and ignores anything else, so a same-named class that is unrelated can never be substituted in.

πŸ—ΊοΈ

Attributes still apply

PHP does not inherit attributes onto overridden methods, so the resolver walks the hierarchy β€” an override that redeclares a method marked #[Environment] keeps its mapping.

Putting them somewhere else

The defaults are app/Settings/Overrides and the matching namespace. Both are configurable, and override_path is resolved at runtime rather than at config-load time β€” so a relative value stays correct even when the config cache was built somewhere else.

// config/env-settings.php
'override'           => env('ENV_SETTINGS_OVERRIDE', false),

//   null                    β†’ app_path('Settings/Overrides')
//   'Custom/Overrides'      β†’ app_path('Custom/Overrides')
//   '/mnt/shared/overrides' β†’ used as-is
'override_path'      => null,
'override_namespace' => 'App\Settings\Overrides',
Read the docs Values written once All four commands Is this for you? Back to the demo