diff --git a/docs/guide/helper-array.md b/docs/guide/helper-array.md index df4fcce176..2552816835 100644 --- a/docs/guide/helper-array.md +++ b/docs/guide/helper-array.md @@ -303,21 +303,68 @@ Encoding will use application charset and could be changed via third argument. ## Merging Arrays +You can use [[yii\helpers\ArrayHelper::merge()|ArrayHelper::merge()]] to merge two or more arrays into one recursively. +If each array has an element with the same string key value, the latter will overwrite the former +(different from [array_merge_recursive()](http://php.net/manual/en/function.array-merge-recursive.php)). +Recursive merging will be conducted if both arrays have an element of array type and are having the same key. +For integer-keyed elements, the elements from the latter array will be appended to the former array. +You can use [[yii\helpers\UnsetArrayValue]] object to unset value from previous array or +[[yii\helpers\ReplaceArrayValue]] to force replace former value instead of recursive merging. + +For example: + ```php - /** - * Merges two or more arrays into one recursively. - * If each array has an element with the same string key value, the latter - * will overwrite the former (different from array_merge_recursive). - * Recursive merging will be conducted if both arrays have an element of array - * type and are having the same key. - * For integer-keyed elements, the elements from the latter array will - * be appended to the former array. - * @param array $a array to be merged to - * @param array $b array to be merged from. You can specify additional - * arrays via third argument, fourth argument etc. - * @return array the merged array (the original arrays are not changed.) - */ - public static function merge($a, $b) +$array1 = [ + 'name' => 'Yii', + 'version' => '1.1', + 'ids' => [ + 1, + ], + 'validDomains' => [ + 'example.com', + 'www.example.com', + ], + 'emails' => [ + 'admin' => 'admin@example.com', + 'dev' => 'dev@example.com', + ], +]; + +$array2 = [ + 'version' => '2.0', + 'ids' => [ + 2, + ], + 'validDomains' => new \yii\helpers\ReplaceArrayValue([ + 'yiiframework.com', + 'www.yiiframework.com', + ]), + 'emails' => [ + 'dev' => new \yii\helpers\UnsetArrayValue(), + ], +]; + +$result = ArrayHelper::merge($array1, $array2); +``` + +The result will be: + +```php +[ + 'name' => 'Yii', + 'version' => '2.0', + 'ids' => [ + 1, + 2, + ], + 'validDomains' => [ + 'yiiframework.com', + 'www.yiiframework.com', + ], + 'emails' => [ + 'admin' => 'admin@example.com', + ], +] ``` diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index ebbff08cab..0e93ff999a 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -9,6 +9,7 @@ Yii Framework 2 Change Log - Bug #11912: Fixed PostgreSQL Schema to support negative default values for integer/float/decimal columns (nsknewbie) - Bug #11947: Fixed `gridData` initialization in `yii.gridView.js` (pavlm) - Bug #11949: Fixed `ActiveField::end` generates close tag when it's `option['tag']` is null (egorio) +- Enh #11275: Added possibility of unset or force replace former value in `ArrayHelper::merge()` (mdmunir, rob006) - Enh #11950: Improve BaseArrayHelper::keyExists speed (egorio) - Bug #11726: `DbSession` was echoing database errors in production mode (samdark, pastuhov, deadkrolik) - Bug #12030: Fixed `yii\base\Model::offsetExists()` throws an exception on un-existing field (klimov-paul) diff --git a/framework/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php index 1968772939..48ca2faccf 100644 --- a/framework/helpers/BaseArrayHelper.php +++ b/framework/helpers/BaseArrayHelper.php @@ -107,6 +107,8 @@ class BaseArrayHelper * type and are having the same key. * For integer-keyed elements, the elements from the latter array will * be appended to the former array. + * You can use [[UnsetArrayValue]] object to unset value from previous array or + * [[ReplaceArrayValue]] to force replace former value instead of recursive merging. * @param array $a array to be merged to * @param array $b array to be merged from. You can specify additional * arrays via third argument, fourth argument etc. @@ -119,7 +121,11 @@ class BaseArrayHelper while (!empty($args)) { $next = array_shift($args); foreach ($next as $k => $v) { - if (is_int($k)) { + if ($v instanceof UnsetArrayValue) { + unset($res[$k]); + } elseif ($v instanceof ReplaceArrayValue) { + $res[$k] = $v->value; + } elseif (is_int($k)) { if (isset($res[$k])) { $res[] = $v; } else { diff --git a/framework/helpers/ReplaceArrayValue.php b/framework/helpers/ReplaceArrayValue.php new file mode 100644 index 0000000000..48644cf2be --- /dev/null +++ b/framework/helpers/ReplaceArrayValue.php @@ -0,0 +1,73 @@ + [ + * 1, + * ], + * 'validDomains' => [ + * 'example.com', + * 'www.example.com', + * ], + * ]; + * + * $array2 = [ + * 'ids' => [ + * 2, + * ], + * 'validDomains' => new \yii\helpers\ReplaceArrayValue([ + * 'yiiframework.com', + * 'www.yiiframework.com', + * ]), + * ]; + * + * $result = \yii\helpers\ArrayHelper::merge($array1, $array2); + * ``` + * + * The result will be + * + * ```php + * [ + * 'ids' => [ + * 1, + * 2, + * ], + * 'validDomains' => [ + * 'yiiframework.com', + * 'www.yiiframework.com', + * ], + * ] + * ``` + * + * @author Robert Korulczyk + * @since 2.0.10 + */ +class ReplaceArrayValue +{ + /** + * @var mixed value used as replacement. + */ + public $value; + + /** + * Constructor. + * @param mixed $value value used as replacement. + */ + public function __construct($value) + { + $this->value = $value; + } +} diff --git a/framework/helpers/UnsetArrayValue.php b/framework/helpers/UnsetArrayValue.php new file mode 100644 index 0000000000..0cb48021ac --- /dev/null +++ b/framework/helpers/UnsetArrayValue.php @@ -0,0 +1,53 @@ + [ + * 1, + * ], + * 'validDomains' => [ + * 'example.com', + * 'www.example.com', + * ], + * ]; + * + * $array2 = [ + * 'ids' => [ + * 2, + * ], + * 'validDomains' => new \yii\helpers\UnsetArrayValue(), + * ]; + * + * $result = \yii\helpers\ArrayHelper::merge($array1, $array2); + * ``` + * + * The result will be + * + * ```php + * [ + * 'ids' => [ + * 1, + * 2, + * ], + * ] + * ``` + * + * @author Robert Korulczyk + * @since 2.0.10 + */ +class UnsetArrayValue +{ +} diff --git a/tests/framework/helpers/ArrayHelperTest.php b/tests/framework/helpers/ArrayHelperTest.php index c8a89c3c97..16184ddde9 100644 --- a/tests/framework/helpers/ArrayHelperTest.php +++ b/tests/framework/helpers/ArrayHelperTest.php @@ -307,6 +307,79 @@ class ArrayHelperTest extends TestCase $this->assertEquals($expected, $result); } + public function testMergeWithUnset() + { + $a = [ + 'name' => 'Yii', + 'version' => '1.0', + 'options' => [ + 'namespace' => false, + 'unittest' => false, + ], + 'features' => [ + 'mvc', + ], + ]; + $b = [ + 'version' => '1.1', + 'options' => new \yii\helpers\UnsetArrayValue(), + 'features' => [ + 'gii', + ], + ]; + + $result = ArrayHelper::merge($a, $b); + $expected = [ + 'name' => 'Yii', + 'version' => '1.1', + 'features' => [ + 'mvc', + 'gii', + ], + ]; + + $this->assertEquals($expected, $result); + } + + public function testMergeWithReplace() + { + $a = [ + 'name' => 'Yii', + 'version' => '1.0', + 'options' => [ + 'namespace' => false, + 'unittest' => false, + ], + 'features' => [ + 'mvc', + ], + ]; + $b = [ + 'version' => '1.1', + 'options' => [ + 'unittest' => true, + ], + 'features' => new \yii\helpers\ReplaceArrayValue([ + 'gii', + ]), + ]; + + $result = ArrayHelper::merge($a, $b); + $expected = [ + 'name' => 'Yii', + 'version' => '1.1', + 'options' => [ + 'namespace' => false, + 'unittest' => true, + ], + 'features' => [ + 'gii', + ], + ]; + + $this->assertEquals($expected, $result); + } + /** * @see https://github.com/yiisoft/yii2/pull/11549 */