Недавно попалось сразу 2 такие задачи. Одна из них — это добавление в ссылочную биржу через api кучу текстов ссылок. При этом, ожидание от сервера может быть довольно долгим, а то и вовсе закончится неудачно.
И вчера (точнее уже позавчера – примечание автора-редактора) решил написать заготовку такого скрипта, с комментариями, специально чтоб тут написать. Надеюсь, будет кому-то полезно.
Старался сделать как можно нагляднее и проще для понимания.
И так, вкратце, принцип работы…
Берется некая очередь заданий, кладется в базу SQLite (если кто не знает – это встроенная в php, упрощенная БД, которая создается тут же в текстовом файле)
Если запустить обработку, то задания из очереди (из базы) будут выполняться поочередно, по каждому будет выполнен ajax запрос и ответ в режиме реального времени выведется на экран. Если задание с ошибкой, это отметиться в БД (закачайте на сервер, посмотрите, скрипт рабочий).
В index.html есть несколько ссылок управления и область для отчетов #console.
В атрибутах data-action ссылок (кроме ScreenClear) хранится имя метода, который вызывается из класса stepWise (файл stepWise.class.php).
Всего 4 файла:
index.html — интерфейс управления
script.js — javascript/jquery скрипт
ajax.php — прокладка для ajax запросов
stepWise.class.php — серверный функционал
На github: github.com/adminoid/ajaxStepwiseBlank
И здесь:
index.html
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.min.js"></script> <script type="text/javascript" src="script.js"></script> </head> <body> <!-- Принцип этой "системы", грубо говоря, следующий: script.js берет значение из атрибута data-action ссылки и вызывает одноименный метод из файла /class/stepWise.class.php, через прокладку /ajax/ajax.php --> <!-- Работа с базой данных --> <ul class="dbActions"> <li><a class="db" data-action="DbRecreateWithQueue" href="#">Пересоздать базу c очередью задач</a></li> <li><a class="db" data-action="DbShowData" href="#">Посмотреть какие данные лежат в БД</a></li> </ul> <!-- Работа c экраном вывода --> <ul class="screenActions"> <li><a class="screen" data-action="ScreenClear" href="#">Очистить экран</a></li> </ul> <!-- Работа с очередью задач --> <ul class="processActions"> <li><a class="process" data-action="ProcessQueue" href="#">Запустить исполнение очереди задач</a></li> </ul> <!-- Область для вывода информации --> <div id="console"></div> <!-- Чтобы ошибки были "красными", а сообщения - "зелеными" --> <style type="text/css"> .error{color: #730000;} .success{color: #007300;} </style> </body> </html>
script.js
var globals = { // Путь от корня сайта к ajax.php ajaxPath: 'ajax.php', // Метод для обработки очереди process: function(action){ $.post(this.ajaxPath,{ action: action }) .success(function(xhr) { var response = $.parseJSON(xhr); $('#console') .append("<span class='"+response.status+"'>"+response.message+"</span>"); // Если статус не complete, то рекурсивно повторить if(response.status !== 'complete'){ globals.process(action); } }); } } $(function(){ // При клике на любую ссылку с css классом "db" $(".db").click(function(e){ e.preventDefault(); // Берем значение data-action, чтобы вызвать одноименный метод через ajax запрос var action = $(e.target).data('action'); // отправляем ajax запрос на урл $.post(globals.ajaxPath,{ action: action }) .success(function(xhr) { var response = $.parseJSON(xhr); if(response.type == 'message'){ $('#console') .empty() .html("<span class='"+response.status+"'>"+response.message+"</span>"); } }); }); // Ссылки с классом .screen используются не для ajax, а для каких-то действий на странице $(".screen").click(function(e){ e.preventDefault(); // Берем значение ее data-action, чтобы понять что будем делать var action = $(e.target).data('action'); switch(action){ case 'ScreenClear': $('#console').empty(); break; } }); // Ссылки с классом .process используются для обработки очереди заданий $(".process").click(function(e){ e.preventDefault(); // Берем значение data-action, чтобы вызвать одноименный метод через ajax запрос var action = $(e.target).data('action'); $('#console').empty(); globals.process(action); }); });
ajax.php
<?php /** * User: Petja * Date: 31.03.13 * Time: 14:15 */ header("Cache-Control: no-store, no-cache, must-revalidate"); // Пропустить только ajax запрос if(empty($_SERVER['HTTP_X_REQUESTED_WITH']) or ($_SERVER['HTTP_X_REQUESTED_WITH']) != 'XMLHttpRequest'){ die('Это не ajax запрос!'); } // Пропустить только допустимые action: $availableActions = array('DbRecreateWithQueue', 'DbShowData', 'DbDeleteAll', 'ProcessQueue'); if(!in_array($action = $_POST['action'], $availableActions)){ die('Нет такого действия!'); } // Подключить функционал include "stepWise.class.php"; $sw = new stepWise; // Запустить действие echo json_encode($sw->$action());
stepWise.class.php
<?php /** * User: Petja * Date: 31.03.13 * Time: 12:50 */ class stepWise { public function DbRecreateWithQueue(){ // Берет очередь задач. Например, список картинок к обработке или список ссылок, чтобы добавить их в SAPE. У меня здесь просто массив. $tasks = array( 'Задача 1', 'Задача 2', 'Задача 3', 'Задача 4', 'Задача 5', 'Задача 6', 'Задача 7', 'Задача 8', 'Задача 9', 'Задача 10' ); try { // Открыть БД SQLite или создать, если ее нет $db = new PDO('sqlite:temp.db'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Очищает таблицу $db->exec( "DROP TABLE IF EXISTS tempTableForTasks;" ); // Создает таблицу для очереди $db->exec( "CREATE TABLE IF NOT EXISTS tempTableForTasks( id INTEGER PRIMARY KEY, tasks TEXT, status TEXT, optional TEXT );" ); // Помещает очередь в БД foreach($tasks as $task){ static $n = 1; $db->exec("INSERT INTO tempTableForTasks(tasks, status, optional) VALUES ('".$task."', '', 'Запись № ".$n."');"); $n++; } // Закрываем подключение к БД $db = null; // Возвращаем данные в случае успеха return array( 'status' => 'success', 'type' => 'message', 'method' => __METHOD__, 'message' => 'БД успешно создана!' ); } catch(PDOException $e) { // Возвращаем данные в случае ошибки return array( 'status' => 'error', 'type' => 'message', 'method' => __METHOD__, 'message' => $e->getMessage() ); } } public function DbShowData(){ try { // Открыть БД SQLite или создать, если ее нет $db = new PDO('sqlite:temp.db'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Взять данные из таблицы $result = $db->query('SELECT * FROM tempTableForTasks'); $result_all = $result->fetchall(PDO::FETCH_ASSOC); // Формируются заголовки таблицы из ключей $output = '<table><tr>'; foreach(array_keys($result_all[0]) as $key){ $output .= "<th>".$key."</th>"; } $output .= '</tr>'; // Формируется сама таблица foreach($result_all as $tr){ $output .= '<tr>'; foreach($tr as $td){ $output .= '<td>'.$td.'</td>'; } $output .= '</tr>'; } $output .= '</table>'; // Закрываем подключение к БД $db = null; // Возвращаем данные в случае успеха return array( 'status' => 'success', 'type' => 'message', 'method' => __METHOD__, 'message' => $output ); } catch(PDOException $e) { // Возвращаем данные в случае ошибки return array( 'status' => 'error', 'type' => 'message', 'method' => __METHOD__, 'message' => $e->getMessage() ); } } /* * Взять из БД один не обработанный элемент * Обработать его * Вернуть успех или ошибку * Пометить в БД этот элемент как обработанный * */ public function ProcessQueue(){ try { // Открыть БД SQLite или создать, если ее нет $db = new PDO('sqlite:temp.db'); $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Взять одну не обработанную строку из таблицы $result = $db->query('SELECT * FROM tempTableForTasks WHERE status != "processed"'); $notProcessedColumn = $result->fetch(PDO::FETCH_ASSOC); // Если необработанных строк не осталось, возвращаем status complete if(!$notProcessedColumn){ return array( 'status' => 'complete', 'type' => 'queue', 'method' => __METHOD__, 'message' => 'Конец' ); } // Отправить задание на обработку $status = $this->OneAction(); if($status == 'success'){ $db->exec('UPDATE tempTableForTasks SET status="processed", optional="обработано" WHERE id = "'.$notProcessedColumn['id'].'"'); }elseif($status == 'error'){ $db->exec('UPDATE tempTableForTasks SET status="processed", optional="ошибка" WHERE id = "'.$notProcessedColumn['id'].'"'); } // Формируем строку ответа - получившаяся строка в БД после обработки $result = $db->query('SELECT * FROM tempTableForTasks WHERE id = "'.$notProcessedColumn['id'].'"'); $processedColumn = $result->fetch(PDO::FETCH_ASSOC); // Закрываем подключение к БД $db = null; // Возвращаем данные в случае успеха return array( 'status' => $status, 'type' => 'queue', 'method' => __METHOD__, 'message' => implode(", ", $processedColumn)."<br>\n" ); } catch(PDOException $e) { // Возвращаем данные в случае ошибки return array( 'status' => 'error', 'type' => 'message', 'method' => __METHOD__, 'message' => $e->getMessage() ); } } /* * Это демонстрационная функция, выполняется с задержкой в 1 секунду * и возвращает ошибку с вероятность 30% * */ private function OneAction(){ // Задержка на секунду для видимости обработки sleep(1); // С вероятностью 30% возвращается ошибка if (rand(1, 100) <= 30){ return 'error'; } return 'success'; } }
ссылка на оригинал статьи http://habrahabr.ru/post/175009/
Добавить комментарий