diff --git a/src/Illuminate/Collections/helpers.php b/src/Illuminate/Collections/helpers.php index 16c8f0118993..10161b88965a 100644 --- a/src/Illuminate/Collections/helpers.php +++ b/src/Illuminate/Collections/helpers.php @@ -108,6 +108,24 @@ function data_get($target, $key, $default = null) */ function data_set(&$target, $key, $value, $overwrite = true) { + return data_map($target, $key, fn () => $value, $overwrite); + } +} + +if (! function_exists('data_map')) { + /** + * Run a map over an array or object using dot notation. + * + * @param mixed $target + * @param string|array $key + * @param callable $callback + * @param bool $overwrite + * @return mixed + */ + function data_map(&$target, $key, callable $callback, bool $overwrite = true) + { + static $fullKey = ''; + $segments = is_array($key) ? $key : explode('.', $key); if (($segment = array_shift($segments)) === '*') { @@ -116,12 +134,16 @@ function data_set(&$target, $key, $value, $overwrite = true) } if ($segments) { - foreach ($target as &$inner) { - data_set($inner, $segments, $value, $overwrite); + $loopKey = $fullKey; + foreach ($target as $i => &$inner) { + $fullKey = trim($loopKey.'.'.$i, '.'); + data_map($inner, $segments, $callback, $overwrite); } } elseif ($overwrite) { - foreach ($target as &$inner) { - $inner = $value; + $loopKey = $fullKey; + foreach ($target as $i => &$inner) { + $fullKey = trim($loopKey.'.'.$i, '.'); + $inner = $callback($inner, $fullKey); } } } elseif (Arr::accessible($target)) { @@ -130,9 +152,10 @@ function data_set(&$target, $key, $value, $overwrite = true) $target[$segment] = []; } - data_set($target[$segment], $segments, $value, $overwrite); + $fullKey = trim($fullKey.'.'.$segment, '.'); + data_map($target[$segment], $segments, $callback, $overwrite); } elseif ($overwrite || ! Arr::exists($target, $segment)) { - $target[$segment] = $value; + $target[$segment] = $callback(Arr::get($target, $segment), $fullKey); } } elseif (is_object($target)) { if ($segments) { @@ -140,20 +163,24 @@ function data_set(&$target, $key, $value, $overwrite = true) $target->{$segment} = []; } - data_set($target->{$segment}, $segments, $value, $overwrite); + $fullKey = trim($fullKey.'.'.$segment, '.'); + data_map($target->{$segment}, $segments, $callback, $overwrite); } elseif ($overwrite || ! isset($target->{$segment})) { - $target->{$segment} = $value; + $target->{$segment} = $callback($target->{$segment} ?? null, $fullKey); } } else { $target = []; if ($segments) { - data_set($target[$segment], $segments, $value, $overwrite); + $fullKey = trim($fullKey.'.'.$segment, '.'); + data_map($target[$segment], $segments, $callback, $overwrite); } elseif ($overwrite) { - $target[$segment] = $value; + $target[$segment] = $callback($target[$segment] ?? null, $fullKey); } } + $fullKey = ''; + return $target; } } diff --git a/tests/Support/SupportHelpersTest.php b/tests/Support/SupportHelpersTest.php index 1074e97dd6d9..1928fe68db36 100644 --- a/tests/Support/SupportHelpersTest.php +++ b/tests/Support/SupportHelpersTest.php @@ -622,6 +622,46 @@ public function testDataSetWithDoubleStar() ], $data); } + public function testDataMap() + { + $data = [ + 'posts' => [ + (object) [ + 'comments' => [ + (object) ['order' => 1], + (object) [], + ], + ], + (object) [ + 'comments' => [ + (object) [], + (object) ['order' => 2], + ], + ], + ], + ]; + + data_map($data, 'posts.*.comments.*.key', fn ($old, $key) => $key); + data_map($data, 'posts.*.comments.*.order', fn ($old) => isset($old) ? ++$old : null); + + $this->assertEquals([ + 'posts' => [ + (object) [ + 'comments' => [ + (object) ['key' => 'posts.0.comments.0', 'order' => 2], + (object) ['key' => 'posts.0.comments.1', 'order' => null], + ], + ], + (object) [ + 'comments' => [ + (object) ['key' => 'posts.1.comments.0', 'order' => null], + (object) ['key' => 'posts.1.comments.1', 'order' => 3], + ], + ], + ], + ], $data); + } + public function testDataRemove() { $data = ['foo' => 'bar', 'hello' => 'world'];