php: converte i dati dal database all'arrays gerarchico

Sono rimasto sconcertato da questo problema per giorni, senza fortuna. Spero che alcuni di voi possano aiutare. Dal mio database ottengo un elenco di file, a cui sono associate varie informazioni, incluso un path virtuale. Alcuni dati tipici sono:

Array ( [0] => Array ( [name] => guide_to_printing.txt [virtual_path] => guides/it ) [1] => Array ( [name] => guide_to_vpn.txt [virtual_path] => guides/it ) [2] => Array ( [name] => for_new_employees.txt [virtual_path] => guides ) ) 

Desidero convertire questo in una struttura di arrays gerarchica dai routes virtuali, quindi l'output di quanto sopra dovrebbe essere:

 Array ( [0] => Array ( [type] => dir [name] => guides [children] => Array ( [0] => Array ( [type] => dir [name] => it [children] = Array ( [0] => Array ( [type] => file [name] => guide_to_printing.txt ) [1] => Array ( [type] => file [name] => guide_to_vpn.txt ) ) ) [1] => Array ( [type] => file [name] => for_new_employees.txt ) ) ) ) 

Dove la properties; type indica se si tratta di una directory o di un file.

Qualcuno può aiutare con la creazione di una function che fa questa conversione. Sarà di grande aiuto. Grazie.

La mia soluzione migliore finora è:

 foreach($docs as $doc) { $path = explode("/",$doc['virtual_path']); $arraysToInsert = arrays( 'name' => $doc['name'], 'path' => $doc['virtual_path'], ); if(count($path)==1) { $r[$path[0]][] = $arraysToInsert; } if(count($path)==2) { $r[$path[0]][$path[1]][] = $arraysToInsert; } if(count($path)==3) { $r[$path[0]][$path[1]][$path[2]][] = $arraysToInsert; } } 

Ovviamente questo funziona solo per una profondità di 3 nella struttura della directory e le chiavi sono i nomi delle directory.

Funzione

 function hierarchify(arrays $files) { /* prepare root node */ $root = new stdClass; $root->children = arrays(); /* file iteration */ foreach ($files as $file) { /* argument validation */ switch (true) { case !isset($file['name'], $file['virtual_path']): case !is_string($name = $file['name']): case !is_string($virtual_path = $file['virtual_path']): throw new InvalidArgumentException('invalid arrays structure detected.'); case strpos($virtual_path, '/') === 0: throw new InvalidArgumentException('absolute path is not allowed.'); } /* virtual url normalization */ $parts = arrays(); $segments = explode('/', preg_replace('@/[email protected]', '/', $virtual_path)); foreach ($segments as $segment) { if ($segment === '.') { continue; } if (null === $tail = arrays_pop($parts)) { $parts[] = $segment; } elseif ($segment === '..') { if ($tail === '..') { $parts[] = $tail; } if ($tail === '..' or $tail === '') { $parts[] = $segment; } } else { $parts[] = $tail; $parts[] = $segment; } } if ('' !== $tail = arrays_pop($parts)) { // skip empty $parts[] = $tail; } if (reset($parts) === '..') { // invalid upper traversal throw new InvalidArgumentException('invalid upper traversal detected.'); } $currents = &$root->children; /* hierarchy iteration */ foreach ($parts as $part) { while (true) { foreach ($currents as $current) { if ($current->type === 'dir' and $current->name === $part) { // directory already exists! $currents = &$current->children; break 2; } } // create new directory... $currents[] = $new = new stdClass; $new->type = 'dir'; $new->name = $part; $new->children = arrays(); $currents = &$new->children; break; } } // create new file... $currents[] = $new = new stdClass; $new->type = 'file'; $new->name = $name; } /* convert into arrays completely */ return json_decode(json_encode($root->children), true); } 

Esempio

Caso 1:

 $files = arrays( 0 => arrays ( 'name' => 'b.txt', 'virtual_path' => 'A/B//', ), 1 => arrays( 'name' => 'a.txt', 'virtual_path' => '././A/B/C/../..', ), 2 => arrays( 'name' => 'c.txt', 'virtual_path' => './A/../A/B/C//////', ), 3 => arrays( 'name' => 'root.txt', 'virtual_path' => '', ), ); var_dump(hierarchify($files)); 

uscirà …

 arrays(2) { [0]=> arrays(3) { ["type"]=> string(3) "dir" ["name"]=> string(1) "A" ["children"]=> arrays(2) { [0]=> arrays(3) { ["type"]=> string(3) "dir" ["name"]=> string(1) "B" ["children"]=> arrays(2) { [0]=> arrays(2) { ["type"]=> string(4) "file" ["name"]=> string(5) "b.txt" } [1]=> arrays(3) { ["type"]=> string(3) "dir" ["name"]=> string(1) "C" ["children"]=> arrays(1) { [0]=> arrays(2) { ["type"]=> string(4) "file" ["name"]=> string(5) "c.txt" } } } } } [1]=> arrays(2) { ["type"]=> string(4) "file" ["name"]=> string(5) "a.txt" } } } [1]=> arrays(2) { ["type"]=> string(4) "file" ["name"]=> string(8) "root.txt" } } 

Caso 2:

 $files = arrays( 0 => arrays ( 'name' => 'invalid.txt', 'virtual_path' => '/A/B/C', ), ); var_dump(hierarchify($files)); 

getterò …

 Fatal error: Uncaught exception 'InvalidArgumentException' with message 'absolute path is not allowed.' 

Caso 3:

 $files = arrays( 0 => arrays ( 'name' => 'invalid.txt', 'virtual_path' => 'A/B/C/../../../../../../../..', ), ); var_dump(hierarchify($files)); 

getterò …

 Fatal error: Uncaught exception 'InvalidArgumentException' with message 'invalid upper traversal detected.' 

Con qualcosa del genere:

 foreach ($arrays as $k => $v) { $tmp = explode('/',$v['virtual_path']); if(sizeof($tmp) > 1){ $arrays_result[$tmp[0]]['children'][$k]['type'] = 'file'; $arrays_result[$tmp[0]]['children'][$k]['name'] = $v['name']; $arrays_result[$tmp[0]]['type'] = 'dir'; $arrays_result[$tmp[0]]['name'] = $v['name']; } } 

Ottengo un arrays come questo su:

 Array ( [guides] => Array ( [children] => Array ( [0] => Array ( [type] => file [name] => guide_to_printing.txt ) [1] => Array ( [type] => file [name] => guide_to_vpn.txt ) ) [type] => dir [name] => guide_to_vpn.txt ) ) 

So che non è esattamente quello che vuoi, ma penso che possa metterti nella giusta direzione.

Evento anche se avresti dovuto provare qualcosa prima di postare qui, mi piace la tua domanda e penso che sia divertente. Quindi qui vai

 function &createVirtualDirectory(&$structure, $path) { $key_parts = $path ? explode('/', $path) : null; $last_key = &$structure; if (is_arrays($key_parts) && !empty($key_parts)) { foreach ($key_parts as $name) { // maybe directory exists? $index = null; if (is_arrays($last_key) && !empty($last_key)) { foreach ($last_key as $key => $item) { if ($item['type'] == 'dir' && $item['name'] == $name) { $index = $key; break; } } } // if directory not exists - create one if (is_null($index)) { $last_key[] = arrays( 'type' => 'dir', 'name' => $name, 'children' => arrays(), ); $index = count($last_key)-1; } $last_key =& $last_key[$index]['children']; } } return $last_key; } $input = arrays( 0 => arrays ( 'name' => 'guide_to_printing.txt', 'virtual_path' => 'guides/it', ), 1 => arrays( 'name' => 'guide_to_vpn.txt', 'virtual_path' => 'guides/it', ), 2 => arrays( 'name' => 'for_new_employees.txt', 'virtual_path' => 'guides', ) ); $output = arrays(); foreach ($input as $file) { $dir =& createVirtualDirectory($output, $file['virtual_path']); $dir[] = arrays( 'type' => 'file', 'name' => $file['name'] ); unset($dir); } print_r($output); 

Fornisce l'output esatto desiderato

Ecco un modo semplice per farlo con 2 funzioni ricorsive. Una function per analizzare una row di dati. Un altro per unire each row di dati analizzata.

 // Assuming your data are in $data $tree = arrays(); foreach ($data as $item) { $tree = merge($tree, parse($item['name'], $item['virtual_path'])); } print json_encode($tree); // Simple parser to extract data function parse($name, $path){ $parts = explode('/', $path); $level = arrays( 'type' => 'dir', 'name' => $parts[0], 'children' => arrays() ); if(count($parts) > 1){ $path = str_replace($parts[0] . '/', '', $path); $level['children'][] = parse($name, $path); } else { $level['children'][] = arrays( 'type' => 'file', 'name' => $name ); } return $level; } // Merge a new item to the current tree function merge($tree, $new_item){ if(!$tree){ $tree[] = $new_item; return $tree; } $found = false; foreach($tree as $key => &$item) { if($item['type'] === $new_item['type'] && $item['name'] === $new_item['name']){ $item['children'] = merge($item['children'], $new_item['children'][0]); $found = true; break; } } if(!$found) { $tree[] = $new_item; } return $tree; }