Модуль Feeds является очень популярным среди Drupal-разработчиков. Но возникает вопрос, что делать если необходимо несколько расширить его функциональность. В этом нам поможет система плагинов модуля Feeds.
Существует 3 вида плагинов от которых необходимо наследовать новые:
- FeedsFetcher — плагин сборщика. Cтандартные: HTTP и File Upload. С помощью этого типа плагинов можно добавить новый источник данных.
- FeedsProcessor — плагин обработки сущностей. Cтандартные: Node processor, Taxonomy term processor, User processor. С помощью нового плагина можно добавить новый обработчик, который будет создавать особенные сущности, не вписывающиеся в стандартный набор.
- FeedsParser — плагин парсера. Стандартные парсеры в Feeds это XML, CSV и многие другие.
В этой статье я хотел бы остановиться именно на написании модуля парсера, так как довольно часто приходится иметь дело с импортом файлов со специфической структурой.
Создание модуля
Как и обычно для создания модуля нам нужно создать info файл и файл модуля:
json_example_parser.info
name = Json example parser
description = Simple json parser
package = Feeds
core = 7.x
dependencies[] = feeds
json_example_parser.module
<?php
/**
* @file
* Json example parser - simple parser plugin
*/
/**
* Implementation of hook_feeds_plugins().
* Регистрация плагина в системе.
*/
function json_example_parser_feeds_plugins() {
$info = array();
$info['JsonExampleParser'] = array(
'name' => 'JSON parser',
'description' => 'Parses custom JSON.',
'handler' => array(
'parent' => 'FeedsParser', // родительский класс от которого наследуем парсер, стандартные классы Feeds - FeedsFetcher, FeedsParser и FeedsProcessor
'class' => 'JsonExampleParser', // название парсера, должно совпадать с ключом в массиве
'file' => 'JsonExampleParser.inc', // файл класса парсера
'path' => drupal_get_path('module', 'json_example_parser'), // путь к классу парсера
),
);
return $info;
}
// очистка кеша плагинов Feeds при включении модуля
function json_example_parser_enable() {
//clear the cache to display in Feeds as available plugin.
cache_clear_all('plugins:feeds:plugins', 'cache');
}
?>
В комментариях описаны основных моменты хуков.
Исходные данные
Для примера за импортируемый материал возьмем ноду “Компьютерная игра” со следующими полями:
Данные, которые будем парсить, будут приходить в JSON формате. Не важно будет это загружаемый файл либо HTTP сборщик, за это отвечает Fetcher и способы получения данных зависят от набора плагинов для сборщика.
Входной JSON:
[
{ "name":"Team Fortress 2",
"year":2007,
"price":0
},
{ "name":"Warcraft III: The Frozen Throne",
"year":2003,
"price":13.9
},
{ "name":"Diablo III",
"year":2012,
"price":33
}
]
Создание класса парсера
Для того чтобы создать класс парсера его нужно наследовать от стандартного абстрактного класса FeedsParser и переопределить методы parse и getMappingSources. Переопределение остальных методов не является обязательным.
JsonExampleParser.inc
<?php
/**
* A JSON example parser
*/
class JsonExampleParser extends FeedsParser {
/**
* Implements FeedsParser::parse().
*/
public function parse(FeedsSource $source, FeedsFetcherResult $fetcher_result) {
$result = new FeedsParserResult();
// получаем последние настройки
$source_config = $source->getConfigFor($this);
// извлекаем JSON данные
$fetch_items = json_decode($fetcher_result->getRaw());
foreach ($fetch_items as $value) {
$item = array('name' => $value->name);
if ($value->year) {
$item['year'] = intval($value->year);
}
if ($value->price) {
$item['price'] = floatval($value->price);
}
// применение настроек из формы
if ( $source_config['type'] == 'all' ||
($source_config['type'] == 'free' && $item['price'] == 0) ||
($source_config['type'] == 'paid' && $item['price'] > 0)) {
// добавляем запись для импорта
$result->items[] = $item;
}
}
return $result;
}
/**
* Implements FeedsParser::getMappingSources().
*/
public function getMappingSources() {
return array(
'name' => array(
'name' => t('Game name'),
'description' => t('The name of the computer game.'),
),
'year' => array(
'name' => t('Release year'),
'description' => t('Release year of the computer game.'),
),
'price' => array(
'name' => t('Game price'),
'description' => t('The cost of the computer game.'),
),
) + parent::getMappingSources();
}
/**
* Configuration form.
* Конфигурационная форма, которая будет отображаться на странице настройки импортера.
*/
public function configForm(&$form_state) {
$form = array();
$form['type'] = array(
'#type' => 'select',
'#title' => t('Game type'),
'#description' => t('Game filter by type.'),
'#options' => array(
'all' => t('All game'),
'paid' => t('Paid'),
'free' => t('Free'),
),
'#default_value' => $this->config['type'],
);
return $form;
}
/**
* Define default configuration values.
* Стандартные настройки для парсера, которые будут применены если форма открыта впервые.
*/
public function configDefaults() {
return array(
'type' => 'all',
);
}
/**
* Define defaults.
* Определение настроек отправленных из формы.
*/
public function sourceDefaults() {
return array(
'type' => $this->config['type'],
);
}
/**
* Show configuration form for users.
* Конфигурационная форма которая будет отображаться пользователям на странице импорта.
*/
public function sourceForm($source_config) {
$form = array();
$form['#weight'] = -10;
$form['help']['#markup'] = '<div class="help"><p>' . t('Select the type of game you want to import') . ':</p></div>';
$form['type'] = array(
'#type' => 'select',
'#title' => t('Game type'),
'#description' => t('Game filter by type.'),
'#options' => array(
'all' => t('All game'),
'paid' => t('Paid'),
'free' => t('Free'),
),
'#default_value' => isset($source_config['type']) ? $source_config['type'] : 'all',
);
return $form;
}
}
Немного о методах.
parse — метод парсинга, получает объект класса источника FeedsSource и объект класса FeedsFetcherResult из которого извлекаются считанные данные. Данный метод формирует готовый объект FeedsParserResult с набором сущностей items для сохранения.
getMappingSources — метод который определяет поля которые будут доступны из источника для записи в поля создаваемого объекта. Например в данном случае поле маппинга name будет записываться в заголовок ноды и т.д.
configForm — этот метод предоставляет форму настройки, которая будет отображаться на странице администратора, в настройках импортера. Сохранение данных происходит автоматически.
configDefaults — стандартные настройки, если пользователь не использовал форму для конфигурации парсера.
sourceDefaults — переопределенный метод для получения доступа к сохраненным параметрам из формы конфигурации.
sourceForm — форма которая будет доступна пользователям при импорте, дополняя форму Fetcher’a.
Заключение
В результате мы получили полноценный плагин для модуля Feeds с неограниченными возможностями кастомизации настроек и обработки любых входных данных. Для более детального представление о плагинах Feeds советую посмотреть исходные коды стандартных дополнений, которые идут вместе с модулем.
Автор: Sanchiz