• Как отфильтровать дублирующие поля записей, выгруженные из БД MySQL для построения DOMDocument с целью последующего сохранения в XML-файл?

    @mobcomm Автор вопроса
    Итоговое решение в результате доработки исходного кода:
    //Создание 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</Формат>
        </Свойства>
        <Разделы>
          <Раздел>МФУ</Раздел>
          <Раздел>Принтеры</Раздел>
        </Разделы>
      </Товар>
      ...
    </Товары>
    Ответ написан
    Комментировать