diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index c5879d1..c1c4f2a 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -26,6 +26,7 @@ Yii Framework 2 Change Log - Bug: Fixed the issue that query cache returns the same data for the same SQL but different query methods (qiangxue) - Enh #364: Improve Inflector::slug with `intl` transliteration. Improved transliteration char map. (tonydspaniard) - Enh #797: Added support for validating multiple columns by `UniqueValidator` and `ExistValidator` (qiangxue) +- Enh #802: Added support for retrieving sub-array element or child object property through `ArrayHelper::getValue()` (qiangxue, cebe) - Enh #1293: Replaced Console::showProgress() with a better approach. See Console::startProgress() for details (cebe) - Enh #1406: DB Schema support for Oracle Database (p0larbeer, qiangxue) - Enh #1437: Added ListView::viewParams (qiangxue) diff --git a/framework/yii/helpers/BaseArrayHelper.php b/framework/yii/helpers/BaseArrayHelper.php index ad8cb21..d3ae705 100644 --- a/framework/yii/helpers/BaseArrayHelper.php +++ b/framework/yii/helpers/BaseArrayHelper.php @@ -123,7 +123,14 @@ class BaseArrayHelper /** * Retrieves the value of an array element or object property with the given key or property name. - * If the key does not exist in the array, the default value will be returned instead. + * If the key does not exist in the array or object, the default value will be returned instead. + * + * The key may be specified in a dot format to retrieve the value of a sub-array or the property + * of an embedded object. In particular, if the key is `x.y.z`, then the returned value would + * be `$array['x']['y']['z']` or `$array->x->y->z` (if `$array` is an object). If `$array['x']` + * or `$array->x` is neither an array nor an object, the default value will be returned. + * Note that if the array already has an element `x.y.z`, then its value will be returned + * instead of going through the sub-arrays. * * Below are some usage examples, * @@ -136,6 +143,8 @@ class BaseArrayHelper * $fullName = \yii\helpers\ArrayHelper::getValue($user, function($user, $defaultValue) { * return $user->firstName . ' ' . $user->lastName; * }); + * // using dot format to retrieve the property of embedded object + * $street = \yii\helpers\ArrayHelper::getValue($users, 'address.street'); * ~~~ * * @param array|object $array array or object to extract value from @@ -144,15 +153,29 @@ class BaseArrayHelper * `function($array, $defaultValue)`. * @param mixed $default the default value to be returned if the specified key does not exist * @return mixed the value of the element if found, default value otherwise + * @throws InvalidParamException if $array is neither an array nor an object. */ public static function getValue($array, $key, $default = null) { if ($key instanceof \Closure) { return $key($array, $default); + } + + if (is_array($array) && array_key_exists($key, $array)) { + return $array[$key]; + } + + if (($pos = strrpos($key, '.')) !== false) { + $array = static::getValue($array, substr($key, 0, $pos), $default); + $key = substr($key, $pos + 1); + } + + if (is_object($array)) { + return $array->$key; } elseif (is_array($array)) { - return isset($array[$key]) || array_key_exists($key, $array) ? $array[$key] : $default; + return array_key_exists($key, $array) ? $array[$key] : $default; } else { - return $array->$key; + return $default; } } diff --git a/tests/unit/framework/helpers/ArrayHelperTest.php b/tests/unit/framework/helpers/ArrayHelperTest.php index f7410d5..d55b4b0 100644 --- a/tests/unit/framework/helpers/ArrayHelperTest.php +++ b/tests/unit/framework/helpers/ArrayHelperTest.php @@ -320,4 +320,62 @@ class ArrayHelperTest extends TestCase $this->assertTrue(ArrayHelper::keyExists('B', $array, false)); $this->assertFalse(ArrayHelper::keyExists('c', $array, false)); } + + public function valueProvider() + { + return [ + ['name', 'test'], + ['noname', null], + ['noname', 'test', 'test'], + ['post.id', 5], + ['post.id', 5, 'test'], + ['nopost.id', null], + ['nopost.id', 'test', 'test'], + ['post.author.name', 'cebe'], + ['post.author.noname', null], + ['post.author.noname', 'test', 'test'], + ['post.author.profile.title', '1337'], + ['admin.firstname', 'Qiang'], + ['admin.firstname', 'Qiang', 'test'], + ['admin.lastname', 'Xue'], + [ + function ($array, $defaultValue) { + return $array['date'] . $defaultValue; + }, + '31-12-2113test', + 'test' + ], + ]; + } + + /** + * @dataProvider valueProvider + * + * @param $key + * @param $expected + * @param null $default + */ + public function testGetValue($key, $expected, $default = null) + { + $array = [ + 'name' => 'test', + 'date' => '31-12-2113', + 'post' => [ + 'id' => 5, + 'author' => [ + 'name' => 'cebe', + 'profile' => [ + 'title' => '1337', + ], + ], + ], + 'admin.firstname' => 'Qiang', + 'admin.lastname' => 'Xue', + 'admin' => [ + 'lastname' => 'cebe', + ], + ]; + + $this->assertEquals($expected, ArrayHelper::getValue($array, $key, $default)); + } }