Как правильно заменить встроенные изображения на стилизованные плавающие div с помощью PHP DOMDocument? - PullRequest
3 голосов
/ 06 июля 2011

Я застрял в следующей проблеме и хотел бы знать, если вы получили совет.

Редактор WYSIWYG позволяет пользователю загружать и вставлять изображения.Тем не менее, мои пользователи в основном ученые, но они не знают, как использовать HTML или даже как правильно изменять размер изображения для веб-страницы.Вот почему я автоматически изменяю размеры изображений на стороне сервера до миниатюр и полного размера.При нажатии на миниатюру откроется лайтбокс с полным изображением.

Редактор WYSIWYG выбрасывает изображения в теги <p>, как показано ниже (см. Последний абзац):

<p>Intro Text</p>
<ul>
   <li>List point 1</li>
   <li>List point 2</li>
</ul>
<p>Some text before an image. 
   <img alt="Slide 1" src="/files/slide1.png" /> 
   Maybe some text in between, nobody knows what the scientists are up to. 
   <img alt="Slide 2" src="/files/slide2.png" /> 
   And even more text right after that.
</p>

Что бы я хотелдля этого нужно получить изображения из тегов <p> и добавить их перед соответствующим абзацем в плавающем <div> s:

<p>Intro Text</p>
<ul>
   <li>List point 1</li>
   <li>List point 2</li>
</ul>
<div class="custom">
   <a href="/files/fullview/slide1.png" rel="lightbox[group][Slide 1]">
      <img src="/files/thumbs/files/slide1.png" />
   </a>
</div>
<div class="custom">
   <a href="/files/fullview/slide2.png" rel="lightbox[group][Slide 2]">
      <img src="/files/thumbs/files/slide2.png" />
   </a>
</div>
<p>Some text before an image. 
   Maybe some text in between, nobody knows what the scientists are up to. 
   And even more text right after that.
</p>

Так что мне нужно сделать, чтобы получить все узлы изображенияHTML, созданный редактором, обрабатывает их, вставляет div и удаляет узлы изображения.Прочитав довольно много похожих вопросов, я что-то упускаю и не могу заставить его работать.Возможно, я все еще неправильно понимаю всю концепцию манипулирования DOM.Вот что я придумал до сих пор:

// create DOMDocument
$doc = new DOMDocument();
// load WYSIWYG html into DOMDocument
$doc->loadHTML($html_from_editor);
// create DOMXpath
$xpath = new DOMXpath($doc);
// create list of all first level DOMNodes (these are p's or ul's in most cases)
$children = $xpath->query("/");
foreach ( $children AS $child ) {
    // now get all images
    $cpath = new DOMXpath($child);
    $images = $cpath->query('//img');
    foreach ( $images AS $img ) {
        // get attributes
        $atts = $img->attributes;
        // create replacement
        $lb_div = $doc->createElement('div');
        $lb_a = $doc->createElement('a');
        $lb_img = $doc->createElement('img');
        $lb_img->setAttribute("src", '/files/thumbs'.$atts->src);
        $lb_a->setAttribute("href", '/files/fullview'.$atts->src);
        $lb_a->setAttribute("rel", "lightbox[slide][".$atts->alt."]");
        $lb_a->appendChild($lb_img);
        $lb_div->setAttribute("class", "custom");
        $lb_div->appendChild($lb_a);
        $child->insertBefore($lb_div);
        // remove original node
        $child->removeChild($img);
    }
}

Проблемы, с которыми я столкнулся:

  1. `$ atts` не заполняется значениями.Он содержит правильные имена атрибутов, но значения отсутствуют.
  2. `insertBefore` должен вызываться на родительском узле дочернего элемента, если я понял это право.Таким образом, это должно быть `$ child-> parentNode-> insertBefore ($ lb_div, $ child);` но родительский узел не определен.
  3. Удаление исходного тега img не работает.

Буду благодарен за любой совет, что мне не хватает.Я на правильном пути или это должно быть сделано совсем иначе?

Заранее, Пол

Ответы [ 2 ]

2 голосов
/ 06 июля 2011

Это должно сделать это ( демо ):

$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->loadXML("<div>$xhtml</div>"); // we need the div as root element

// find all img elements in paragraphs in the partial body
$xp = new DOMXPath($dom);
foreach ($xp->query('/div/p/img') as $img) {

    $parentNode = $img->parentNode; // store for later
    $parentNode->removeChild($img); // unlink all found img elements

    // create a element
    $a = $dom->createElement('a');
    $a->setAttribute('href', '/files/fullview/' . basename($img->getAttribute('src')));
    $a->setAttribute('rel', sprintf('lightbox[group][%s]', $img->getAttribute('alt')));
    $a->appendChild($img);

    // prepend img src with path to thumbs and remove alt attribute
    $img->setAttribute('href', '/files/thumbs' . $img->getAttribute('src'));
    $img->removeAttribute('alt'); // imo you should keep it for accessibility though

    // create the holding div
    $div = $dom->createElement('div');
    $div->setAttribute('class', 'custom');
    $div->appendChild($a);

    // insert the holding div
    $parentNode->parentNode->insertBefore($div, $parentNode);
}

$dom->formatOutput = true;
echo $dom->saveXml($dom->documentElement);
1 голос
/ 06 июля 2011

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

  1. Вы перебирали корневой элемент документа.Это всего лишь один элемент, поэтому собирать в нем все изображения.
  2. Второй xpath должен быть относительно дочернего, поэтому начиная с ..
  3. Если вы загружаете в блок HTML, DomDocumentсоздаст недостающие элементы, такие как body вокруг него.Так что вам нужно обратиться к ним для ваших запросов xpath и вывода.
  4. Способ доступа к атрибутам был неправильным.С сообщением об ошибке это дало бы вам информацию об ошибке.

Просто взгляните на рабочий код, который мне удалось собрать ( Demo ).Я оставил несколько заметок:

$html_from_editor = <<<EOD
<p>Intro Text</p>
<ul>
   <li>List point 1</li>
   <li>List point 2</li>
</ul>
<p>Some text before an image. 
   <img alt="Slide 1" src="/files/slide1.png" /> 
   Maybe some text in between, nobody knows what the scientists are up to. 
   <img alt="Slide 2" src="/files/slide2.png" /> 
   And even more text right after that.
</p>
EOD;

// create DOMDocument
$doc = new DOMDocument();
// load WYSIWYG html into DOMDocument
$doc->loadHTML($html_from_editor);
// create DOMXpath
$xpath = new DOMXpath($doc);

// create list of all first level DOMNodes (these are p's or ul's in most cases)
# NOTE: this is XHTML now
$children = $xpath->query("/html/body/p");

foreach ( $children AS $child ) {
    // now get all images
    $cpath = new DOMXpath($doc);
    $images = $cpath->query('.//img', $child); # NOTE relative to $child, mind the .

    // if no images are found, continue
    if (!$images->length) continue;

    // insert replacement node
    $lb_div = $doc->createElement('div');
    $lb_div->setAttribute("class", "custom");
    $lb_div = $child->parentNode->insertBefore($lb_div, $child);


    foreach ( $images AS $img ) {
        // get attributes
        $atts = $img->attributes;
        $atts = (object) iterator_to_array($atts); // make $atts more accessible    

        // create the new link with lighbox and full view
        $lb_a = $doc->createElement('a');
        $lb_a->setAttribute("href", '/files/fullview'.$atts->src->value);
        $lb_a->setAttribute("rel", "lightbox[slide][".$atts->alt->value."]");

        // create the new image tag for thumbnail
        $lb_img = $img->cloneNode(); # NOTE clone instead of creating new
        $lb_img->setAttribute("src", '/files/thumbs'.$atts->src->value);

        // bring the new nodes together and insert them
        $lb_a->appendChild($lb_img);
        $lb_div->appendChild($lb_a);

        // remove the original image
        $child->removeChild($img);
    }
}

// get body content (original content)
$result = '';
foreach ($xpath->query("/html/body/*") as $child) {
    $result .= $doc->saveXML($child); # NOTE or saveHtml 
}

echo $result;
...