In Laravel 4, we had configuration along the following lines:

app/config/
    app.php
    auth.php
    cache.php
    compile.php
    database.php
    local/
        app.php
        auth.php
        cache.php
        database.php
    mail.php
    production/
        app.php
        auth.php
        cache.php
        database.php
    queue.php
    remote.php
    services.php
    session.php
    testing/
        cache.php
        session.php
    view.php
    workbench.php

In addition to this directory structure, we then had environment configuration files for each environment - .env.php, .env.local.php, .env.testing.php.

With the old structure, it was necessary to create the directory structure for anything we wanted to change per-environment, in addition to a .env.*.php file for any sensitive configuration we wanted to keep out of version control. This approach is quite messy.

With the introduction of Laravel 5, this configuration structure was flattened:

config/
    app.php
    auth.php
    cache.php
    compile.php
    database.php
    filesystems.php
    mail.php
    queue.php
    services.php
    session.php
    view.php

We now also have a single .env file.

But how do we configure things for different environments now? This is madness Taylor!

In flattening the structure, what we have now is a much simpler way of configuring the application and moving it between environments (homestead, Digital Ocean, S3). That's the important thing to note here; that environments are generally completely independent of each other.

So now, you have different configuration parameters for your database. On your local environment, you might be using SQLite. On your production environment, you're probably using MySQL or PostgreSQL. How is that configured?

<?php

return [
    'fetch' => PDO::FETCH_CLASS,
    'default' => env('DB_DRIVER', 'mysql'),
    'connections' => [
        'sqlite' => [
            'driver' => 'sqlite',
            'database' => storage_path() . '/database.sqlite',
            'prefix' => '',
        ],
        'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', 'localhost'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'charset' => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix' => '',
            'strict' => false,
        ],
    ],
];

Your .env file will simply contain the parameters that change from deployment to deployment.

DB_HOST=localhost
DB_DATABASE=mydatabase
DB_USERNAME=myusername
DB_PASSWORD=5up3r53cr37

The env() helper takes two parameters - the key to retrieve from the .env file and a default to be used if it hasn't been set. Based on my database.php and .env files above, we can see that for this environment we'll be using the mysql driver and our credentials will be set based on values in the .env file.

Laravel 5 shifted to using PHP dotenv, which enables us to use the .env file, in lieu of proper environment variables.

You should never store sensitive credentials in your code. Storing configuration in the environment is one of the tenets of a twelve-factor app. Anything that is likely to change between deployment environments – such as database credentials or credentials for 3rd party services – should be extracted from the code into environment variables.

What does that mean? When you deploy to your production environment using, for example, Laravel Forge you would actually set these configuration parameters via the web interface. The .env file doesn't even exist!

PHP dotenv allows us to be agile in adding, changing, and removing environment variables without having to mess around with virtual hosts in Apache or nginx, adding php_value keys to .htaccess files and so on. When we're developing, these things can change quite often. Once we're in production, once they're set, they're set (unless you're adding new ones, or updating credentials).

For testing environment, these settings can be changed in your phpunit.xml file. Remember - you don't need to have the messy structure of Laravel 4.

<php>
    <env name="APP_ENV" value="testing"/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="SESSION_DRIVER" value="array"/>
</php>

So what if you still really want to switch between environments? In development, it's actually quite simple. You can create multiple environment files - .env.dev, .env.local, .env.testing - then symlink between them creating a reference to whichever you want in your .env file.

Now, in doing this, you would need to make sure that you update .gitignore to ignore .env and .env.* to make sure you don't accidentally commit any sensitive information to version control. From there, it's a matter of flicking the switch.

Here's a simple bash function you can use - just drop it in your .zshrc or .bash_aliases file, whichever is appropriate for the shell you're using.

function envswitch() {
    if [ ! "$1" ]
    then
        echo "Missing required parameter envname"
        echo "File should exist in current directory as .env.envname"
        echo "Usage:"
        echo "  envswitch envname"
        return
    fi

    ENV_PATH="./.env"
    ENV_LINK=".env.$1"

    # Ensure the file being sylinked exists
    if [ ! -e "$ENV_LINK" ]
    then
        echo "Error: "$ENV_LINK" does not exist in current directory"
        return
    fi

    # If the ENV_PATH already exists, remove it
    if [ -e "$ENV_PATH" ]
    then
        rm -f "$ENV_PATH"
    fi

    # Symlink the file to $ENV_LINK
    ln -s "$ENV_LINK" "$ENV_PATH"
}

Source the file (. ~/.zshrc), then you can run envswitch dev, which will symlink your .env.dev file to .env and you will have all your dev environment variables.