Array
(
[id] => 517
[Код] => 201
[Название] => Бумага А4
[Тип] => Базовая
[Цена] => 11.50
[Свойства] => Плотность
[значение свойства] => 100
[Раздел] => Бумага
)
Array
(
[id] => 517
[Код] => 201
[Название] => Бумага А4
[Тип] => Москва
[Цена] => 12.50
[Свойства] => Плотность
[значение свойства] => 100
[Раздел] => Бумага
)
Array
(
[id] => 517
[Код] => 201
[Название] => Бумага А4
[Тип] => Базовая
[Цена] => 11.50
[Свойства] => Белизна ЕдИзм=%
[значение свойства] => 100
[Раздел] => Бумага
)
Array
(
[id] => 517
[Код] => 201
[Название] => Бумага А4
[Тип] => Москва
[Цена] => 12.50
[Свойства] => Белизна ЕдИзм=%
[значение свойства] => 100
[Раздел] => Бумага
)
Array
(
[id] => 518
…
<?xml version="1.0" encoding="windows-1251"?>
<Товары>
<Товар Код="201" Название="Бумага А4">
<Цена Тип="Базовая">11.50</Цена>
<Цена Тип="Москва">12.50</Цена>
<Цена Тип="Базовая">11.50</Цена>
<Цена Тип="Москва">12.50</Цена>
</Товар>
<Товар Код="202" Название="Бумага А3">
<Цена Тип="Базовая">18.50</Цена>
<Цена Тип="Москва">22.50</Цена>
<Цена Тип="Базовая">18.50</Цена>
<Цена Тип="Москва">22.50</Цена>
</Товар>
…
</Товары>
<?xml version="1.0" encoding="windows-1251"?>
<Товары>
<Товар Код="201" Название="Бумага А4">
<Цена Тип="Базовая">11.50</Цена>
<Цена Тип="Москва">12.50</Цена>
<Свойства>
<Плотность>100</Плотность>
<Белизна ЕдИзм="%">150</Белизна>
</Свойства>
<Разделы>
<Раздел>Бумага</Раздел>
</Разделы>
</Товар>
<Товар Код="202" Название="Бумага А3">
<Цена Тип="Базовая">18.50</Цена>
<Цена Тип="Москва">22.50</Цена>
<Свойства>
<Плотность>90</Плотность>
<Белизна ЕдИзм="%">100</Белизна>
</Свойства>
<Разделы>
<Раздел>Бумага</Раздел>
</Разделы>
</Товар>
…
</Товары>
<?php
//Подключение к серверу БД
…
} else {
$result = mysqli_query(
$connect,
"SELECT a_product.id, a_product.код AS `Код`, a_product.название AS `Название`, a_price.`тип цены` AS `Тип`, a_price.`цена` AS `Цена`, a_property.`свойство` AS `Свойства`, a_property.`значение свойства`, a_category.`название`AS `Раздел`
FROM a_product
INNER JOIN a_price ON название = a_price.товар
INNER JOIN a_property ON a_product.название = a_property.товар
INNER JOIN a_category ON a_product.код = a_category.код
;");
};
//Создание DOM-документа
$dom = new DOMDocument('1.0', 'windows-1251'); //создал дескриптор на новый DOM-документ (объект)
$dom->formatOutput = True; //Форматирует вывод, добавляя отступы и дополнительные пробелы. Не работает, если документ был загружен с включённым параметром preserveWhitespace. https://www.php.net/manual/ru/class.domdocument.php
//Создание корневого элемента (узла) <ТОВАРЫ>
$root = $dom->createElement("Товары"); //создаем корневой элемент
$dom->appendChild($root); //присоединяем элемент к корню документа
//===================================================================================
//Функция создания элементов/узлов второго уровня (дочерние по отношению к <Товар>)
//(на входе: $dom, $node, $child, Имя узла ($tempNameElement), Текстовое содержимое узла ($tempCreate = NULL), Имя атрибута ($tempSetNameAttribute = NULL), Значение атрибута ($tempSetAttribute = NULL))
function addChild2($dom, $node, $tempNameElement, $tempCreate = NULL, $tempSetNameAttribute = NULL, $tempSetAttribute = NULL)
{
$child = $dom->createElement($tempNameElement); //создали <элемент>
$node->appendChild($child); //добавили <элемент> к элементу <Товар> в качестве дочернего
if($tempCreate !== NULL) {
$child->appendChild($dom->createTextNode($tempCreate)); //(вывели текст в элементе <>...</>) создали дочерний Текстовый узел для текущего узла.
}
if($tempSetNameAttribute != NULL && $tempSetAttribute != NULL) {
$child->setAttribute($tempSetNameAttribute,$tempSetAttribute); //присваиваем атрибут "тип цены" элементу <Цена>
}
return $child;
};
//===================================================================================
//===================================================================================
//Функция создания элементов/узлов третьего уровня
//(на входе: $dom, $node, $child, Имя узла ($tempNameElement), Текстовое содержимое узла ($tempCreate = NULL), Имя атрибута ($tempSetNameAttribute = NULL), Значение атрибута ($tempSetAttribute = NULL))
function addChild3($dom, $node, $child, $tempNameElement, $tempCreate = NULL, $tempSetNameAttribute = NULL, $tempSetAttribute = NULL)
{
$child2 = $dom->createElement($tempNameElement); //создали <элемент>
$child->appendChild($child2); //добавили <элемент> к элементу <...> в качестве дочернего
if($tempCreate !== NULL) {
$child2->appendChild($dom->createTextNode($tempCreate)); //(вывели значение элемента в теге <>...</>) создали дочерний Текстовый узел для текущего узла В КОНЕЦ СПИСКА.
}
if($tempSetNameAttribute != NULL && $tempSetAttribute != NULL) {
$child2->setAttribute($tempSetNameAttribute,$tempSetAttribute); //присваиваем атрибут (наименование="значение") текущему дочернему элементу
}
return $child2;
};
//=========================================================================================================
//ЛОГИКА. Получаем записи из БД MySQL и формируем из них дерево DOM
$i = 0; //инициализация индекса массива $rows[$i] (полученных из БД MySQL записей)
$elements = []; //Массив $node (узлов DOM)
$rows = []; //Массив полученных из БД MySQL записей
while($row = $result->fetch_assoc()) { //получение записей из таблицы 'a_product' БД MySQL
//ДЛЯ ОТЛАДКИ - УДАЛИТЬ !!!
echo '<pre>';
print_r($row);
echo '</pre>';
//Если такой ID пока не сохранен в массиве узлов $elements[] (избавляемся от дублей по ID)
if(FALSE === ($key = array_search($row['id'], array_column($rows, 'id')))) { //$key - индекс строки в массиве записей из БД MySQL ($rows[]) проверяем на FALSE, потому что array_search возвращает FALSE или ключ найденного элемента
$rows[$i] = $row; //массив записей из БД MySQL
//создаем элементы <Товар> (получаем наименование для элемента <Товар> из функци fetch_assoc() в вышестоящем цикле while пока не закончатся записи из БД)
$node = $dom->createElement('Товар'); //создаем элемент <Товар> (дочерний элемент корневого элемента "Товары")
$root->appendChild($node); //и добавляем его в корневой элемент <Товары>
$elements[$i] = $node; //Массив узлов
//Атрибуты <ТОВАР>
$elements[$i]->setAttribute("Код", $rows[$i]['Код']); //присваиваем атрибуты 'код' и 'название' элементу <Товар>
$elements[$i]->setAttribute("Название", $rows[$i]['Название']); //присваиваем атрибуты 'код' и 'название' элементу <Товар>
//<ЦЕНА> - узел 1 уровня
addChild2($dom, $elements[$i], 'Цена', $rows[$i]['Цена'], 'Тип', $rows[$i]['Тип']);
$i++; //увеличиваем индекс
}else{ //если <ТОВАР> есть в массиве записей из БД MySQL ($rows[]) (и, соответственно, есть $key с ключом его ID, а также есть соответствующий в массиве узлов ($elements[]))- отсеиваем дубли по атрибутам и проводим запись
//<ЦЕНА> - узел 1 уровня
foreach ($elements[$key]->childNodes as $childN) { //Перебираем в массиве узлов ($elements[]) детей найденого в массиве записей из БД MySQL ($rows[]) узла <ТОВАР>
if($childN->nodeName == 'Цена') { //Если атрибут тега <ЦЕНА> не совпадает с атрибутом текущей (проверяемой) записи из БД MySQL ($rows[])
if($childN->getAttribute('Тип') == $rows[$i]['Тип']) {
$addPrice = FALSE;
break;
} else {
$addPrice = TRUE;
}
}
}
//Результат проверки == FALSE или TRUE
if($addPrice === TRUE) {
addChild2($dom, $elements[$key], 'Цена', $row['Цена'], 'Тип', $row['Тип']); // то записываем в найденный в массиве узлов ($elements[]) узел <ТОВАР> - новый узел <ЦЕНА>
}
//}
}
};
$dom->save( 'target.xml' ); //сохранение объекта (DOM-документа) в файл.
…
<?php
$inputData = 'SELECT ....'; //Получаем данные из БД
$outputData = [];
foreach ($inputData as $row){//Собираем данные в нужном формате
if(!isset($outputData[ $row['Код'] ])){//Собираем основыне данные
$outputData[ $row['Код'] ]['Свойства'] = $row['Свойства'];
$outputData[ $row['Код'] ]['Раздел'] = $row['Раздел'];
}
$outputData[ $row['Код'] ]['Цены'][] = [//Собираем цены
'Цена' => $row['Цена'],
'Тип' => $row['Тип']
];
}
unset($inputData);//Удаляем оригинал данных
//По итогу перебираем $outputData и формируем из имеющегося xml
echo '<Товары>';
foreach ($outputData as $key => $row){//Тут уже под свои нужды пишем =)
echo '<Товар Код="'.$key.'">';
foreach ($row as $rowInner){
echo '<Цена Тип="'.$rowInner['Тип'].'">'.$rowInner['Цена'].'</Цена>';
}
/*
* ...
* ...
*/
echo '</Товар>';
}
echo '</Товары>';
//Создание DOM-документа
$dom = new DOMDocument('1.0', 'windows-1251');
$dom->formatOutput = True;
//Создание корневого элемента <ТОВАРЫ>
$root = $dom->createElement("Товары");
$dom->appendChild($root);
//=========================================================================================================
//Функция создания элементов/узлов второго уровня (дочерние по отношению к <Товар>)
function addChild2($dom, $node, $tempNameElement, $tempCreate = NULL, $tempSetNameAttribute = NULL, $tempSetAttribute = NULL)
{
$child = $dom->createElement($tempNameElement); //создали <элемент>
$node->appendChild($child); //добавили <элемент> к элементу <Товар> в качестве дочернего
if($tempCreate !== NULL) {
$child->appendChild($dom->createTextNode($tempCreate)); //(вывели текст в элементе <>...</>) создали дочерний Текстовый узел для текущего узла.
}
if($tempSetNameAttribute != NULL && $tempSetAttribute != NULL) {
$child->setAttribute($tempSetNameAttribute,$tempSetAttribute); //присваиваем атрибут "тип цены" элементу <Цена>
}
return $child;
};
//=========================================================================================================
//Функция создания элементов/узлов третьего уровня
function addChild3($dom, $node, $child, $tempNameElement, $tempCreate = NULL, $tempSetNameAttribute = NULL, $tempSetAttribute = NULL)
{
$child2 = $dom->createElement($tempNameElement); //создали <элемент>
$child->appendChild($child2); //добавили <элемент> к элементу <...> в качестве дочернего
if($tempCreate !== NULL) {
$child2->appendChild($dom->createTextNode($tempCreate)); //вывели значение элемента в теге <>...</>
}
if($tempSetNameAttribute != NULL && $tempSetAttribute != NULL) {
$child2->setAttribute($tempSetNameAttribute,$tempSetAttribute); //присваиваем атрибут (наименование="значение") текущему дочернему элементу
}
return $child2;
};
//=========================================================================================================
//Функция проверки элементов на дубли
function checkDuplicate2($childKey, $checkMassive, $row)
{
//Перебираем в массиве узлов ($elements[$key]) детей найденного в массиве записей из БД MySQL ($rows[]) узла <ТОВАР>
foreach($checkMassive->childNodes as $childN) { //на каждой итерации цикла проверяем один очередной дочерний узел узла <Товар> (например: Товар->Цена или Товар->Свойства или Товар->Разделы)
//Проверка на дубли по наименованию узла
switch ($childKey) {
case 'Цена':
if($childN->getAttribute('Тип') == $row['Тип']) { //проверяем атрибут 'Тип' узла <Цена>
$add = FALSE; //Индикатор записи (если записывали в массив ранее)
break 2; //выходим из switch и из foreach.
} else {
$add = TRUE;
break; //выходим только из switch
}
case 'Свойства':
if('Свойства' == $childN->nodeName) {
$add = FALSE; //Индикатор записи (если записывали в массив ранее)
break 2; //выходим из switch и из foreach.
} else {
$add = TRUE;
break; //выходим только из switch
}
//case 'Разделы':
case 'Раздел':
if('Разделы' == $childN->nodeName) {
$add = FALSE; //Индикатор записи (если записывали в массив ранее)
break 2; //выходим из switch и из foreach.
} else {
$add = TRUE;
break; //выходим только из switch (так как требуется продолжать проверку по всем детям текущего узла, пока не будет совпадение или конец списка)
}
}
}
return $add;
}
//=========================================================================================================
//Функция проверки элементов 3го уровня на дубли
function checkDuplicate3($childKey, $childValue, $checkMassive, $row)
{
//Перебираем в массиве узлов ($elements[$key]) детей найденного в массиве записей из БД MySQL ($rows[]) узла <ТОВАР>
foreach($checkMassive->childNodes as $childN) { //на каждой итерации цикла проверяем один очередной дочерний узел узла <Товар> (например: Товар->Цена)
foreach($childN->childNodes as $childValue3) { //<Разделы> -> <Раздел> или...
switch ($childKey) {
case 'Раздел':
if($childValue == $childValue3->nodeValue) { //$childValue3->nodeValue -это значение (содержимое) узла <Раздел>
$add = FALSE; //Индикатор записи (если записывали в массив ранее)
break 3; //выходим из switch и из обоих foreach.
} else {
$add = TRUE;
break; //выходим только из switch (так как требуется продолжать проверку по всем детям текущего узла, пока не будет совпадение или конец списка)
}
case 'Свойства':
if($childValue == $childValue3->nodeName && $row['значение свойства'] == $childValue3->nodeValue) { //$childValue3->nodeName -это наименование (ключ) дочернего узла <Свойства>, а $childValue3->nodeValue -значение (содержимое) этого дочернего узла
$add = FALSE;
break 3; //выходим из switch и из обоих foreach.
} else {
$add = TRUE;
break; //выходим только из switch
}
}
}
}
return $add;
}
//=========================================================================================================
//ЛОГИКА. Получаем записи из таблиц и формируем из них дерево DOM
$i = 0; //инициализация индекса Массива $rows[$i] (полученных из БД MySQL записей)
$elements = []; //Массив $node (узлов DOM)
$rows = []; //Массив полученных из БД MySQL записей
while($row = $result->fetch_assoc()) { //получение записей из таблиц БД MySQL
//Если такой ID пока не сохранён в Массиве записей из БД MySQL (избавляемся от дублей по ID)
if(FALSE === ($key = array_search($row['id'], array_column($rows, 'id')))) { //$key - индекс строки в Массиве записей из БД MySQL ($rows[])
$rows[$i] = $row; //Массив записей из БД MySQL
//Создаем элемент <Товар>
$node = $dom->createElement('Товар');
$root->appendChild($node);
$elements[$i] = $node; //Массив узлов
//Получаем наименования для дочерних узлов узла <Товар> из функции fetch_assoc()
//АТРИБУТЫ <ТОВАРА>
$elements[$i]->setAttribute("Код", $rows[$i]['Код']); //присваиваем атрибуты 'код' и 'название' элементу <Товар>
$elements[$i]->setAttribute("Название", $rows[$i]['Название']);
//<ЦЕНА> - узел 1 уровня
addChild2($dom, $elements[$i], 'Цена', $row['Цена'], 'Тип', $row['Тип']);
$i++; //увеличиваем индекс
}else{ //Если <ТОВАР> есть в Массиве записей из БД MySQL ($rows[]) (и, соответственно, есть $key с ключом его ID, а также есть соответствующий в Массиве узлов ($elements[]))
foreach ($row as $childKey => $childValue) { //на каждой итерации цикла проверяем один очередной элемент записи из БД MySQL (например: $row['Цена'] или $row['Свойства'] или $row['Раздел'])
$add = checkDuplicate2($childKey, $elements[$key], $row);
//Результат проверки == FALSE или TRUE для узлов 2-го уровня(<ЦЕНА> или <Свойства> или <Раздел>)
if($add === TRUE) {
if($childKey == 'Цена') {
addChild2($dom, $elements[$key], $childKey, $childValue, 'Тип', $row['Тип']); //то записываем в найденный в Массиве узлов ($elements[]) узел <ТОВАР> - новый узел <ЦЕНА>
}
if($childKey == 'Раздел') {
$childR = addChild2($dom, $elements[$key], 'Разделы');
}
if($childKey == 'Свойства') {
$childP = addChild2($dom, $elements[$key], 'Свойства');
}
}
//Результат проверки == FALSE или TRUE для узлов 3-го уровня(<Плотность> или <Белизна> или <Формат> или <Тип> или <Раздел>)
if($childKey == 'Раздел') {
$add3 = checkDuplicate3($childKey, $childValue, $elements[$key], $row);
if($add3 === TRUE) {
$child2 = addChild3($dom, $elements[$key], $childR, $childKey, $childValue);
}
}
if($childKey == 'Свойства') {
$add4 = checkDuplicate3($childKey, $childValue, $elements[$key], $row);
if($add4 === TRUE) {
$child2 = addChild3($dom, $elements[$key], $childP, $childValue, $row['значение свойства']);
}
}
} //end foreach
}
};
foreach ($root->childNodes as $q) {
if ($q->lastChild->nodeName == $q->firstChild->nodeName) {
//public DOMNode::replaceChild ( DOMNode $node , DOMNode $child ) : DOMNode|false. Возвращает старый узел или false в случае возникновения ошибки.
$w = $q->replaceChild($q->lastChild, $q->childNodes[1]); //функция DOMNode::replaceChild заменяет дочерний узел child новым узлом.
$q->appendChild($w);
$w = $q->replaceChild($q->lastChild, $q->childNodes[2]);
$q->appendChild($w);
}
}
$dom->save( 'target.xml' ); //сохранение объекта (DOM-документа) в файл.
//Закрываем подключение к серверу БД
mysqli_close($connect);
<?xml version="1.0" encoding="windows-1251"?>
<Товары>
<Товар Код="201" Название="Бумага А4">
<Цена Тип="Базовая">11.50</Цена>
<Цена Тип="Москва">12.50</Цена>
<Свойства>
<Плотность>100</Плотность>
<Белизна>100</Белизна>
</Свойства>
<Разделы>
<Раздел>Бумага</Раздел>
</Разделы>
</Товар>
<Товар Код="302" Название="Принтер Canon">
<Цена Тип="Базовая">3010.00</Цена>
<Цена Тип="Москва">3500.00</Цена>
<Свойства>
<Тип>Лазерный</Тип>
<Формат>A4</Формат>
<Формат>A3</Формат>
</Свойства>
<Разделы>
<Раздел>МФУ</Раздел>
<Раздел>Принтеры</Раздел>
</Разделы>
</Товар>
...
</Товары>