Программное использование cron в Drupal 7

№9-1,

технические науки

В статье рассказывается как программно использовать cron в Drupal 7 для выполнения задач. В качестве примера используется модуль для взаимодействия с сайтом seogenerator.ru. Сайт предоставляет возможность изменять слова в тексте на их синонимы.

Похожие материалы

В этой статье расскажу о программном использовании cron в Drupal 7. Cron позволяет выполнять запланированные действия через определенный промежуток времени. Тем самым снижается разовая нагрузка сайт. В качестве приведу пример модуля, который позволяет изменять слова в тексте на их синонимы. Для этого модуль с помощью cron, будет обращаться к сайту seogenerator.ru. Данный способ активно применяется в SEO для уникализации текста. Лично я не являюсь сторонником данного метода. Считаю что, материал не должен быть сворован и оптимизирован под SEO. Такие сайты никогда не будут пользоваться популярностью.  

Создадим директорию seogenerator для будущего модуля. Добавим информацию о модуле в файл seogenerator.info.

name = SeoGenerator
description = SeoGenerator
core = 7.x
configure = admin/config/content/seogenerator

Добавим функцию,  которая будет отвечать за создание таблицы в базе данных. В этой таблице будем хранить информацию об узлах, которые прошли обработку с помощью нашего модуля.

/**
* hook_schema().
*/
function seogenerator_schema() {
  $schema['seogenerator'] = array(
    'description' => 'SeoGenerator',
    'fields' => array(
      'nid' => array(
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
        'default' => 0,
        'description' => "{node}.nid.",
      ),        
    ),
    'primary key' => array('nid'),
  );
  return $schema;
}

Добавим функцию для удаления модуля из Drupal.

/**
 * hook_uninstall().
 */
function seogenerator_uninstall() {
  db_delete('variable')
    ->condition('name', 'seogenerator_%', 'LIKE')
    ->execute();
  cache_clear_all('variables', 'cache_bootstrap');
}

Добавим страницу для настройки нашего модуля.

/**
 * hook_menu().
 */
function seogenerator_menu() {
  $items['admin/config/content/seogenerator'] = array(
    'title' => 'SeoGenerator',
    'description' => 'Настройки SeoGenerator',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('seogenerator_settings'),
    'access arguments' => array('administer seogenerator'),
  );
  return $items;
}

function seogenerator_settings() {
  ...
}

Добавим поле типа переключатель, которое позволит включить / выключить модуль.

  $form['seogenerator_enable'] = array(
    '#type' => 'checkbox',
    '#title' => t('Включить SeoGenerator'),
    '#default_value' => variable_get('seogenerator_enable', 0),
  );  

Добавим группу независимых переключателей для выбора типа материала, для которого будем применять наш модуль. Функция node_type_get_names позволяет поличить список всех типов материала.

  $form['seogenerator_node_types'] = array(
    '#type' => 'checkboxes',
    '#title' => t('Типы материалов'),
    '#options' => node_type_get_names(),
    '#default_value' => variable_get('seogenerator_node_types', array()),
  );

Функционал сайта позволяет использовать несколько баз синонимов для генерации текста. Позволим пользователям выбирать из какой базы генерировать текст. Добавим список зависимых переключателей для выбора.

  $form['seogenerator_base'] = array(
    '#type' => 'radios',
    '#title' => t('База синонимов'),
    '#default_value' => variable_get('seogenerator_base', 0),
    '#options' => array(0 => 'Big one', 1 => 'Small one', 2 => 'Small two'),
    '#description' => t('Используемая база синонимов для генерации текста.'),
  );

Также функционал сайта позволяет выбрать метод подстановки символов. Обеспечим для пользоватлей возможность выбора. Добавим список зависимых переключателей для выбора.

  $form['seogenerator_type'] = array(
    '#type' => 'radios',
    '#title' => t('Метод подстановки'),
    '#default_value' => variable_get('seogenerator_type', 0),
    '#options' => array(0 => 'Первый', 1 => 'Случайный'),
    '#description' => t('Метод подстановки синонимов (первый или случайный).'),
  );

Добавим стандартные кнопки и обработчик результатов заполнения формы.

  return system_settings_form($form);

Определим права доступа к странице настройки модуля.

/**
 * hook_permission().
 */
function seogenerator_permission() {
  return array(
    'administer seogenerator' => array(
      'title' => t('Администратирование SeoGenerator'),
      'description' => t('Изменение настроек модуля SeoGenerator.'),
    ),
  );
}

Для определения новой задачи cron существует хук hook_cron_queue_info. В параметре worker callback указываем какая функция будет вызываться при выполнении cron. Параметр time определяет время выполнения функции.

/**
 * hook_cron_queue_info()
 */
function seogenerator_cron_queue_info() {
  $queues['seogenerator'] = array(
    'worker callback' => '_seogenerator_update', 
    'time' => 5,
  );
  return $queues;
}

Функция выполняет запросы на сайт и заменяет заголовок узла и его содержимое на сгенерированный текст. Сайт позволяет за один раз генерировать только 6 тысяч символов. Поэто разбираем текст на части. 

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

function _seogenerator_update($node) {
  watchdog('seogenerator', $node->nid, array(), WATCHDOG_INFO);
  $node = node_load($node->nid);
  $node->title = _seogenerator_query($node->title);
  $body = $node->body['und'][0]['value'];
  $elements = str_split($body, 6000);
  $node->body['und'][0]['value'] = '';
  foreach ($elements as $element) {
    $body = _seogenerator_query($element);
    $node->body['und'][0]['value'] .= $body;
  }
  node_save($node);
  db_insert('seogenerator')
    ->fields(array('nid' => $node->nid))
    ->execute();
}

Функция для выполнения запроса с помощью cURL на сайт seogenerator.ru. Отправляем параметры для генератора текста и получаем ответ.

function _seogenerator_query($text) {
  $params = array();
  $params['text'] = $text;
  $params['base'] = variable_get('seogenerator_base', 0);
  $params['type'] = variable_get('seogenerator_type', 0);
  $params['format'] = 'text';
  $curl = curl_init();
  curl_setopt($curl, CURLOPT_URL, 'http://seogenerator.ru/api/synonym/');
  curl_setopt($curl, CURLOPT_POST, 1);
  curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
  curl_setopt($curl, CURLOPT_HEADER,0);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);  
  $result = curl_exec($curl);
  curl_close($curl);
  return $result == 'Exceeded the limit queries from this IP address'? $text : $result;
}

Для перехвата cron используем хук hook_cron

/**
 * hook_cron()
 */
function seogenerator_cron() {
  if (variable_get('seogenerator_enable', 0)) {
    ...
  }
}

Выполняем запрос в базу данных и получаем список id узлов, которые отсутствуют в нашем списке обработанных узлов. Для этого формируем запрос в базу данных. 

    $nodes = db_select('node', 'n');
    $nodes->leftJoin('seogenerator', 's', 'n.nid = s.nid');
    $nodes = $nodes
      ->fields('n', array('nid'))
      ->range(0, 5)
      ->condition('n.type', _seogenerator_get_selected_node_types())
      ->isNull('s.nid')
      ->execute()
      ->fetchAll();

Добавляем элементы для выполнения нашей задачи, с помощью cron. Для этого в Drupal 7 предназначен объект DrupalQueue. Метод get устанавливает задачу. Как добавить новую задачу было рассказано выше. Используя метод createItem мы можем добавить новый элемент к нашей задачи. Параметры этого метода передаются функции, которая была установлена для задачи cron.

    $queue = DrupalQueue::get('seogenerator');
    foreach($nodes as $node) {
      $queue->createItem($node);
    }