Keep .env for secrets and stock Laravel keys. Move the non-secret configuration your app adds on top β third-party integrations, AI providers, payment gateways, microservice endpoints β into typed, version-controlled PHP classes with full IDE autocomplete and code review visibility.
.env?env() returns null after config:cachestring, int, bool, floatconfig:cache β no env() calls
Laravel's own keys β and those of every third-party or community package β are turned into
config() entries by
config/*.php files during the
LoadConfiguration bootstrap step, before the
service container exists. A settings class resolves from that container, so it can never feed them.
Those keys stay in .env, and this package
makes no attempt to move them.
.envAPP_KEY
DB_*
MAIL_*
QUEUE_*
config/*.php reads through env().AiSettings
PaymentSettings
NotificationSettings
ExternalApiSettings
app/Settings/, visible in every pull request.
The boundary is enforced by Laravel, not by convention: put
AiSettings::resolve() inside a
config/*.php file and the app dies with
Class "env" does not exist β the container
isn't built yet.
No third-party runtime dependencies beyond Laravel itself.
Install via Composer β that's all you need.
composer require hpwebdeveloper/laravel-env-settings
This creates config/env-settings.php where you register your settings classes and configure the environment map.
php artisan vendor:publish --tag="env-settings-config"
Use the artisan command to scaffold a new settings class with typed properties. Each class defines what the values should be in each environment.
php artisan env-settings:make AiSettings \
--properties="provider:string,text_model:string,max_tokens:int,temperature:float"
Settings class created: app/Settings/AiSettings.php
β Registered \App\Settings\AiSettings::class in config/env-settings.php
This creates app/Settings/AiSettings.php:
class AiSettings extends EnvironmentSettings
{
public function __construct(
public string $provider,
public string $text_model,
public int $max_tokens,
public float $temperature,
) {}
public static function development(): static
{
return new static(
provider: 'ollama', // free, local
text_model: 'llama3.2',
max_tokens: 1000,
temperature: 0.9,
);
}
public static function production(): static
{
return new static(
provider: 'openai', // paid, best quality
text_model: 'gpt-4o',
max_tokens: 8000,
temperature: 0.2,
);
}
}
A settings class stays inert until it is listed in the register array of config/env-settings.php β so env-settings:make appends that line for you. If it can't, because the config isn't published or has no register array, it says so and tells you what to add. Everything listed here is resolved once and bound as a singleton.
// config/env-settings.php
'register' => [
\App\Settings\AiSettings::class,
],
Access your settings via the global helper, the container, or type-hinted dependency injection β all three resolve the same singleton instance.
// Option A: Global helper (works everywhere)
$provider = envSettings(AiSettings::class)->provider;
// Option B: Container resolution
$ai = app(AiSettings::class);
$ai->text_model; // 'gpt-4o' in production, 'llama3.2' in development
// Option C: Type-hinted injection (recommended in controllers/services)
public function __invoke(AiSettings $ai): View
{
return view('dashboard', ['model' => $ai->text_model]);
}
View the resolved values for the current environment or compare two environments side by side.
# Show resolved values for the current environment
php artisan env-settings:show "App\Settings\AiSettings"
# Show all registered settings classes
php artisan env-settings:show
# Compare development vs production
php artisan env-settings:diff "App\Settings\AiSettings" development production
AI-powered apps are the perfect use case β every environment uses different providers, models, and token limits. Instead of juggling .env variables, make each choice explicit and typed.
use Prism\Prism\Prism;
$ai = envSettings(AiSettings::class);
$response = Prism::text()
->using($ai->provider, $ai->text_model)
->withMaxTokens($ai->max_tokens)
->withPrompt('Summarize...')
->asText();
use Laravel\Ai\Facades\Ai;
$ai = envSettings(AiSettings::class);
$response = Ai::text()
->using($ai->provider, $ai->text_model)
->withMaxTokens($ai->max_tokens)
->withPrompt('Summarize...')
->asText();
In development: uses ollama + llama3.2 (free, local) β’
In staging: uses openai + gpt-4o-mini (balanced) β’
In production: uses openai + gpt-4o (best quality) β
every change visible in a PR, fully typed, zero .env juggling.
Power-user features for teams that need flexibility.
Individual developers can override any settings class locally by creating an override class in app/Settings/Overrides/.
Add that folder to .gitignore and enable via ENV_SETTINGS_OVERRIDE=true in your .env.
Compose multiple settings classes into a single root object. Access nested settings like
envSettings(AppSettings::class)->ai->text_model
for a unified configuration entry point.
Map any APP_ENV value to a factory method.
'local' => 'development',
'prod' => 'production',
etc. Customize in config/env-settings.php.