6

I am developing multiple websites based on Yii2 framework and I was wondering if there is any good way or if it's even good idea or not to create a shared basic config main.php file.

The reason behind this is that all my projects are based on same system, have a lot of similarities and I simply want to keep things DRY. Configs are also mostly same - the only differences actually are database names and few other values. Even urlManager rules are not so different.

The problem is that when for example I would want to add additional FileTarget to logger targets for all websites I have to do this for each project separately and I can never be sure if all project configs are up to date and have everything set up properly.

So I was wondering if there is any Yii way of creating default config file shared in multiple projects or if it's simply a bad idea. For example I could create bootstrap component which sets defaults or add default config as first item in the list of merged configs in frontend\web\index.php file. I have private packages added in composer so it's not a problem adding file that will be shared in all projects.

labm0nkey
  • 626
  • 1
  • 5
  • 9

3 Answers3

6

I am working on the same question for a pretty long time and tried several ideas.

At the moment I've developed nice solution for the issues you describe. The heart of the solution is hiqdev/composer-config-plugin - composer plugin which merges configs from extensions.

For example we have extension that is base for all our web projects hiqdev/hisite-core it contains basic Yii application config and params:

return [
    'id' => 'hisite',
    'name' => 'HiSite',
    'aliases' => [
        '@bower' => '@vendor/bower-asset',
        ...

These configs are enabled in composer.json with such lines:

"extra": {
    "config-plugin": {
        "params": "src/config/params.php",
        "hisite": "src/config/hisite.php"
    }
},

And that's it. You require hiqdev/hisite-core and after running composer update you have assembled configs (you may have multiple config) in vendor/hiqdev/config/hisite.php

Then you use it from your index.php like this:

$config = require dirname(dirname(__DIR__)) . '/hiqdev/config/hisite.php';

You can achieve similar behaviour with Yii extension bootstrapping but bootstrap have to run on every page request.

The beauty of the solution is that you can have lots of components every one of them providing own part of config and it works without slowing page rendering time because the config is merged once on composer install/update. (And you can force merging of config with composer dump-autoload, don't forget to run it after changing config).

Using this technique we've creating auto plugging extensions: themes, modules and more that are not required to be configured in main config - you just require them in composer.json and you have it working.

This solution is still work in progress and it's going to evolve and change but we use it for really big Yii2 based application with 30+ extensions and it is already tested and stable enough.

hiqsol
  • 101
  • 3
  • @hisqol You said that you tried different ideas so as I understand this one works best for you? Also, why it's work in progress? Are there any drawbacks or something missing that you didn't mention? – labm0nkey Aug 25 '16 at 14:34
  • 1
    I've tried bootstrapping way - but it has performance problem I've mentioned. This solution works fine. Work in progress because it's used by us only and I think with more users there may appear changes. – hiqsol Aug 25 '16 at 14:36
  • I've checked your plugin and I can see the idea although it seemed to be a bit of overkill for me. I've decided to keep things simple and just merge additional configs. – labm0nkey Aug 26 '16 at 10:36
  • Yes, I see, I've tried that too :) I've just got tired copy and paste same code over projects and created general solution with advanced features. – hiqsol Aug 27 '16 at 15:03
5

As a simpler alternative you can just put a file somewhere and include it from all the configs i.e.

$commonConfig = require '/var/www/common/config.php';
$appConfig = require 'configs/main.php';
$config = array_merge($commonConfig, $appConfig);
// ...
Sam Dark
  • 5,291
  • 1
  • 34
  • 52
0

What I've ended up doing was creating a ConfigHelper class that helps merging local configs with global ones. I'm posting it just for other people to see the idea. This might not be the utlimate solution but it worked with my existing setup.

The ConfigHelper along with default config files are included in one of the packages (private repository) that I am including in all my yii2 based projects by using composer.

And now instead of the default fronted/web/index.php config merging from the yii2 template:

$config = yii\helpers\ArrayHelper::merge(
    require(__DIR__ . '/../../common/config/main.php'),
    require(__DIR__ . '/../../common/config/main-local.php'),
    require(__DIR__ . '/../config/main.php'),
    require(__DIR__ . '/../config/main-local.php')
);

$application = new yii\web\Application($config);
$application->run();

I am using my helper class:

$config = ConfigHelper::build(
    __DIR__ . '/../..', //project root path
    ConfigHelper::TIER_FRONTEND, // fronted, console or backend
    'my-unique-app-name', //could be any other param that you need
);

$application = new yii\web\Application($config);
$application->run();

The helper class which also has default configs in files next to it:

class ConfigHelper
{
    const TIER_FRONTEND = 'frontend';
    const TIER_CONSOLE  = 'console';
    const TIER_BACKEND  = 'backend';

    public static function build($root, $tier, $website)
    {
        // Notice that local files are included so in case they don't exist there won't be errors
        return ArrayHelper::merge(
            require(__DIR__ . "/../config/common/main.php"), // global basic config file for all projects
            require("{$root}/common/config/main.php"),
            include("{$root}/common/config/main-local.php"),
            require(__DIR__ . "/../config/{$tier}/main.php"), // global basic config file for all projects
            require("{$root}/{$tier}/config/main.php"),
            include("{$root}/{$tier}/config/main-local.php")
        );
    }
}

The global default config files are same as the ones used in Yii2. They simply contain common settings excluding unique settings that you can add per app as you normally do.

labm0nkey
  • 626
  • 1
  • 5
  • 9