Как сделать иерархию файлов, зная только путь к файлам?
Есть слудующая структура в БД:
> ID;name_file;file_patch;pages
> 1;11.pdf;C:\Documents\2020.02\АК\Документы;15
> 2;12.pdf;C:\Documents\2020.02\АК\Документы\Срочное;26
> 3;13.docx;C:\Documents\2020.02\АК\Документы\Не срочное;35
> 4;555.docx;C:\Documents\2020.02\АК\Приказы;88
> 5;777.docx;C:\Documents\2020.02\АК\Приказы;10
> 6;25.pdf;C:\Documents\2020.02\АК\Справки;1
> 7;54.pdf;C:\Documents\2020.02\АК\Справки;1
> 8;25.pdf;C:\Documents\2020.02\АК\Остальные доки;15
> 9;2323.docx;C:\Documents\2020.02\АК\Остальные доки\Картинки;25
> 10;23.pdf;C:\Documents\2020.02\АК\Остальные доки\Рабочее\хлам;23
Соответственно,
ID каждого файла - он уникальный в БД, и должен использоваться в конечном результате
name_file имя файла (неуникальное имя в БД, но уникально по общему пути/ид),
filepatch - сам путь, где лежит файл,
pages - количество страниц в файле.
Общим является путь сканируемой папки с файлами C:\Documents\2020.02\АК - ее мы обрезаем, и получаем соответствующие названия папок.
А дальше интересное:
Как построить иерархию файлов, чтобы построить иерархию только лишь на основе названий директорий, с вложенностью. Т.е. Вывести название папки, указать что в ней есть перечисленные файлы(с указанием ID и имени файла) и еще какая-то директория (далее перечислить все, что находится уже в этой директории).
Пример того, что должно получиться:
name:'Остальные доки',
value:{ 'flag':'icon-folder', 'id_file':'Остальные доки',},
children:[
{
name:'Рабочее',
value:{'flag':'icon-folder', 'id_file':'Рабочее'},
children:[
{
name:'Хлам',
value:{ 'isTeam' : true , 'flag':'icon-folder', 'id_file':'Хлам'},
children:[
{
name:'документ.docx',
value:{ 'isTeam' : true , 'flag':'icon-doc', 'id_file':'90', 'pages':125},
children:[]
},
{
name:'скан.pdf',
value:{ 'isTeam' : true , 'flag':'icon-pdf', 'id_file':'91', 'pages':125},
children:[]
},
]
},
{
name:'Москва.pdf',
value:{ 'isTeam' : true , 'flag':'icon-pdf', 'id_file':'98', 'pages':125},
children:[]
},
{
name:'Екатеринбург.docx',
value:{ 'isTeam' : true , 'flag':'icon-word', 'id_file':'64', 'pages':125},
children:[]
},
]
},
{
name:'Картинки',
value:{'isTeam' : true ,'flag':'icon-folder', 'id_file':'Картинки'},
children:[
{
name:'Netherlands.pdf',
value:{ 'isTeam' : true , 'flag':'icon-pdf', 'id_file':'22', 'pages':125},
children:[]
},
{
name:'Chile.pdf',
value:{ 'isTeam' : true , 'flag':'icon-pdf', 'id_file':'42', 'pages':125},
children:[]
},
]
}
]
Хранить имя родительской папки в БД (даже в отдельных таблицах) невозможно, т.к. сложность заключается в том, что структура папок может быть абсолютно любая, с любой вложенностью. Также файлы могут удалиться, и тогда производится повторное сканирование директорий. Постоянная директория только C:\Documents\2020.02 Я произвожу только сканирование всех файлов в директориях и заношу в БД. А далее нужно построить дерево.
Приветствую любую помощь: код/мнение/мысли...
Заранее спасибо.
Ответы (2 шт):
Через рекурсию как-то так, наверное. Что положить в value и тд, сам сообразишь, просто передай доп параметром в процедуру то, что тебе необходимо. Нетривиальная задачка кстати :)
<?php
function getTree(&$array, $str) {
$tmp = explode('/', $str);
//стучимся в дно рекурсии, когда в последнем каталоге нет разделителей
if($tmp[0] === $str) {
$array[] = ['name' => $str,
'value' => '???',
'children' => []];
return true;//конец
}
//иначе путь долгий(нет). Для начала проверяем существование обрабатываемой директории
else {
$folder = $tmp[0];//первый фолдер в строке, который нам нужен
$str = str_replace($folder . '/', '', $str); //оставшиеся кусок строки
foreach($array as $index => $child) {
//Мы ее нашли?! Плюхаемся в нее без всякого создания и сомнений
if($child['name'] == $folder) {
getTree($array[$index]['children'], $str);
return true;//еще один конец
}
}
}
//Все варианты исчерпали, то просто создаем каталог
$array[] = ['name' => $folder,
'value' => '???',
'children' => []];
getTree($array[array_key_last($array)]['children'], $str);
}
//собственно сам массив, на выходе древовидного формата,джейсонь его сколько хочешь
$array = ['name' => 'C:/Documents/2020.02/AK/',
'value' => '',
'children' => []
];
//здесь у тебя плюс минус ориентировочные рекорды, которые ты с базы получаешь после предварительной обработки
$queryMySQL = ['Document/pupsik',
'Document/mir/voda/stalin',
'Images/mir/blyat/domik'];
//бежим по рекордам
foreach ($queryMySQL as $str) {
getTree($array['children'],$str);
}
var_dump($array);
Можно и без рекурсии обойтись, но с небольшой ссылочной магией
<?php
// Выборка из БД
$rows = getRows();
// Длина обрезки
$remove = mb_strlen('C:\Documents\2020.02\\');
// Результат
$result = [];
// Ссылки на подкаталоги
$links = [];
// Обходим массив
foreach ($rows as $row) {
// Ссылаемся на корень результирующего массива
$current = &$result;
// Обрезаем путь
$path = mb_substr($row['file_patch'], $remove);
// Обходим папки пути
foreach (explode('\\', $path) as $dir) {
/*
* Если нужной папки нет в результирующем массиве, добавляем.
* Для удобства навигации в качестве ключа будем использовать
* имя папки
*/
if (!array_key_exists($dir, $current)) {
$current[$dir] = [
'name' => $dir,
'value' => [
'flag' => 'icon-folder',
'id_file' => $dir,
],
'children' => [],
];
}
// Добавляем ссылку, чтобы потом ассоциативный массив преобразовать в обычный
$links[] = &$current[$dir];
// Меняем ссылку на нижний уровень для следующей итерации
$current = &$current[$dir]['children'];
}
// добавляем в конечную папку файлик
$current[] = [
'name' => $row['name_file'],
'value' => [
'isTeam' => true,
'flag' => 'icon-' . pathinfo($row['name_file'], PATHINFO_EXTENSION),
'id_file' => $row['ID'],
'pages' => $row['pages'],
],
'children' => [],
];
}
// Проходим по нашим ссылкам и преобразуем их в обычные массивы
foreach ($links as $link) {
$link['children'] = array_values($link['children']);
}
// очищаем указатели ссылки и массив ссылок
unset($current, $link, $links);
// преобразуем результирующий массив в обычный
$result = array_values($result);
// готово
print_r($result);
function getRows()
{
return [
[
'ID' => 1,
'name_file' => '11.pdf',
'file_patch' => 'C:\Documents\2020.02\АК\Документы',
'pages' => 15,
],
[
'ID' => 2,
'name_file' => '12.pdf',
'file_patch' => 'C:\Documents\2020.02\АК\Документы\Срочное',
'pages' => 26,
],
[
'ID' => 3,
'name_file' => '13.docx',
'file_patch' => 'C:\Documents\2020.02\АК\Документы\Не срочное',
'pages' => 35,
],
[
'ID' => 4,
'name_file' => '555.docx',
'file_patch' => 'C:\Documents\2020.02\АК\Приказы',
'pages' => 88,
],
[
'ID' => 5,
'name_file' => '777.docx',
'file_patch' => 'C:\Documents\2020.02\АК\Приказы',
'pages' => 10,
],
[
'ID' => 6,
'name_file' => '25.pdf',
'file_patch' => 'C:\Documents\2020.02\АК\Справки',
'pages' => 1,
],
[
'ID' => 7,
'name_file' => '54.pdf',
'file_patch' => 'C:\Documents\2020.02\АК\Справки',
'pages' => 1,
],
[
'ID' => 8,
'name_file' => '25.pdf',
'file_patch' => 'C:\Documents\2020.02\АК\Остальные доки',
'pages' => 15,
],
[
'ID' => 9,
'name_file' => '2323.docx',
'file_patch' => 'C:\Documents\2020.02\АК\Остальные доки\Картинки',
'pages' => 25,
],
[
'ID' => 10,
'name_file' => '23.pdf',
'file_patch' => 'C:\Documents\2020.02\АК\Остальные доки\Рабочее\хлам',
'pages' => 23,
],
];
}