public function actionImport()
 {
     $import_data = CRequest::getString("export_data");
     // $import_data = stripsplashes($import_data);
     $import_data = unserialize($import_data);
     /**
      * Первый проход
      * Добавляем или обновляем описатели без учета иерархии
      */
     foreach ($import_data as $field_arr) {
         $field = CPrintManager::getField($field_arr["alias"]);
         if (is_null($field)) {
             $field = new CPrintField();
             $field->alias = $field_arr["alias"];
         }
         /**
          * Обновляем данными из массива
          */
         $field->description = $field_arr["description"];
         $field->value_evaluate = $field_arr["value_evaluate"];
         $field->parent_node = $field_arr["parent_node"];
         $field->title = $field_arr["title"];
         /**
          * Привязываем к набору форм
          */
         $formset = CPrintManager::getFormset($field_arr["formset"]);
         if (is_null($formset)) {
             trigger_error("Не могу найти набор форм " . $field_arr["formset"] . ". Продолжение невозможно", E_USER_ERROR);
         }
         $field->formset_id = $formset->getId();
         $field->save();
     }
     /**
      * Второй проход
      * Выстраиваем иерархию
      */
     foreach ($import_data as $field_arr) {
         if (array_key_exists("parent", $field_arr)) {
             $parent = CPrintManager::getField($field_arr["parent"]);
             if (is_null($parent)) {
                 trigger_error("Не могу найти описатель с псевдонимом " . $field_arr["parent"] . ". Импортируйте сначала его", E_USER_ERROR);
             }
             $field = CPrintManager::getField($field_arr["alias"]);
             $field->parent_id = $parent->getId();
             $field->save();
         }
     }
     /**
      * Возвращаемся обратно
      */
     $this->redirect("?action=index");
 }
 private function processNode(DOMNode $node, CPrintField $field, $object, CPrintForm $form)
 {
     $doc = $node->ownerDocument;
     /**
      * Определим поля двух типов: простые и сложные. Простые нужны для
      * вывода информации, сложные для вывода частей документа по шаблону,
      * например, печать билетов - в документе указывается шаблон одного билета,
      * а затем он размножается на все билеты. Простые поля не содержат подполей,
      * это будет первым отличительным признаком, по нему и разделим.
      */
     if ($field->children->getCount() == 0) {
         /**
          * Это простое поле. Простые поля бывают трех типов:
          * 1. Просто вывод текста (по умолчанию)
          * 2. Вывод таблицы
          */
         if ($field->type_id == "1" || $field->type_id == "0") {
             $debug = array();
             /**
              * Это вывод просто текста. Мы заменяем DOMNode текстом, который
              * получается при вычислении данного описателя
              */
             $parent = $node->parentNode;
             /**
              * Собираем отладочную инфу
              */
             $debug[0] = $doc->saveXML($parent);
             /**
              * Заменяем пользовательское поле просто текстовой нодой
              */
             // echo $field->alias.", ";
             try {
                 $newNode = $doc->createTextNode($field->evaluateValue($object));
             } catch (Exception $e) {
                 echo "Возникла ошибка " . $e->getMessage() . " в поле " . $field->alias;
             }
             $parent->replaceChild($newNode, $node);
             /**
              * На случай отладки показываем что было до и что стало после
              */
             if ($this->_isDebug) {
                 $debug[1] = $doc->saveXML($parent);
                 var_dump($debug);
             }
             /**
              * Сохраняем
              */
             return $doc->saveXML();
         } elseif ($field->type_id == "2") {
             $debug = array();
             /**
              * Это вывод таблицы. Описатель поля должен стоять в первой ячейке
              * нужной таблицы
              *
              * Для начала нам нужно определить строку в таблице, в которой стоит
              * описатель
              */
             $row = $node->parentNode;
             while ($row->localName !== "table-row") {
                 $row = $row->parentNode;
             }
             /**
              * Получаем таблицу, к которой привязана эта строка.
              * Именно с таблицей мы и будем работать. Попутно собираем
              * отладочную инфу
              */
             $table = $row->parentNode;
             $debug[0] = $doc->saveXML($table);
             /**
              * Просматриваем дочерние элементы полученной строки.
              * Если среди них есть пользовательский описатель поля, то
              * удаляем его
              */
             $this->removeChildsByName($row, "user-field-get");
             $debug[1] = $doc->saveXML($table);
             /**
              * Теперь ищем все ячейке в выбранной строке. Мы будем их клонировать
              * и заполнять данными из результирующего массива
              */
             $arr = $field->evaluateValue($object);
             foreach ($arr as $data_row) {
                 $i = 0;
                 /**
                  * Клонируем исходную строку
                  */
                 $newRow = $row->cloneNode(true);
                 foreach ($newRow->childNodes as $newCell) {
                     /**
                      * Получим значение, которое мы должны будем положить в
                      * ячейку новой таблицы
                      */
                     $cellValue = "";
                     if (array_key_exists($i, $data_row)) {
                         $cellValue = $data_row[$i];
                     }
                     /**
                      * В ячейке таблице получем элемент с локальным именем p -
                      * в него и нужно добавлять текст
                      */
                     $p = $this->getFirstChildByName($newCell, "p");
                     if (!is_null($p)) {
                         $cellText = $doc->createTextNode($cellValue);
                         $p->appendChild($cellText);
                     }
                     $i++;
                 }
                 /**
                  * Добавляем новую строку перед исходной, она будет
                  * постепенно двигаться вниз, а затем мы ее удалим
                  */
                 $table->insertBefore($newRow, $row);
             }
             /**
              * Удаляем из таблицы исходную строку
              */
             $table->removeChild($row);
             if ($this->_isDebug) {
                 $debug[2] = $doc->saveXML($table);
                 var_dump($debug);
             }
             /**
              *  Сохраняем
              */
             return $doc->saveXML();
         }
     } else {
         /**
          * Это место, где вычисляются групповые описатели.
          * Посмотрим-с
          */
         $items = $field->evaluateValue($object);
         /**
          * Сразу отрубаем, если описатель возвращает не тот тип
          * данных. Можно было бы тянуть вторую часть else, но
          * как-то совсем не хочется.
          */
         if (strtoupper(get_class($items)) !== "CARRAYLIST") {
             trigger_error("Групповой описатель items должен возвращать объект типа CArrayList с дочерними элементами, сейчас там " . get_class($items), E_USER_ERROR);
             var_dump($field);
             exit;
         }
         /**
          * Ищем родительский элемент указанного типа, в котором
          * стоит наш групповой описатель. Когда найдем это будет
          * нода, которую будем множить для каждого элемента
          * массива с данными
          */
         $nodeType = $field->parent_node;
         $parentNode = $node->parentNode;
         while ($parentNode->localName !== $nodeType) {
             $parentNode = $parentNode->parentNode;
         }
         /**
          * Теперь для каждого элемента из результатов выполнения
          * родительского описателя будем делать копию родительской
          * ноды, извлекать внедренные в нее описатели и вычислять.
          */
         foreach ($items->getItems() as $item) {
             $debug = array();
             /**
              * Делаем копию ноды для текущего элемента
              */
             $newNode = $parentNode->cloneNode(true);
             $debug[0] = $doc->saveXML($newNode);
             /**
              * Теперь выделяем описатели
              */
             $childNodes = $this->getElementsByName($newNode, "user-field-get");
             /**
              * Удаляем из списка описателей родительский описатель,
              * он там стоял для того, чтобы указывать на начало
              * блока групповых описателей
              */
             foreach ($childNodes as $key => $node) {
                 if ($key == $field->alias) {
                     unset($childNodes[$key]);
                     $node->parentNode->removeChild($node);
                 }
             }
             /**
              * Теперь вычисляем каждый описатель
              */
             foreach ($childNodes as $fieldName => $node) {
                 if (!is_null($form->formset->getFieldByName($fieldName))) {
                     $childField = $form->formset->getFieldByName($fieldName);
                     $this->processNode($node, $childField, $item, $form);
                 }
             }
             $debug[1] = $doc->saveXML($newNode);
             /**
              * Добавим перед родительской нодой полученную
              */
             $parentNode->parentNode->insertBefore($newNode, $parentNode);
             /**
              * Показываем отладочную информацию, если это необходимо
              */
             if ($this->_isDebug) {
                 var_dump($childNodes);
                 var_dump($debug);
             }
         }
         /**
          * Удаляем из документа исходный узел с шаблоном
          */
         $parentNode->parentNode->removeChild($parentNode);
         /**
          * Сохраняем результаты
          */
         return $doc->saveXML();
     }
 }