Laravel中带通配符的数组还原与JSON Schema映射问题
解决Laravel中JSON Schema映射的索引还原问题
问题根源分析
你当前的实现核心问题在于:将数组扁平化后用正则把数字索引替换成*,但Laravel的data_fill无法将*识别为动态索引,而是直接当作字面量键名,导致最终结果出现*键,且多元素数组(如pedidos)的内容被覆盖,未映射的字段(如email)被孤立。
修复后的实现代码
我们需要保留原索引数字,通过正则匹配映射规则,将通配符*替换为对应的捕获组,生成带正确索引的键路径,再用data_set设置值:
use Illuminate\Support\Arr; $payload = [ [ "identificador" => 1, "nome_completo" => "John Doe", "email" => "j@email.com", "empresa" => [ "razao_social" => "ABC Company", "endereco" => "123 Main St" ], "pedidos" => [ [ "SKU" => 1, "descricao" => "Shoes", "qtd" => 2 ], [ "SKU" => 2, "descricao" => "Shirt", "qtd" => 1 ] ] ] ]; $mappingConfig = [ "*.identificador" => "*.id", "*.nome_completo" => "*.name", "*.empresa.razao_social" => "*.company.name", "*.empresa.endereco" => "*.company.address", "*.pedidos.*.SKU" => "*.orders.*.id", "*.pedidos.*.descricao" => "*.orders.*.product", "*.pedidos.*.qtd" => "*.orders.*.quantity", ]; // 将映射规则转换为正则+替换模板 $regexMappings = []; foreach ($mappingConfig as $pattern => $replacement) { // 把*转为捕获数字的正则组 $regexPattern = preg_replace('/\*/', '(\d+)', $pattern); $regexPattern = '/^' . str_replace('.', '\.', $regexPattern) . '$/'; // 把替换模板中的*转为捕获组占位符 $replaceTemplate = preg_replace('/\*/', '$$$1', $replacement); $regexMappings[] = [ 'regex' => $regexPattern, 'replace' => $replaceTemplate, ]; } $dottedPayload = Arr::dot($payload); $transformedPayload = []; foreach ($dottedPayload as $key => $value) { $newKey = $key; // 默认保留原键 // 匹配映射规则 foreach ($regexMappings as $mapping) { if (preg_match($mapping['regex'], $key, $matches)) { array_shift($matches); // 移除完整匹配的字符串,保留捕获的索引 // 替换捕获组占位符为实际索引 $newKey = preg_replace_callback('/\$(\d+)/', function($match) use ($matches) { return $matches[$match[1]-1]; }, $mapping['replace']); break; } } // 用data_set替代data_fill,确保值被正确设置到路径 data_set($transformedPayload, $newKey, $value); } // 输出结果与预期一致 dd($transformedPayload);
优化方案:递归遍历处理(无需扁平化)
对于复杂嵌套结构,递归遍历数组可以更直观地控制转换逻辑,避免扁平化带来的路径处理问题:
function transformArray($array, $mappingConfig, $currentPath = '') { $result = []; foreach ($array as $key => $value) { $fullPath = $currentPath ? "{$currentPath}.{$key}" : $key; $mappedPath = $fullPath; // 匹配映射规则 foreach ($mappingConfig as $pattern => $replacement) { $regexPattern = preg_replace('/\*/', '(\d+)', $pattern); $regexPattern = '/^' . str_replace('.', '\.', $regexPattern) . '$/'; if (preg_match($regexPattern, $fullPath, $matches)) { array_shift($matches); $mappedPath = preg_replace_callback('/\$(\d+)/', function($match) use ($matches) { return $matches[$match[1]-1]; }, preg_replace('/\*/', '$$$1', $replacement)); break; } } // 拆分目标路径,设置值或递归处理子数组 $pathParts = explode('.', $mappedPath); $targetKey = array_pop($pathParts); $targetParent = implode('.', $pathParts); if (is_array($value)) { $childResult = transformArray($value, $mappingConfig, $fullPath); $targetParent ? data_set($result, "{$targetParent}.{$targetKey}", $childResult) : ($result[$targetKey] = $childResult); } else { $targetParent ? data_set($result, "{$targetParent}.{$targetKey}", $value) : ($result[$targetKey] = $value); } } return $result; } // 使用方式 $transformedPayload = transformArray($payload, $mappingConfig); dd($transformedPayload);
关键优化点
- 保留原索引:通过正则捕获原路径中的数字索引,替换映射规则中的
*,生成带正确索引的目标路径。 - 使用
data_set:替代data_fill,确保每个值都被精准设置到目标路径,避免多元素数组的覆盖问题。 - 递归遍历:对于复杂嵌套结构,递归处理无需扁平化数组,逻辑更清晰,性能更优。
内容的提问来源于stack exchange,提问作者Gabriel Baierle
相关产品推荐
相关产品推荐

