Парсинг таблицы xml в sql [закрыт]

110
08 ноября 2019, 09:30

Программа формирует xml файл, подобного формата.

<?xml version="1.0" encoding="windows-1251"?> 
<timetable> 
   <teachers options="import:disable,canadd,canremove,canupdate,primarytt,silent" columns="id,name"> 
      <teacher id="*1" name="Ivanov Ivan"/> 
      <teacher id="*2" name="Petrovskii Vasilii"/> 
      <teacher id="*3" name="Sidorova Maria"/> 
   </teachers> 
   <classes options="import:disable,canadd,canremove,canupdate,primarytt,silent" columns="id,name"> 
      <class id="*1" name="11a"/> 
      <class id="*2" name="10b"/> 
      <class id="*3" name="8a"/> 
   </classes> 
   <subjects options="import:disable,canadd,canremove,canupdate,primarytt,silent" columns="id,name"> 
      <subject id="*1" name="Matematika"/> 
      <subject id="*2" name="Fizica"/> 
      <subject id="*3" name="Himia"/> 
   </subjects> 
   <classrooms options="import:disable,canadd,canremove,canupdate,primarytt,silent" columns="id,name"> 
      <classroom id="*1" name="cab 1"/> 
      <classroom id="*2" name="cab 22"/> 
      <classroom id="*3" name="cab 6"/> 
   </classrooms> 
   <cards options="import:disable,canadd,canremove,canupdate,primarytt,silent" columns="day,period,subjectid,teacherids,classroomids"> 
      <card subjectid="*1" teacherids="*1" classroomids="*3" day="2" period="1"/> 
      <card subjectid="*2" teacherids="*3" classroomids="2" day="3" period="4"/> 
      <card subjectid="*3" teacherids="*2" classroomids="*2" day="2" period="3"/> 
   </cards> 
</timetable>

Каким образом можно спарсить последнюю таблицу "cards" в sql, при этом чтобы место ид, были значения этих элементов?

Answer 1

Первое, что вам надо необходимо сделать - загрузить этот XML. Тут все просто:

$xml = simplexml_load_file("data.xml");

Далее определиться какие столбцы (атрибуты) вас интересуют. Либо вы уже знаете набор, либо вы хотите использовать те, что указаны в атрибуте columns:

$cols = explode(',', (string)$xml->cards['columns']);
// $cols = ['subjectid', 'teacherids'];               //если знаете

Далее XML узлы card нужно перевести в вид массива. Определим для этого функцию, которая вычленит нам только нужные атрибуты:

$map = function($card) use ($cols){
            $result = [];
            foreach($cols as $c){
                $result[$c] = (string)$card[$c];
            }
            return $result;
        };

У вас там в ключах звездочки присутствуют, будем считать, что атрибуты строковые. Иначе приводили бы к (int), например.

Далее пройдемся по элементам и построим массив данных:

$data = [];
foreach($xml->cards->card as $card){
    $data[] = $map($card);
}

на данном этапе получим массив вида

 [0] => Array (
        [day] => 2
        [period] => 1
        [subjectid] => *1
        [teacherids] => *1
        [classroomids] => *3
    )
 ........

Теперь необходимо подключиться к БД, используйте, например PDO. Также нужен текст SQL запроса для вставки данных. Опять же, либо мы знаем какие у нас столбцы и сколько значений, либо нет. В случае, когда не знаем, используем нечто вида:

 $sql = "insert into xxx (".
                implode(',', $cols) .
            ")\n values (".
                implode(',', array_fill( 0, count($cols), '?')) .
            ");";

Если же знаем, то просто пишем запрос а-ля

$sql = "INSERT INTO xxx (subjectid, teacherids) VALUES (?, ?)";

Обратите внимание, что в общем случае наименования столбцов должны быть экранированы, в случае mysql это обратные кавычки, для MS SQL - квадратные скобки [], и т.д. Либо убедиьтся, что переданные строки - валидные идентификаторы названий столбцов и входят в ваш "белый список" столбцов.

На этом шаге получаем следующий текст запроса:

insert into xxx (day,period,subjectid,teacherids,classroomids)
   values (?,?,?,?,?);

Теперь подготавливаем запрос, и выполняем его для каждого элемента массива:

$pdo = new PDO(...);
$st = $pdo->prepare($sql);
foreach($data as $d){
    $st->execute($d);
}

Здесь стоит отметить, что для относительно большого объема данных выполнения отдельного запроса на каждую вставку - плохая практика. Лучше будет разбить массив $data на части, элементов по 300-500 и использовать вариант запроса на вставку сразу нескольких строк insert ... values (...), (...), (...).
Да в целом плохо в любом случае, но в рамках данного вопроса оставим так.

Ну а в целом, если XML документ достаточно большой, мегабайт 50 уже будет достаточно, чтобы Simple XML уже слег на загрузке файла. В подобных случаях используют SAX-парсеры, которые не загружают весь документ в память.

дополнение. пропустил часть про значения.

в общем говоря, считаем что поля список полей нам известен. Опредялем функции для загрузки справочников и маппинга карточек.

$loadRefs = function($xml, $refs){
                $result = [];
                foreach($refs as $r ){
                    foreach($xml->$r->children() as $entry){
                        $id = (string)$entry['id'];
                        $result[$r][$id] = (string)$entry['name'];
                    }
                }
                return $result;
            };
$map = function($card) use ($refs){
            return [
                'day' => (int)$card['day'],
                'period' => (int) $card['period'],
                'subject' => $refs['subjects'][ (string)$card['subjectid'] ],
            ];
        };

вызываем их и вносим результат в базу, как и ранее

$refs = $loadRefs($xml, ['classes', 'teachers', 'subjects']);
$data = [];
foreach($xml->cards->card as $card){
    $data[] = $map($card);
}
$pdo = new PDO(...);
$sql = "INSERT INTO xxx (`day`, period, subject) VALUES (?,?,?);";
$st = $pdo->prepare($sql);
foreach($data as $d){
    $st->execute($d);
}
READ ALSO
WP исключить стиль из футера

WP исключить стиль из футера

Есть код для подключения стилей и скриптов из header в footer для оптимизации

114
Оплата в зависимости от города [закрыт]

Оплата в зависимости от города [закрыт]

Как можно узнать способы оплаты для клиентов, в определенном городе,как здесь

147
Как лучше останавливать скрипт?

Как лучше останавливать скрипт?

есть скрипт, который необходимо останавливать по клику кнопки отмены

117