PHP и XML - удаление узлов - PullRequest
       17

PHP и XML - удаление узлов

3 голосов
/ 26 февраля 2012

Я создал скрипт PHP, который создает таблицу из XML-документа через AJAX. Например:

<bookstore>
  <book>
    <title>Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book>
    <title>Harry Potter</title>
    <author>J K.  Rowling</author>
    <year>2005</year>    
    <price>29.99</price>
  </book>
</bookstore>

Создает таблицу со столбцами заголовка, автора, года и цены плюс дополнительный столбец удаления. При синтаксическом анализе XML я установил значение tr id равным текущему элементу XML (0 и 1) в приведенном выше случае.

Когда я нажимаю удалить, я запускаю AJAX-запрос с идентификатором строки, которую я хочу удалить. Сценарий удаления получает текущий номер строки без проблем, но у меня возникают странные результаты при попытке удалить его. Текущий код, который я пытаюсь найти ниже (взят из http://quest4knowledge.wordpress.com/2010/09/04/php-xml-create-add-edit-modify-using-dom-simplexml-xpath/ 7.2)

if (isset($_POST['rowNumber'])) {

    $rowNumber = $_POST['rowNumber'];
    $file = $_POST['file'];

    $dom = new DOMDocument();
    $dom->load("../XML/".$file);      
    $xml = $dom->documentElement;
    //PROBLEM HERE
    $xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item($rowNumber));

    $handle = fopen("../XML/".$file, 'w');
    fwrite($handle, $dom->saveXML()); 

}

Я строю таблицу при загрузке страницы, а затем при каждом удалении. Проблема в том, что удаляются неправильные строки, и я не могу понять, почему.

ДОПОЛНИТЕЛЬНЫЕ ИСПЫТАНИЯ ...

Удаляет первый узел при 50% кликов:

$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(0));

Всегда удаляет первый узел:

$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(1));

Удаляет второй узел на 50% кликов:

$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(2));

Всегда удаляет второй узел:

$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(3));

Удаляет третий узел при 50% кликов:

$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(4));

Всегда удаляет третий узел:

$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item(5));

ДОБАВЛЕНО МОЙ AJAX-КОД

$('#generatedTable a.delete').live('click', function (e) {
    e.preventDefault();

//TABLE ROW ID TO BE DELETED. CAN ALERT THIS FINE.
    var trID = $(this).closest('tr').attr('id'); 

    $.ajax({
        url: "functions/xmlDelete.php", 
        type: "POST",
        dataType: "json",
        data: "rowNumber="+ trID + "&fileName=" + fileName,
        success: function(data) {

            $.ajax({
                url: "functions/xmlParser.php", 
                type: "POST",
                dataType: "json",
                data: "fileName="+ fileName,
                success: function(data) {

                    $('#xmlTable').html(data.table);
                    $('#xmlTable').fadeIn('fast');

                    oTable = $('#generatedTable').dataTable({
                        "bJQueryUI": true,
                        "bPaginate": false,
                        "bLengthChange": false,
                        "bFilter": false,
                        "bSort": false,
                        "bInfo": true
                    }); 

                }      
            }); 

        }      
    });         
} );

Вот так выглядят строки моей таблицы, и я могу без проблем предупредить trID.

<tr id="0" class="odd">
  <td id="0">1999 Grammy Nominees</td>
  <td id="1">Many</td>
  <td id="2">USA</td>
  <td id="3">Grammy</td>
  <td id="4">10.20</td>
  <td id="5">1999</td>
  <td align="center"><a class="edit" href="">Edit</a></td>
  <td align="center"><a class="delete" href="">Delete</a></td>
</tr>

Может кто-нибудь помочь объяснить, что я вижу здесь. Спасибо!

Ответы [ 2 ]

2 голосов
/ 27 февраля 2012

Вы не учитываете, что childNodes() включает все узлы, а не только элементы.

Для предоставленного вами XML-документа $dom->documentElement->childNodes->item(0) - это пробел между концом<bookstore> и начало <book>, не первого <book> узла.

Теперь вы знаете, почему DOM так раздражает.

Я предлагаювы используете DOMXPath или SimpleXML вместо циклического перебора childNodes для сбора индексов элементов.

DOMXPath решение

if (isset($_POST['rowNumber'], $_POST['file']) and ctype_digit($_POST['rowNumber'])) {
    $rowNumber = $_POST['rowNumber'];
    $file = '../XML/'.$_POST['file'];

    $dom = new DOMDocument();
    $dom->load($file);

    $root = $dom->documentElement;

    $xp = new DOMXPath($dom);
    $books = $xp->query('*', $root);
    if ($books->item($rowNumber)) {
        $root->removeChild($books->item($rowNumber));
        // Note that "$root->childNodes->item(0)->parentNode" is completely unnecessary.
        // You already have the parent node ($root), so just use it directly!
    } else {
        echo "Row does not exist";
    }

    $dom->save($file);
}

SimpleXML решение

if (isset($_POST['rowNumber'], $_POST['file']) and ctype_digit($_POST['rowNumber'])) {
    $rowNumber = (int) $_POST['rowNumber']; // casting to int is necessary!!
    $file = '../XML/'.$_POST['file'];

    $sxe = simplexml_load_file($file);
    unset($sxe->book[$rowNumber]);

    // or, if you don't want to make element name assumptions:
    // $children = $sxe->children();
    // unset($children[$rowNumber]);

    $sxe->asXML($file);
}

Проблемы

Однако у всего вашего подхода есть две серьезные проблемы.

  1. Вы принимаете ненадежный ввод через переменную post file.Пользователь ничего не читает и не записывает в любой XML-файл на вашем диске.У вас должен быть заранее определенный список действительных file значений, которым $_POST['file'] должно соответствовать, прежде чем вы обработаете запрос.

  2. Вы не учитываете параллелизм.Это проявляется двумя способами:

    1. Если два пользователя редактируют одновременно, а один удаляет книгу 0, то, когда второй удаляет книгу 0, он будет удалять книгу, отличную от него.намеревался , то есть то, что ему кажется книгой 1!Решение этой проблемы заключается в том, что каждой книге нужен явный уникальный идентификатор, который вы используете в файле XML и в своем HTML-интерфейсе.Вы не можете использовать индексную нумерацию для идентификации узлов.
    2. Ваши операции чтения и записи в и из файла XML не являются атомарными, и при этом они не используют блокировку файлов.Таким образом, кто-то может прочитать XML, пока другой процесс PHP находится в процессе записи в файл, предоставляя читателю неполный файл XML.Вы должны либо выполнить атомарную запись (в системах * nix записать в уникальный временный файл и затем link() в реальное имя файла), либо открыть файл с помощью flock() или какой-либо другой механизм блокировки.

Поскольку управление одновременным чтением и записью (особенно быстро) очень сложно, я рекомендую вам отказаться от этого файла, так какПодход к базе данных и использование реальной базы данных.

(И, кстати, ваш HTML недопустим. id атрибуты должны быть уникальными в документе, но у вас есть два id="0".)

2 голосов
/ 27 февраля 2012

Если я правильно понимаю документацию, которую вы правильно связали,

 $library->childNodes->item(0)->parentNode

просто сложный и раздражающий способ приведения DOMDocument к DOMElement.Этот шаблон вы заметите еще в нескольких примерах.

Теперь, когда у нас есть элемент DOMElement, мы можем вызвать на нем метод removeChild (), которому передается элемент DOMElement.Этот элемент

$library->childNodes->item(1)

для второго элемента.

Итак, ваш код, возможно, должен выглядеть следующим образом:

$xml->childNodes->item(0)->parentNode->removeChild($xml->childNodes->item($rowNumber));

Учитывая, что $ rowNumber основан на 0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...