Из кода программы на PHP мы попытаемся загрузить картинку в публикацию сайта на Drupal 7. Здесь описана логика и показан код всех операций.
Постановка задачи
Эта задача является частью задачи импорта статьи, извлеченной из HTML кода веб-страницы сайта, в хранилище публикаций сайта на движке drupal. Потому, мы можем извлечь полную информацию об изображении, которая есть в HTML документе, не ограничиваясь только файлом картинки. Например, это значения атрибутов тега IMG — alt и title.
Итак, входными данными у нас будет HTML код с тегом IMG. А результатом работы — создание публикации с полем типа image, куда будет загружено изображение.
План реализации
Для захвата планеты выполнения задачи набросаем планчик:
- Загрузка изображения из внешнего источника;
- поиск имеющегося или сопоставление нового объекта file;
- создание поля типа image для публикации;
- сохранение публикации.
Дальше все по плану.
Загрузка изображения из внешнего источника.
Входные данные, ожидается, что представляют из себе вот такой текст:
1 2 3 |
//картинка $img = '<img src="...." alt="какое то описание" title="возможно, даже есть заголовок" width="x" height="y" />' ; |
Для получения атрибутов я использую незамысловатую функцию собственного производства — myLib::tagParse($htmlTagCode). Она дербанит картинку, и мы получаем массив её свойств. Код tagParse, смотрите чуть ниже.
1 |
$img = myLib::tagParse($img); |
Дальше мы выясняем, что из себя представляет ссылка и выполняем загрузку файла изображения, используя ещё одну из 1000 функции из моей коллекции — myLib::local_copy($externalURL) :).
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$fileUri = false; //если перед нами абсолютная ссылка if (substr($img['p']['src'], 0, 7) == 'http://') { $fileUri = myLib::local_copy($uri); //относительная ссылка //здесь пример только одного варианта относительной ссылки; //можно рассматривать и другие варианты, но они едва ли //встречаются в теле статей в современных CMS. } else if (substr($img['p']['src'], 0, 1) == '/') { $uri = $_SERVER['DOCUMENT_ROOT'] . $img['p']['src']; $fileUri = myLib::local_copy($site['scheme'] . '://' . $site['host'] . $img['p']['src']); } |
На этом этапе мы должны получить в переменной $fileUri относительный url (начинается с «/») на изображение уже загруженное на сайт под drupal.
А вот код tagParse.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* сборник функций */ class myLib { /* извлечение атрибутов из HTML тега я собираюсь получить вот такую структуру: $data = array( 'tag' => 'scalar',//имя HTML тега, 'p' => array() //массив его атрибутов ); */ static function tagParse($html) { $data = array(); if (preg_match('/^<([^\s"]+)/ism', $html, $m)) $data['tag'] = $m[1]; //параметры if (preg_match_all('/([\w]+)="([^"]+)"/ism', $html, $m)) { foreach ($m[0] as $k => $v) $data['p'][strtolower($m[1][$k])] = $m[2][$k]; } return $data; } } |
Поиск имеющегося или сопоставление нового объекта file.
Функция local_copy, которой мы воспользовались на прошлом этапе не будет скачивать картинку, если такая уже есть на сайте в папке для экспорта. Она просто вернет её относительный url. Изображение даже может уже содержаться в таблице file_managed. Попробуем найти его там.
Результатом этого этапа должен стать объект file, описывающий загруженный в drupal файл.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
//собираем данные о файле if ($fileUri !== false) { //заполним шаблон объекта $file = new stdClass(); $file->uid = $user->uid; $file->status = 1; $file->filename = trim(drupal_basename($fileUri), '.'); //папка /auto-import служит приемником файлов $file->uri = 'public://auto-import/' . $file->filename; $file->filemime = file_get_mimetype($file->filename); $file->filesize = filesize($_SERVER['DOCUMENT_ROOT'] . $fileUri); //поиск в базе $res = db_query("SELECT * FROM file_managed WHERE filename = :filename AND uri = :uri AND filesize = :filesize", array( ':filename' => $file->filename, ':uri' => $file->uri, ':filesize' => $file->filesize )); if ($res->rowCount()) { //такой файл уже есть $file = $res->fetchObject(); } else { //сохраняем запись в file_managed db_query("INSERT INTO file_managed (uid, status, filename, uri, filemime, filesize, timestamp) VALUES(:uid, :status, :filename, :uri, :filemime, :filesize, UNIX_TIMESTAMP())", array( ':uid' => $file->uid, ':status' => $file->status, ':filename' => $file->filename, ':uri' => $file->uri, ':filemime' => $file->filemime, ':filesize' => $file->filesize ) ); //извлекаем код новой записи $r = db_query('SELECT LAST_INSERT_ID() as fid')->fetchObject(); $file->fid = $r->fid; } } else { $file = false; } |
Создание поля типа image для публикации.
Пусть в нашем примере оно имеет имя field_image. Оно одно из многих полей публикации, которые нужно определить перед сохранением публикации в базу. Не будем перечислять все, нам важно только как выглядит структура картинки.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
global $user; $node = new stdClass(); $node->uid = $user->uid; $node->title = 'название статьи'; $node->status = 1; //опубликовано $node->type = 'article'; //тип публикации //... здесь мы определяем поля будущей публикации //но для данного примера важно только поле field_image if ($file) $node->field_image = array('und' => array(0 => array( 'fid' => $file->fid, 'uid' => $file->uid, 'filename' => $file->filename, 'uri' => $file->uri, 'filemime' => $file->filemime, 'filesize' => $file->filesize, 'status' => 1, 'alt' => !empty($img['p']['alt']) ? $img['p']['alt'] : '', 'title' => !empty($img['p']['title']) ? $img['p']['title'] : '', 'width' => $img['p']['width'], 'height' => $img['p']['height'], ))); |
Осталось сохранить публикацию.
1 |
node_save($node); |
Статистику использования файла в таблице file_usage, drupal обновит сам.