Table of Contents
📖 2 minutes read
Scattering env() calls across your codebase creates hidden coupling and makes testing painful. Here’s a better pattern: centralize related configuration into a dedicated service.
The Problem
When you need external service credentials, it’s tempting to reach for env() wherever you need them:
class ReportUploader
{
public function upload($file)
{
$host = env('CLIENT_SFTP_HOST');
$user = env('CLIENT_SFTP_USERNAME');
$pass = env('CLIENT_SFTP_PASSWORD');
$this->sftpClient->connect($host, $user, $pass);
// ...
}
}
This works, but now your service class is tightly coupled to specific environment variable names. Change a variable name? Hunt down every env() call. Mock config for tests? Good luck.
The Fix
Create a settings service that acts as a single source of truth:
class ClientSettingsService
{
public function getSftpHost(): string
{
return config('clients.sftp.host');
}
public function getSftpUsername(): string
{
return config('clients.sftp.username');
}
public function getSftpPassword(): string
{
return config('clients.sftp.password');
}
}
Now inject the service instead of calling env() directly:
class ReportUploader
{
public function __construct(
private ClientSettingsService $settings
) {}
public function upload($file)
{
$host = $this->settings->getSftpHost();
$user = $this->settings->getSftpUsername();
$pass = $this->settings->getSftpPassword();
$this->sftpClient->connect($host, $user, $pass);
// ...
}
}
Benefits
- Centralized changes: Rename a config key? Update one service method.
- Easy testing: Mock the service, not individual
env()calls. - Type safety: Explicit return types catch config issues at compile time.
- Domain clarity:
$settings->getSftpHost()is more readable thanenv('CLIENT_SFTP_HOST').
If you’re reaching for env() in a service class, stop. Create a settings service first.
Leave a Reply