In Laravel 4, we had configuration along the following lines:
1app/config/ 2 app.php 3 auth.php 4 cache.php 5 compile.php 6 database.php 7 local/ 8 app.php 9 auth.php10 cache.php11 database.php12 mail.php13 production/14 app.php15 auth.php16 cache.php17 database.php18 queue.php19 remote.php20 services.php21 session.php22 testing/23 cache.php24 session.php25 view.php26 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:
1config/ 2 app.php 3 auth.php 4 cache.php 5 compile.php 6 database.php 7 filesystems.php 8 mail.php 9 queue.php10 services.php11 session.php12 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?
1<?php 2 3return [ 4 'fetch' => PDO::FETCH_CLASS, 5 'default' => env('DB_DRIVER', 'mysql'), 6 'connections' => [ 7 'sqlite' => [ 8 'driver' => 'sqlite', 9 'database' => storage_path() . '/database.sqlite',10 'prefix' => '',11 ],12 'mysql' => [13 'driver' => 'mysql',14 'host' => env('DB_HOST', 'localhost'),15 'database' => env('DB_DATABASE', 'forge'),16 'username' => env('DB_USERNAME', 'forge'),17 'password' => env('DB_PASSWORD', ''),18 'charset' => 'utf8',19 'collation' => 'utf8_unicode_ci',20 'prefix' => '',21 'strict' => false,22 ],23 ],24];
Your .env
file will simply contain the parameters that change from deployment to deployment.
1DB_HOST=localhost2DB_DATABASE=mydatabase3DB_USERNAME=myusername4DB_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.
1<php>2 <env name="APP_ENV" value="testing"/>3 <env name="CACHE_DRIVER" value="array"/>4 <env name="SESSION_DRIVER" value="array"/>5</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.
1function envswitch() { 2 if [ ! "$1" ] 3 then 4 echo "Missing required parameter envname" 5 echo "File should exist in current directory as .env.envname" 6 echo "Usage:" 7 echo " envswitch envname" 8 return 9 fi10 11 ENV_PATH="./.env"12 ENV_LINK=".env.$1"13 14 # Ensure the file being sylinked exists15 if [ ! -e "$ENV_LINK" ]16 then17 echo "Error: "$ENV_LINK" does not exist in current directory"18 return19 fi20 21 # If the ENV_PATH already exists, remove it22 if [ -e "$ENV_PATH" ]23 then24 rm -f "$ENV_PATH"25 fi26 27 # Symlink the file to $ENV_LINK28 ln -s "$ENV_LINK" "$ENV_PATH"29}
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.