Skip to main content
Back to ScopeForged

ScopeForged Documentation

Technical documentation, guides, and feature references for the ScopeForged client portal.

Core Standards/Configuration

Configuration Guide

Last Updated: 2026-01-09 Status: Active Audience: Developers

This guide documents configuration patterns and environment management for the Client Portal application.


Table of Contents

  1. Overview
  2. Environment Variables
  3. Configuration Files
  4. Custom Configuration
  5. Accessing Configuration
  6. Environment-Specific Settings
  7. Secrets Management

Overview

Laravel configuration is managed through:

  • .env files for environment-specific values
  • config/*.php files for application settings
  • Environment variables for sensitive data

Configuration Priority

  1. Environment variables (.env)
  2. Config file defaults
  3. Package defaults

Environment Variables

.env File Structure

#--------------------------------------------------------------------
# Application
#--------------------------------------------------------------------
APP_NAME="Client Portal"
APP_ENV=local
APP_KEY=base64:...
APP_DEBUG=true
APP_URL=http://localhost:8000

#--------------------------------------------------------------------
# Database
#--------------------------------------------------------------------
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=client_portal
DB_USERNAME=root
DB_PASSWORD=

#--------------------------------------------------------------------
# Cache & Session
#--------------------------------------------------------------------
CACHE_DRIVER=file
SESSION_DRIVER=database
SESSION_LIFETIME=120
QUEUE_CONNECTION=sync

#--------------------------------------------------------------------
# Mail
#--------------------------------------------------------------------
MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="noreply@clientportal.test"
MAIL_FROM_NAME="${APP_NAME}"

#--------------------------------------------------------------------
# Application-Specific
#--------------------------------------------------------------------
CLIENT_PORTAL_MAX_FILE_SIZE=10485760
CLIENT_PORTAL_INVOICE_DUE_DAYS=30

.env.example Template

Always maintain .env.example with all variables (without sensitive values):

APP_NAME="Client Portal"
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=client_portal
DB_USERNAME=root
DB_PASSWORD=

# Add comments for non-obvious variables
# Maximum file upload size in bytes (default: 10MB)
CLIENT_PORTAL_MAX_FILE_SIZE=10485760

Naming Conventions

PatternExample
Laravel coreAPP_NAME, DB_HOST, MAIL_MAILER
Custom appCLIENT_PORTAL_* prefix
ServicesSTRIPE_SECRET, AWS_ACCESS_KEY_ID

Configuration Files

Core Configuration Files

config/
├── app.php          # Application settings
├── auth.php         # Authentication guards, providers
├── cache.php        # Cache drivers
├── database.php     # Database connections
├── filesystems.php  # Storage disks
├── logging.php      # Log channels
├── mail.php         # Mail configuration
├── queue.php        # Queue connections
├── services.php     # Third-party services
└── session.php      # Session configuration

Configuration File Structure

<?php

// config/app.php
return [
    /*
    |--------------------------------------------------------------------------
    | Application Name
    |--------------------------------------------------------------------------
    |
    | This value is the name of your application, which will be used when the
    | framework needs to place the application's name in a notification or
    | other UI elements where an application name needs to be displayed.
    |
    */

    'name' => env('APP_NAME', 'Laravel'),

    'env' => env('APP_ENV', 'production'),

    'debug' => (bool) env('APP_DEBUG', false),

    'url' => env('APP_URL', 'http://localhost'),

    // ...
];

Custom Configuration

Creating Custom Config Files

<?php

// config/client_portal.php
return [
    /*
    |--------------------------------------------------------------------------
    | File Upload Settings
    |--------------------------------------------------------------------------
    */

    'files' => [
        // Maximum file size in bytes (10MB default)
        'max_size' => (int) env('CLIENT_PORTAL_MAX_FILE_SIZE', 10 * 1024 * 1024),

        // Allowed MIME types
        'allowed_types' => [
            'application/pdf',
            'application/msword',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'application/vnd.ms-excel',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'image/jpeg',
            'image/png',
        ],

        // Storage disk for project files
        'disk' => env('CLIENT_PORTAL_FILES_DISK', 'local'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Invoice Settings
    |--------------------------------------------------------------------------
    */

    'invoices' => [
        // Default days until invoice is due
        'due_days' => (int) env('CLIENT_PORTAL_INVOICE_DUE_DAYS', 30),

        // Days before due date to send reminder
        'reminder_days' => [7, 3, 1],

        // Tax rate (decimal)
        'tax_rate' => (float) env('CLIENT_PORTAL_TAX_RATE', 0.10),

        // Invoice number prefix
        'number_prefix' => env('CLIENT_PORTAL_INVOICE_PREFIX', 'INV-'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Notification Settings
    |--------------------------------------------------------------------------
    */

    'notifications' => [
        // Send email notifications
        'email_enabled' => (bool) env('CLIENT_PORTAL_EMAIL_NOTIFICATIONS', true),

        // Admin email for system notifications
        'admin_email' => env('CLIENT_PORTAL_ADMIN_EMAIL'),
    ],

    /*
    |--------------------------------------------------------------------------
    | Pagination
    |--------------------------------------------------------------------------
    */

    'pagination' => [
        'per_page' => 15,
        'max_per_page' => 100,
    ],
];

Organizing Complex Configuration

<?php

// config/client_portal.php - using separate files
return [
    'files' => require __DIR__ . '/client_portal/files.php',
    'invoices' => require __DIR__ . '/client_portal/invoices.php',
    'notifications' => require __DIR__ . '/client_portal/notifications.php',
];

Accessing Configuration

Using the config() Helper

// Get single value
$maxSize = config('client_portal.files.max_size');

// Get with default
$taxRate = config('client_portal.invoices.tax_rate', 0.10);

// Get entire section
$fileConfig = config('client_portal.files');

// Set value at runtime (not recommended)
config(['client_portal.files.max_size' => 5 * 1024 * 1024]);

Using the Config Facade

use Illuminate\Support\Facades\Config;

$maxSize = Config::get('client_portal.files.max_size');
$fileConfig = Config::get('client_portal.files');

In Service Classes

class InvoiceService
{
    private int $dueDays;
    private float $taxRate;

    public function __construct()
    {
        $this->dueDays = config('client_portal.invoices.due_days');
        $this->taxRate = config('client_portal.invoices.tax_rate');
    }

    public function calculateDueDate(): Carbon
    {
        return now()->addDays($this->dueDays);
    }
}

Injecting Configuration

// For testability, inject config values
class InvoiceService
{
    public function __construct(
        private readonly int $dueDays,
        private readonly float $taxRate
    ) {}
}

// In AppServiceProvider
$this->app->when(InvoiceService::class)
    ->needs('$dueDays')
    ->give(fn () => config('client_portal.invoices.due_days'));

$this->app->when(InvoiceService::class)
    ->needs('$taxRate')
    ->give(fn () => config('client_portal.invoices.tax_rate'));

Environment-Specific Settings

Local Development

# .env (local)
APP_ENV=local
APP_DEBUG=true

# Use log driver for mail (see emails in logs)
MAIL_MAILER=log

# Use sync queue for immediate processing
QUEUE_CONNECTION=sync

# SQLite for simplicity (optional)
DB_CONNECTION=sqlite

Production

# .env (production)
APP_ENV=production
APP_DEBUG=false

# Real mail provider
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=postmaster@mg.example.com
MAIL_PASSWORD=your-password

# Database queue for reliability
QUEUE_CONNECTION=database

# Redis for cache and sessions
CACHE_DRIVER=redis
SESSION_DRIVER=redis

Testing

// phpunit.xml
<php>
    <env name="APP_ENV" value="testing"/>
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value=":memory:"/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="SESSION_DRIVER" value="array"/>
    <env name="QUEUE_CONNECTION" value="sync"/>
    <env name="MAIL_MAILER" value="array"/>
</php>

Checking Environment

// Check current environment
if (app()->environment('production')) {
    // Production-only code
}

if (app()->environment(['local', 'staging'])) {
    // Local or staging
}

// Check debug mode
if (config('app.debug')) {
    // Debug mode enabled
}

// In Blade
@production
    <script src="analytics.js"></script>
@endproduction

@env('local')
    <div class="debug-bar">...</div>
@endenv

Secrets Management

Never Commit Secrets

# .gitignore
.env
.env.local
.env.*.local
.env.production
.env.staging

Required vs Optional Variables

// config/client_portal.php

// Required - will throw error if missing
'stripe_secret' => env('STRIPE_SECRET') ?? throw new \RuntimeException(
    'STRIPE_SECRET environment variable is required'
),

// Optional with sensible default
'tax_rate' => (float) env('CLIENT_PORTAL_TAX_RATE', 0.10),

Validating Configuration

// app/Providers/AppServiceProvider.php
public function boot(): void
{
    if (app()->environment('production')) {
        $this->validateProductionConfig();
    }
}

private function validateProductionConfig(): void
{
    $required = [
        'APP_KEY',
        'DB_HOST',
        'DB_DATABASE',
        'MAIL_HOST',
    ];

    foreach ($required as $key) {
        if (empty(env($key))) {
            throw new \RuntimeException("Missing required environment variable: {$key}");
        }
    }

    if (config('app.debug')) {
        throw new \RuntimeException('APP_DEBUG must be false in production');
    }
}

Production Secrets

For production, consider:

# AWS Parameter Store / Secrets Manager
# Set environment variables in your hosting platform

# Laravel Forge
# Configure via Forge dashboard

# Docker
# Use Docker secrets or environment files

# Kubernetes
# Use ConfigMaps and Secrets

Configuration Caching

Cache Configuration for Production

# Cache all configuration
php artisan config:cache

# Clear configuration cache
php artisan config:clear

Important Notes

When configuration is cached:

  • .env file is NOT read
  • env() calls in config files work
  • env() calls elsewhere return null
// Good - env() in config file
// config/services.php
'stripe' => [
    'secret' => env('STRIPE_SECRET'),
],

// Then use config()
$secret = config('services.stripe.secret');

// Bad - env() outside config file
// app/Services/PaymentService.php
$secret = env('STRIPE_SECRET'); // Returns null when cached!

Best Practices

Do

  • Use .env for environment-specific values
  • Provide sensible defaults in config files
  • Document all custom environment variables
  • Use config() helper, not env() in application code
  • Cache configuration in production
  • Validate required configuration on boot

Don't

  • Commit .env files
  • Use env() outside of config files
  • Hard-code environment-specific values
  • Store sensitive data in config files
  • Forget to update .env.example

Config File Checklist

  • All values use env() where appropriate
  • Sensible defaults provided
  • Comments explain non-obvious settings
  • Values are cast to correct types
  • .env.example is updated
  • Validated in production if critical