├── .gitignore
├── admin_screenshot.png
├── src
├── registration.php
├── Model
│ └── Config
│ │ ├── AdditionalInterface.php
│ │ ├── YamlFile.php
│ │ └── AggregateYamlFiles.php
├── etc
│ ├── module.xml
│ └── di.xml
├── Test
│ └── Unit
│ │ ├── SourceTest.php
│ │ ├── AdditionalConfigStub.php
│ │ └── Model
│ │ └── Config
│ │ ├── YamlFileTest.php
│ │ └── AggregateYamlFilesTest.php
├── Source.php
└── Block
│ └── System
│ └── Config
│ └── Form
│ └── Field.php
├── phpcs.xml
├── composer.json
├── .travis.yml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.lock
2 | /vendor/
3 |
--------------------------------------------------------------------------------
/admin_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/webgriffe/module-config-override/HEAD/admin_screenshot.png
--------------------------------------------------------------------------------
/src/registration.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | Magento 2 Module Coding Standard
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Model/Config/AdditionalInterface.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/Test/Unit/SourceTest.php:
--------------------------------------------------------------------------------
1 | get();
13 | $this->assertTrue($config['default']['additional']['config']['stub']);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/Test/Unit/AdditionalConfigStub.php:
--------------------------------------------------------------------------------
1 | ['config' => ['stub' => true]]];
15 | }
16 |
17 | /**
18 | * @return array
19 | */
20 | public function asFlattenArray()
21 | {
22 | return ['additional/config/stub' => true];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/Source.php:
--------------------------------------------------------------------------------
1 | additionalConfig = $additionalConfig;
19 | }
20 |
21 | /**
22 | * Retrieve configuration raw data array.
23 | *
24 | * @param string $path
25 | * @return array
26 | */
27 | public function get($path = '')
28 | {
29 | return ['default' => $this->additionalConfig->asArray()];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/etc/di.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | -
9 |
- Webgriffe\ConfigOverride\Source
10 | - 150
11 |
12 |
13 |
14 |
15 |
16 |
17 | Magento\Backend\Block\Template\Context
18 | Webgriffe\ConfigOverride\Model\Config\AdditionalInterface
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webgriffe/module-config-override",
3 | "description": "A Magento 2 module that reads from file additional configuration",
4 | "type": "magento2-module",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Webgriffe SRL",
9 | "email": "support@webgriffe.com"
10 | }
11 | ],
12 | "require": {
13 | "php": "~5.6.0|~7.0.0|~7.1.0",
14 | "magento/framework": "^100.0.0|^101.0.0",
15 | "magento/module-backend": "^100.0.0",
16 | "magento/module-config": "^100.1.1|^101.0.0",
17 | "symfony/yaml": "^2.0|^3.0"
18 | },
19 | "require-dev": {
20 | "squizlabs/php_codesniffer": "^2.5",
21 | "phpunit/phpunit": "^5.7",
22 | "mikey179/vfsStream": "^1.6"
23 | },
24 | "repositories": [
25 | {
26 | "type": "composer",
27 | "url": "https://repo.magento.com/"
28 | }
29 | ],
30 | "autoload": {
31 | "files": [ "src/registration.php" ],
32 | "psr-4": {
33 | "Webgriffe\\ConfigOverride\\": "src/"
34 | }
35 | },
36 | "autoload-dev": {
37 | "psr-4": {
38 | "Magento\\Framework\\": "lib/internal/Magento/Framework/",
39 | "Magento\\Setup\\": "setup/src/Magento/Setup/"
40 | }
41 | },
42 | "extra": {
43 | "magento-force": "override"
44 | },
45 | "minimum-stability": "alpha",
46 | "prefer-stable": true
47 | }
48 |
--------------------------------------------------------------------------------
/src/Model/Config/YamlFile.php:
--------------------------------------------------------------------------------
1 | data = Yaml::parse(file_get_contents($filePath)) ?: [];
24 | }
25 | }
26 |
27 | /**
28 | * @return array
29 | */
30 | public function asArray()
31 | {
32 | return $this->data;
33 | }
34 |
35 | /**
36 | * @return array
37 | */
38 | public function asFlattenArray()
39 | {
40 | if (is_null($this->flattenData)) {
41 | $this->flattenData = $this->doFlatten($this->data);
42 | }
43 | return $this->flattenData;
44 | }
45 |
46 | private function doFlatten($array, $prefix = '')
47 | {
48 | $result = array();
49 | foreach ($array as $key => $value) {
50 | if (is_array($value)) {
51 | $result = $result + $this->doFlatten($value, $prefix . $key . '/');
52 | } else {
53 | $result[$prefix . $key] = $value;
54 | }
55 | }
56 | return $result;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - '5.6'
4 | - '7.0'
5 | - '7.1'
6 |
7 | env:
8 | global:
9 | - DB_HOST="127.0.0.1"
10 | - DB_NAME="module-config-override"
11 | - DB_USER="root"
12 | - DB_PASS=""
13 | - DB_TEST="module-config-override-test"
14 | - MYSQL_CLI_CREDENTIALS=false
15 | - BASE_URL="http://module-config-override.test/"
16 | matrix:
17 | - MAGENTO_VERSION="2.1.*"
18 | - MAGENTO_VERSION="2.2.*"
19 |
20 | matrix:
21 | exclude:
22 | - php: "5.6"
23 | env: MAGENTO_VERSION="2.2.*"
24 | - php: "7.1"
25 | env: MAGENTO_VERSION="2.1.*"
26 |
27 | cache:
28 | directories:
29 | - $HOME/.composer
30 |
31 | install:
32 | - composer install -n
33 | before_script:
34 | - phpenv config-rm xdebug.ini
35 | script:
36 | - vendor/bin/phpcs --ignore=src/Test/ src/
37 | - vendor/bin/phpunit src/Test/Unit
38 | - composer require -n --dev --no-update magento/product-community-edition "$MAGENTO_VERSION"
39 | - rm composer.lock
40 | - composer install -n
41 | - curl -Ls https://raw.githubusercontent.com/mmenozzi/m2-bash-ci/master/m2-bash-ci.sh | bash
42 | - curl -O https://files.magerun.net/n98-magerun2.phar
43 | - curl -LJO https://github.com/mikefarah/yq/releases/download/1.14.0/yq_linux_amd64
44 | - chmod +x yq_linux_amd64
45 | - touch app/etc/default.yml.dist
46 | - ./yq_linux_amd64 w -i app/etc/default.yml.dist web.unsecure.base_url http://overridden-from-file.test/
47 | - cat app/etc/default.yml.dist
48 | - php n98-magerun2.phar cache:clean
49 | - php n98-magerun2.phar dev:console "\$di->get('\Magento\Framework\App\Config')->getValue('web/unsecure/base_url');exit;" | grep http://overridden-from-file.test/
50 |
--------------------------------------------------------------------------------
/src/Test/Unit/Model/Config/YamlFileTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(YamlFile::class, $defaultYamlFile);
16 | $this->assertEmpty($defaultYamlFile->asArray());
17 | }
18 |
19 | public function testIsInitializableWithSomeData()
20 | {
21 | $filename = 'default.yml';
22 | $content = << ['etc' => [$filename => $content]]]);
28 | $defaultYamlFile = new YamlFile(vfsStream::url('root/app/etc/default.yml'));
29 | $this->assertInstanceOf(YamlFile::class, $defaultYamlFile);
30 | $this->assertEquals(['design' => ['head' => ['default_title' => 'My Title']]], $defaultYamlFile->asArray());
31 | }
32 |
33 | public function testAsFlattenArray()
34 | {
35 | $filename = 'default.yml';
36 | $content = << ['etc' => [$filename => $content]]]);
45 | $defaultYamlFile = new YamlFile(vfsStream::url('root/app/etc/default.yml'));
46 | $this->assertInstanceOf(YamlFile::class, $defaultYamlFile);
47 | $this->assertEquals(
48 | ['design/head/default_title' => 'My Title', 'other/config/setting' => 'value'],
49 | $defaultYamlFile->asFlattenArray()
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Model/Config/AggregateYamlFiles.php:
--------------------------------------------------------------------------------
1 | directoryList = $directoryList;
25 | $this->request = $request;
26 | }
27 |
28 | /**
29 | * @return array
30 | */
31 | public function asArray()
32 | {
33 | $data = [];
34 | foreach ($this->getYmlFiles() as $ymlFile) {
35 | $data = array_replace_recursive($data, $ymlFile->asArray());
36 | }
37 | return $data;
38 | }
39 |
40 | /**
41 | * @return array
42 | */
43 | public function asFlattenArray()
44 | {
45 | $data = [];
46 | foreach ($this->getYmlFiles() as $ymlFile) {
47 | $data = array_replace_recursive($data, $ymlFile->asFlattenArray());
48 | }
49 | return $data;
50 | }
51 |
52 | /**
53 | * @return array
54 | */
55 | private function getYmlFiles()
56 | {
57 | $configPath = $this->directoryList->getPath(DirectoryList::CONFIG);
58 | $ymlFiles = [];
59 | $ymlFiles[] = new YamlFile($configPath . DIRECTORY_SEPARATOR . self::BASE_FILE_NAME . '.yml.dist');
60 | $ymlFiles[] = new YamlFile($configPath . DIRECTORY_SEPARATOR . self::BASE_FILE_NAME . '.yml');
61 | $env = $this->request->getServer('MAGE_ENVIRONMENT');
62 | if ($env) {
63 | $envBaseFileName = sprintf('%s-%s', self::BASE_FILE_NAME, $env);
64 | $ymlFiles[] = new YamlFile($configPath . DIRECTORY_SEPARATOR . $envBaseFileName . '.yml.dist');
65 | $ymlFiles[] = new YamlFile($configPath . DIRECTORY_SEPARATOR . $envBaseFileName . '.yml');
66 | }
67 | return $ymlFiles;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Block/System/Config/Form/Field.php:
--------------------------------------------------------------------------------
1 | flatOverriddenConfig = $additionalConfig->asFlattenArray();
24 | }
25 |
26 | protected function _getElementHtml(AbstractElement $element)
27 | {
28 | if ($element->getData('scope') === self::ACTION_SCOPE_TYPE) {
29 | if ($this->isElementValueOverridden($element)) {
30 | $element->setValue($this->getElementOverriddenValue($element));
31 | $element->setData('disabled', true);
32 | $element->setComment(
33 | sprintf(
34 | 'The value for this configuration setting, for scope "%s", comes from file ' .
35 | 'and cannot be modified.
',
36 | self::ACTION_SCOPE_TYPE
37 | ) .
38 | $element->getComment()
39 | );
40 | }
41 | }
42 | return parent::_getElementHtml($element);
43 | }
44 |
45 | /**
46 | * @param AbstractElement $element
47 | * @return string
48 | */
49 | protected function isElementValueOverridden(AbstractElement $element)
50 | {
51 | $fieldConfig = $element->getData('field_config');
52 | $path = $fieldConfig['path'] . '/' . $fieldConfig['id'];
53 | return array_key_exists($path, $this->flatOverriddenConfig);
54 | }
55 |
56 | /**
57 | * @param AbstractElement $element
58 | * @return string
59 | */
60 | protected function getElementOverriddenValue(AbstractElement $element)
61 | {
62 | $fieldConfig = $element->getData('field_config');
63 | $path = $fieldConfig['path'] . '/' . $fieldConfig['id'];
64 | return $this->flatOverriddenConfig[$path];
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Test/Unit/Model/Config/AggregateYamlFilesTest.php:
--------------------------------------------------------------------------------
1 | directoryList = $this->prophesize(DirectoryList::class);
24 | $this->directoryList->getPath(DirectoryList::CONFIG)->willReturn(vfsStream::url('root'));
25 | $this->request = $this->prophesize(Http::class);
26 | $this->aggregateYamlFiles = new AggregateYamlFiles($this->directoryList->reveal(), $this->request->reveal());
27 | }
28 |
29 | public function testAsArrayAggregatesYamlFilesInDirectory()
30 | {
31 | $defaultYmlDist = << $defaultYmlDist, 'default.yml' => $defaultYml]);
46 | $this->assertEquals(
47 | [
48 | 'design' => ['head' => ['default_title' => 'Another title', 'default_other' => 'Other']],
49 | 'other' => ['config' => ['setting' => 'value']]
50 | ],
51 | $this->aggregateYamlFiles->asArray()
52 | );
53 | }
54 |
55 | public function testAsFlattenArrayAggregatesYamlFilesInDirectory()
56 | {
57 | $defaultYmlDist = << $defaultYmlDist, 'default.yml' => $defaultYml]);
72 | $this->assertEquals(
73 | [
74 | 'design/head/default_title' => 'Another title',
75 | 'design/head/default_other' => 'Other',
76 | 'other/config/setting' => 'value',
77 | ],
78 | $this->aggregateYamlFiles->asFlattenArray()
79 | );
80 | }
81 |
82 | public function testLoadsEvenConfigForSpecifiedEnvironment()
83 | {
84 | $this->request->getServer('MAGE_ENVIRONMENT')->willReturn('dev');
85 | $defaultYml = << $defaultYml,
112 | 'default-dev.yml' => $devConfigYml,
113 | 'default-dev.yml.dist' => $devConfigDistYml,
114 | 'default-prod.yml.dist' => $prodConfigDistYml
115 | ]
116 | );
117 | $this->assertEquals(
118 | [
119 | 'design' => ['head' => ['default_title' => 'My Title']],
120 | 'dev' => ['config' => ['setting' => 'value', 'other' => 'setting']],
121 | ],
122 | $this->aggregateYamlFiles->asArray()
123 | );
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Config Override Magento 2 Module
2 | ================================
3 |
4 | ## :warning:DEPRECATION:warning:
5 |
6 | This module it is now useless, you can:
7 | 1. Do `composer require symfony/dotenv`, take a look to [these exaplanations](https://github.com/symfony/demo/blob/v2.2.1/.env#L1-L12) to see the hierarchy;
8 | 2. Create the file `app/etc/EnvironmentVariablesLoader.php`
9 | ```php
10 | loadEnv(__DIR__ . "/../../.env");
17 | ```
18 | 3. Load it throught composer modifing the `composer.json` file:
19 | ```json
20 | "files": [
21 | + "app/etc/EnvironmentVariablesLoader.php",
22 | "app/etc/NonComposerComponentRegistration.php"
23 | ],
24 | ```
25 | 4. Create the env files configuring variables as written on the [magento docu](https://experienceleague.adobe.com/docs/commerce-operations/configuration-guide/paths/override-config-settings.html?lang=en#environment-variables).
26 | ## :warning:DEPRECATION:warning:
27 |
28 | [](https://travis-ci.org/webgriffe/module-config-override)
29 |
30 | A Magento 2 module that overrides default configuration from file which can be added to version control, inspired by this Magento 1.x extension: [https://github.com/webgriffe/config-extension](https://github.com/webgriffe/config-extension).
31 |
32 | Installation
33 | ------------
34 |
35 | Add this extension as dependency using [Composer](https://getcomposer.org):
36 |
37 | composer require webgriffe/module-config-override
38 | php bin/magento setup:upgrade
39 |
40 | Config override
41 | ---------------
42 |
43 | Magento configuration is driven by database. This, sometimes, is overkill and forces us to maintain upgrade script to keep Magento envorinment aligned with features development.
44 | So, this extension enables additional config source that loads several YAML files and overrides database configuration.
45 |
46 | Loaded YAML files are (in this order):
47 |
48 | * `app/etc/default.yml.dist`: this intended to be under version control to distribute configuration for all environments.
49 | * `app/etc/default.yml`: this is intendet to be ignored by version control system to provide configuration related to local needs.
50 |
51 | Also, if the `MAGE_ENVIRONMENT` environment variable is defined, then two additiontal files are loaded. For example, if the `MAGE_ENVIRONMENT` variable value is `dev`, the following two files are loaded:
52 |
53 | * `app/etc/default-dev.yml.dist`: as `default.yml.dist`, this is intended to be under version control to distribute configuration but only for the `dev` environment.
54 | * `app/etc/default-dev.yml`: as `default.yml`, this is intended to be ignored by version control to provide configuration related to local needs but only for the `dev` environment.
55 |
56 | Configuration in YAML files must be specified with the same structure of Magento system configuration, for example:
57 |
58 | ```yml
59 | web:
60 | secure:
61 | base_url: "http://my-project-url.dev/"
62 | unsecure:
63 | base_url: "http://my-project-url.dev/"
64 | ```
65 | Only `default` configuration scope is overridden.
66 |
67 | CLI notes
68 | ---------
69 |
70 | Please note that if you use the `MAGE_ENVIRONMENT` variable then it should be **always** set when Magento is running. So is not enough to set it through your webserver (for example with a `SetEnv "dev"` in Apache) but it should also be set into your command line shell. Otherwise, if you have the Magento's configuration cache enabled and you clear the cache from the command line, the `MAGE_ENVIRONMENT` will not be applied and you'll get unexpected behaviours.
71 |
72 | Remember to "export" you env variable when you run a command via shell:
73 |
74 | ```
75 | export MAGE_ENVIRONMENT=dev &&
76 | ```
77 |
78 | alternative you can add the `MAGE_ENVIRONMENT=dev` in your shell configuration (ex. for bash):
79 |
80 | file `/home//.bashrc`:
81 |
82 | ```
83 | ....
84 | ....
85 |
86 | MAGE_ENVIRONMENT=dev
87 | ```
88 |
89 | Overridden config values are shown in backend
90 | ---------------------------------------------
91 |
92 | Overridden config values are shown in Magento's backend. Every config setting it's shown on its section. For example, if you have the following `default.yml` file:
93 |
94 | ```yml
95 | design:
96 | head:
97 | default_title: Webgriffe Store
98 | title_suffix: - Webgriffe Store
99 | ```
100 |
101 | When you'll go to `Stores -> Configuration -> General -> Design` you'll find the overridden config value shown and not editable.
102 |
103 | 
104 |
105 | This feature improves a lot the usability of this extension.
106 |
107 | To Do
108 | -----
109 |
110 | * Improve system config admin interface to support complex fields
111 |
112 | Credits
113 | -------
114 |
115 | * Developed by [Webgriffe®](http://webgriffe.com)
116 |
--------------------------------------------------------------------------------