Laravel 5 load env file based on .env.master? - php

I'm planing to do something like, specifying what env file to load during application bootstrap time by creating a variable in .env.master such as ENV_VER = dev.env
This is because I have several branches such as development, release-1.1 and etc. Thus by loading the env file based on the name specified in a master env file, developers no longer have to copy and paste new variables into their local copy of .env and instead, just specify what version of env file to load in the master env file. By the way, I have several env files such as dev.env, 1.6.env etc.
Is it possible?

Definitely, me myself I tend to "bend" a framework little bit everytime and there's always a way, not always the best solution though. I'm not giving a whole implementation here, just pointing you in some direction, that might work for you.
You can extend Laravel's base application class Illuminate\Foundation\Application, which contains $environmentFile variable storing environment file loaded during appplication bootstrap or possibly override function loadEnvironmentFrom($file) or environmentFile(). The whole logic is up to you.
So basically all you need to do in order to be able to "play" with .env loading is...
Create a new application class extending Laravel's one:
namespace MyNamespace;
...
use Illuminate\Foundation\Application as App;
/**
* I'm doing alots of magic with .env in this class
*/
class Application extends App
{
...
/**
* The environment file to load during bootstrapping.
*
* #var string
*/
protected $environmentFile = '.env.main';
...
}
And now, because we have a new bootstraping class, we have to tell Laravel we want to use it. So you'll have to modify bootstrap/app.php file in point where a new instance is being created:
...
$app = new MyNamespace\Application(
realpath(__DIR__.'/../')
);
...
Note: For inspiration I recommend you to really look at Illuminate\Foundation\Application class.

In laravel 5.5+ you can, maybe earlier, you can set your server have a APP_ENV environment or server variable that the process can see (apache, command line...)
this will allow you to use a suffix or file extension on your .env files for auto loading those files...
APP_ENV=dev :: .env.dev
APP_ENV=production :: .env.production
much easier than other solutions.
If you want to look into how this is done, it starts with
1. application bootstrappers
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\SetRequestForConsole::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
2. LoadEnvironmentVariables
first the environment is determined…
\Illuminate\Foundation\Bootstrap\LoadConfiguration::bootstrap calls
\Illuminate\Foundation\Application::detectEnvironment
If
--env={env} for CLI then it will use that for APP_ENV.
Else
\Illuminate\Foundation\EnvironmentDetector::detectWebEnvironment
is called which uses a callback…
function () use ($config) {
return $config->get('app.env', 'production');
}
where app.env defaults to env('APP_ENV', 'production')
3 LoadConfiguration
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::bootstrap calls...
\Illuminate\Foundation\Application::afterLoadingEnvironment
which eventually gets to
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::checkForSpecificEnvironmentFile
which sets the environment file based on app env IF the file exists.
$this->setEnvironmentFilePath(
$app, $app->environmentFile().'.'.env('APP_ENV')
);
allowing it to load the .env.{APP_ENV} instead of .env
NOTE: Testing.
when running php unit tests. the Illuminate/Foundation will try to load the .env.testing file for configurations!

Related

Laravel Environment Variables(without default value passed in method) not working inside Artisan Command file

I am using Laravel 6.x for client's project wherein I have built an API in the Artisan Command for syncing data.
Now client wants the configuration to be discreet, out of main source code and without any fallback values as possible. That means I must define the configs in the .env file and use the env() method without any fallback default values.
This must be possible inside Laravel Artisan command class files, but it is not working as intended when I use the env method in the code as below:
[siteroot]\.env:
APP_ENV=local
[siteroot]\app\Console\Commands\SyncSomeData.php:
use Illuminate\Console\Command;
class SyncSomeData extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'sync:some-data';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
exit(env('APP_ENV','staging1'));
}
}
This always prints staging1 in console and if I use this instead of the given env method, then it prints nothing:
exit(env('APP_ENV'));
As I know and can trust that Laravel is most secure, there must be way to have env method work without fallback value in the command file, can anyone help fulfilling this ?
There are a couple parts to this answer.
Caching Config
The issue in this question is only present if you have cached your config on your local environment. If you have ever run php artisan config:cache or php artisan optimize, you have cached your config.
As a result, laravel will no longer read your .env file and load all values for config and .env from cache.
It is best practice, to not use env() throughout your application but create config files for these values. This will speed up your application in production. If you want to take advantage of config cache, you CAN NOT use env() anywhere but in your config files.
In your local environment, the documentation recommends you should not be caching your configuration, since it will be updated frequently. I think this is good advice with one caveat: env(). If you aren't caching your config on local, but are in production, you won't come across errors like the one that was the impetus for this post in local. If you have a ci pipeline and good testing practices than this hurdle is overcome.
If you stick with cache config in local, every time you update your config files or your .env file you will need to run php artisan config:cache.
Disabling config cache
By running php artisan config:clear or removing /bootstrap/cache/config.php laravel will no longer to attempt to read config from cache, and .env will work outside of config files. If you do this, as I stated in (1) make sure there is a part of your pipeline that will catch these errors, if you are caching config in production.
After more discussion with OP, I will make one more thing clear:
Cache config does not automatically just cache your .env keys so you can access them like config('SOME_CUSTOM_DOT_ENV_KEY'). When using cache config, if you need to access environment variables inside of your application, you must reference them in a config file.
Again, once config cache is enabled,.env becomes useless, but it is used to build the cache.
So if you in .env say:
GOOGLE_API_TOKEN=xxxx
You would maybe create a config file like:
google.php
return [
'api_token' => env('GOOGLE_API_TOKEN', 'some-default-value'),
];
And in your application, you will only reference config('google.api_token') and never env('GOOGLE_API_TOKEN').

Where should I keep my constants in lumen?

I am writing a web service using Lumen and need to store some constants, specifically error values, but also other configuration parameters. Where would it be most appropriate to put these?
Here is how I did it.
I made a const.php file within bootstrap folder where I have defined some error codes.
<?php
define('VALIDATION_EXCEPTION',422);
I included the const.php file in app.php using require_once.
<?php
require_once __DIR__.'./const.php';
Now I can call VALIDATION_EXCEPTION from anywhere. for instance.
$router->get('/test',function(){
return VALIDATION_EXCEPTION;
});
That's it!
Create a file constants.php inside config/ and put your settings in an array:
<?php
return [
'CONTACT_NAME' => 'Admin'
];
Then in your controllers you can get the value by using Config Facade:
echo Config::get('constants.CONTACT_NAME');
This solution was from this link
.env is intended for per-environment configuration and sensitive credentials.
You can maintain a config with this type of data. There is no default config for Lumen like there is for Laravel, so you need to add the required config directory, and tell Lumen to use it:
$ mkdir config
$ touch config/app.php
In the app.php file, you can return an array with the config:
// config/app.php
<?php
return [
'order_by' => 'whatever'
// and so on
];
Then tell Lumen to load the config from app.php inside bootstrap/app.php, by adding (after $app has been initialised):
$app->configure('app');
Finally, you can use the config() helper method to get the configuration:
config('app.order_by') // whatever
1.Create New Directory Helpers under App Directory.
2.Create New php file ErrorCodes.php under Helpers directory
3.Put your constants in that file
<?php define('VALIDATION_ERROR', 'E001'); define('EXCEPTION', 'E002'); define('CUSTOM','E003'); ?>
4.Add this ErrorCodes file under autoload section of composer.json
"files" : [
"app/Helpers/ErrorCodes.php"
]
5.Run composer autoload command in your artisan terminal
composer dumpautoload
And you are all set to use your constants anywhere in your lumen App.
Seems like you are talking about two different things here. For configuration values the only correct location is the .env file (as already mentioned in the comments).
https://lumen.laravel.com/docs/5.5/configuration#environment-configuration
This is where you put all environmental specific configuration values (e.g. API keys, debug mode, etc. etc.). If you have a strong wish for it you can put the env-variables into constants during the bootstrap process e.g.:
define('APP_DEBUG',env('APP_DEBUG', true));
For defining simple constants there should be multiple possibiilties.
You could do this either during the bootstrap process or define a class (e.g. a model):
class Error
{
const FATAL = 1;
const WARNING = 2;
}
use Error;
....
Error::FATAL;
...

Overriding configuration files of composer packages in Lumen

I have a Lumen project with external Composer packages installed. As usual with Lumen, they're stored in the vendor directory, each in their respective folder. Some of these packages have configuration files, which I would like to override with custom ones.
I have registered the files in my bootstrap/app.php using $app->configure() right after I register the application itself, so it looks like this:
require_once __DIR__ . '/../vendor/autoload.php';
try {
(new Dotenv\Dotenv(__DIR__ . '/../'))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
//
}
$app = new Laravel\Lumen\Application(
realpath(__DIR__ . '/../')
);
$app->withFacades();
$app->withEloquent();
$app->configure('/Configuration/Lumen/app.php');
$app->configure('/Configuration/Lumen/auth.php');
$app->configure('/Configuration/Tymon/jwt.php');
The files are present in their respective directories, and contain the settings I want Lumen to use instead of the defaults, which are located, respectively, at:
/vendor/laravel/lumen-framework/config/app.php
/vendor/laravel/lumen-framework/config/auth.php
/vendor/tymon/jwt-auth/config/config.php
The problem I run into is that with this configuration, Lumen seems to ignore my custom files, and instead uses the defaults. What am I doing wrong here?
Put your configuration files in config/ and register them in bootstrap/app.php by their filename, without the .php file ending.
// Your custom config in config/jwt-auth.php
$app->configure('jwt-auth');
I don't think any of the above answers is truly answering what the OP wanted to know how to do.
What they appear to want, is to load a composer package and register that packages configuration into the app without having to do any kind of manual configuration.
Sort of like when you import a standard composer package which builds a logger using environment variables and autoconfigures its setup without having to add that configuration to the app. So then things are simpler.
Although I'm assuming the OP knows that this leads to a few problems, in that you're stuck with a composer package configuring your app, with a few options to override those settings locally.
But I assume you're happy with that. So therefore, I'd like to propose this solution
In your composer package, create a providers folder, then add this code into a lumen service provider class, in my case I've called it TestProvider
<?php declare(strict_types=1);
namespace YourLibrary\Providers;
class TestProvider extends \Illuminate\Support\ServiceProvider
{
public function boot()
{
// Because we're using 'require', this needs to be a valid path
$path = __DIR__.'/vendor_config.php';
// Get a configuration object from the service container
$config = app()->make('config');
// An example: Set into the 'app' configuration at a specific subkey
// just to show you can modify the default app configuration
$config->set('app.subkey', require $path);
error_log(json_encode(config('app')));
// An example: Set into the 'vendor.test' configuration at a specific subkey
$config->set('vendor.test', require $path);
error_log(json_encode(config('vendor')));
}
}
As you can see, you can use the dot notation for the first parameter to set() to insert your configuration into the applications config, but be careful with the naming cause in theory I guess you could override any setting like this, it could be that you end up overwriting the entire config('app') and all it's settings which would lead to a partially non-functioning app.
Then inside your bootstrap.php file in your app, you need to register this service provider, like so:
$app->register(YourLibrary\Providers\TestProvider::class);
I've tested it with an app I was working on locally and this works and I'm already using it for a library that has a very static configuration, everything is working pretty great.

How to set different Laravel 5 storage and cache path local vs production?

I can't find any good answers on how to best set different storage and cache paths for my Laravel 5 application based on which environment it's running on.
I specially want to change the storage path and the cache path.
At the moment the config/cache.php has a line like this:
'path' => storage_path().'/framework/cache',
I want to change that to "/www/app-cache/" for production environment only.
And I want to change the default storage_path to "/www/app-store/" for production environment only.
Is there a good way to achieve this?
At the moment I have created a new application class which extends the old one, like this:
<?php namespace App;
use Illuminate\Foundation\Application;
class XXXApplication extends Application
{
public function storagePath()
{
if($this->environment('production')) {
return '/www/app-store/';
}
return parent::storagePath();
}
}
Is that a smart way to do it, and if so, how do I also change the cache path?
Usually, in you local env, you are using the .env file.
So, you can do the following in your config/cache.php file
'path' => env('CACHE_PATH', storage_path().'/framework/cache');
If in your .env there is a CACHE_PATH variable, your app will use it.
If not, it will use by default the other one specified in the second parameter.
Do the some for you storage path in app.php

How to have environment specific .env files for dotenv (in Laravel 5)

I've just started using Laravel 5 which uses the dotenv library. This uses a .env file in the root of the project which sets the environment with this line:
APP_ENV=local
According to everything I've read on the subject, all other environmental specific configuration should be placed in this file, so database passwords, urls etc, which are then read into the main config array like this:
env('DB_HOST', 'localhost')
While I feel this may work for a few specific things like database passwords that you might not want committed, what I really want is to be able to commit most or all of my different environmental values for each environment.
Thus what I want is for .env to define APP_ENV as "local", "staging" or "production" and then have a .local.env or .env.local file containing the values, which I can then commit and the correct file will be loaded based on APP_ENV.
Is this possible? Laravel 4 had the cascading config arrays which seemed a lot more flexible but if I can have an environmental .env file then I can live with that.
Solved it in the end by modifying app/Providers/ConfigServiceProvider.php. This file is added as a stub to your app folder when you create a project and is intended for overriding config values.
It now handles the cascading configs, so that any values in config/local/app.php for example will override config/app.php. As the comment below says it doesn't handle matching arrays in the environment config and will just replace then. But I can solve that when I need it.
<?php namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Finder\Finder;
class ConfigServiceProvider extends ServiceProvider {
/**
* Overwrite any vendor / package configuration.
*
* This service provider is intended to provide a convenient location for you
* to overwrite any "vendor" or package configuration that you may want to
* modify before the application handles the incoming request / command.
*
* Modified 2014-01-20 to allow environment specific configs to be loaded
* from app/config/[environment]/ which will cascade over the base configs.
*
* #return void
*/
public function register()
{
$config = app('config');
$envPath = app()->configPath() . '/' . getenv('APP_ENV');
foreach (Finder::create()->files()->name('*.php')->in($envPath) as $file)
{
$configName = basename($file->getRealPath(), '.php');
$oldConfigValues = $config->get($configName);
$newConfigValues = require $file->getRealPath();
// Replace any matching values in the old config with the new ones.
// Doesn't yet handle matching arrays in the config, it will just replace them.
$config->set($configName, $newConfigValues + $oldConfigValues);
}
}
}
You dont have to use .env for everything. There are a few options.
Option 1 - Use only .env for a variable
'default' => env('DB_CONNECTION'),
Option 2 - Use only .env for a variable, but have a system default if none exists
'default' => env('DB_CONNECTION', 'mysql'),
Option 3 - just hard code your variable and not make it settable via the .env
'default' => 'mysql',
Option 2 is probably the best for most config options. You still define (and commit) an option for your config to your git repo - but you can easily override it in any .env file in the future if you want.
Option 1 is best specifically for passwords, app keys etc - so they are never committed to your git repo.
Option 3 is for a few config variables which you know will just never change.
Note - the cascading Laravel 4 config folder option is no longer available.
It is easy to configure Laravel 5 environment.
Open your root application folder and find ".env.example",
Copy and rename into ".env",
Please fit ".env" file into your environment,
If you use GIT, make sure you don't push this file to your GIT repository.
For 'complete explanation', I write this configuration here.
I quote from the dotenv developer;
phpdotenv is made for development environments, and generally should
not be used in production. In production, the actual environment
variables should be set so that there is no overhead of loading the
.env file on each request. This can be achieved via an automated
deployment process with tools like Vagrant, chef, or Puppet, or can be
set manually with cloud hosts like Pagodabox and Heroku.

Categories