Система управления задачами на PHP: реализация и варианты

Раздел: Веб-разработка -> Системы управления задачами

Основные подходы к управлению задачами

В веб-разработке часто требуется реализовать функционал списка задач (todo-приложения). Базовая структура включает отображение, добавление, удаление и изменение статуса задач. В качестве идентификатора удобно использовать индекс элемента в массиве (task index php index). Рассмотрим несколько способов реализации с разными уровнями сложности и сохранности данных.

Наиболее эффективное решение: база данных MySQL и PDO

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

Как создать простое приложение для задач с использованием MySQL и PDO?

Создадим таблицу tasks:


CREATE TABLE tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    status TINYINT(1) DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
  

Task index php index (управление задачами)

Код index.php:


<?php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$action = $_GET['action'] ?? 'index';

if ($action === 'add' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = trim($_POST['title']);
    if ($title) {
        $stmt = $pdo->prepare('INSERT INTO tasks (title) VALUES (?)');
        $stmt->execute([$title]);
    }
    header('Location: index.php');
    exit;
}

if ($action === 'toggle' && isset($_GET['id'])) {
    $id = (int)$_GET['id'];
    $pdo->exec("UPDATE tasks SET status = NOT status WHERE id = $id");
    header('Location: index.php');
    exit;
}

if ($action === 'delete' && isset($_GET['id'])) {
    $id = (int)$_GET['id'];
    $pdo->exec("DELETE FROM tasks WHERE id = $id");
    header('Location: index.php');
    exit;
}

$stmt = $pdo->query('SELECT * FROM tasks ORDER BY created_at DESC');
$tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Task Manager</title></head>
<body>
<h2>Список задач</h2>
<form method="post" action="?action=add">
    <input type="text" name="title" required>
    <button>Добавить</button>
</form>
<ul>
<?php foreach ($tasks as $task): ?>
    <li>
        <?= htmlspecialchars($task['title']) ?>
        <a href="?action=toggle&id=<?= $task['id'] ?>">
            <?= $task['status'] ? '✅' : '⬜' ?>
        </a>
        <a href="?action=delete&id=<?= $task['id'] ?>">❌</a>
    </li>
<?php endforeach; ?>
</ul>
</body>
</html>
  

Типичные ошибки:

  • Отсутствие проверки существования записи перед обновлением/удалением.
  • SQL-инъекции при прямом подставлении id в запрос (в примере использовано приведение к int, но лучше использовать подготовленные выражения).

Вариант 1: Хранение задач в сессии

Как реализовать управление задачами без базы данных, используя только сессии?

Подходит для прототипов или однопользовательских инструментов, где данные не нужно сохранять между посещениями.


<?php
session_start();
if (!isset($_SESSION['tasks'])) {
    $_SESSION['tasks'] = [];
}

$action = $_GET['action'] ?? 'index';

if ($action === 'add' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = trim($_POST['title']);
    if ($title) {
        $_SESSION['tasks'][] = ['title' => $title, 'done' => false];
    }
    header('Location: index.php');
    exit;
}

if ($action === 'toggle' && isset($_GET['index'])) {
    $index = (int)$_GET['index'];
    if (isset($_SESSION['tasks'][$index])) {
        $_SESSION['tasks'][$index]['done'] = !$_SESSION['tasks'][$index]['done'];
    }
    header('Location: index.php');
    exit;
}

if ($action === 'delete' && isset($_GET['index'])) {
    $index = (int)$_GET['index'];
    if (isset($_SESSION['tasks'][$index])) {
        array_splice($_SESSION['tasks'], $index, 1);
    }
    header('Location: index.php');
    exit;
}

$tasks = $_SESSION['tasks'];
?>
  

Проблемы:

  • Данные теряются после закрытия браузера (если не настроить постоянное хранение сессий).
  • При одновременной работе нескольких пользователей данные пересекаются.

Вариант 2: Хранение в JSON-файле

Как сохранять задачи в файл для простого хранения между запросами?

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


<?php
$file = 'tasks.json';

function loadTasks() {
    global $file;
    if (!file_exists($file)) return [];
    $data = file_get_contents($file);
    return json_decode($data, true) ?? [];
}

function saveTasks($tasks) {
    global $file;
    file_put_contents($file, json_encode($tasks, JSON_PRETTY_PRINT));
}

$tasks = loadTasks();

$action = $_GET['action'] ?? 'index';

if ($action === 'add' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = trim($_POST['title']);
    if ($title) {
        $tasks[] = ['title' => $title, 'done' => false];
        saveTasks($tasks);
    }
    header('Location: index.php');
    exit;
}

if ($action === 'toggle' && isset($_GET['index'])) {
    $index = (int)$_GET['index'];
    if (isset($tasks[$index])) {
        $tasks[$index]['done'] = !$tasks[$index]['done'];
        saveTasks($tasks);
    }
    header('Location: index.php');
    exit;
}

if ($action === 'delete' && isset($_GET['index'])) {
    $index = (int)$_GET['index'];
    if (isset($tasks[$index])) {
        array_splice($tasks, $index, 1);
        saveTasks($tasks);
    }
    header('Location: index.php');
    exit;
}
?>
  

Ошибки:

  • Необходимость прав на запись в файл.
  • Одновременная запись из нескольких процессов может привести к повреждению файла (конкуренция).

Вариант 3: Использование SQLite

Как использовать легковесную базу данных SQLite для управления задачами?

Хороший компромисс между простотой файлового хранения и мощью SQL. Не требует отдельного сервера.


<?php
$db = new PDO('sqlite:tasks.db');
$db->exec('CREATE TABLE IF NOT EXISTS tasks (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    done INTEGER DEFAULT 0
)');

$action = $_GET['action'] ?? 'index';

if ($action === 'add' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = trim($_POST['title']);
    if ($title) {
        $stmt = $db->prepare('INSERT INTO tasks (title) VALUES (?)');
        $stmt->execute([$title]);
    }
    header('Location: index.php');
    exit;
}

if ($action === 'toggle' && isset($_GET['id'])) {
    $id = (int)$_GET['id'];
    $db->exec("UPDATE tasks SET done = NOT done WHERE id = $id");
    header('Location: index.php');
    exit;
}

if ($action === 'delete' && isset($_GET['id'])) {
    $id = (int)$_GET['id'];
    $db->exec("DELETE FROM tasks WHERE id = $id");
    header('Location: index.php');
    exit;
}

$tasks = $db->query('SELECT * FROM tasks ORDER BY id DESC')->fetchAll(PDO::FETCH_ASSOC);
?>
  

Недостатки:

  • Не подходит для высоконагруженных проектов.
  • Одновременная запись может блокироваться (но для одного пользователя это не критично).

Цели и случаи использования

Выбор варианта зависит от требований:

  • MySQL + PDO для production-приложений с множеством пользователей.
  • Сессии для быстрых прототипов или временных данных.
  • JSON-файл для небольших личных инструментов или бэкенда без БД.
  • SQLite для десктопных или мобильных веб-приложений, где нужна легкость развертывания.

Расширенные примеры с пошаговыми пояснениями

Пример 1: Добавление категорий и приоритетов (MySQL)

Как расширить функционал задач, добавив категории и приоритеты?

Создадим дополнительные таблицы category и приоритет (поле priority).

Пример

-- Таблица категорий
CREATE TABLE categories (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL
);

-- Таблица задач с новыми полями
CREATE TABLE tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    status TINYINT(1) DEFAULT 0,
    priority INT DEFAULT 1,  -- 1 - низкий, 2 - средний, 3 - высокий
    category_id INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (category_id) REFERENCES categories(id)
);

-- Вставка тестовых категорий
INSERT INTO categories (name) VALUES ('Работа'), ('Личное'), ('Учёба');

Код для добавления задачи с выбором категории:

Пример

<?php
// index.php (фрагмент)
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$categories = $pdo->query('SELECT * FROM categories')->fetchAll();

if ($action === 'add' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $title = trim($_POST['title']);
    $priority = (int)$_POST['priority'] ?? 1;
    $category_id = !empty($_POST['category']) ? (int)$_POST['category'] : null;
    
    if ($title) {
        $stmt = $pdo->prepare('INSERT INTO tasks (title, priority, category_id) VALUES (?, ?, ?)');
        $stmt->execute([$title, $priority, $category_id]);
    }
    header('Location: index.php');
    exit;
}
?>
<form method="post" action="?action=add">
    <input type="text" name="title" required>
    <select name="priority">
        <option value="1">Низкий</option>
        <option value="2" selected>Средний</option>
        <option value="3">Высокий</option>
    </select>
    <select name="category">
        <option value="">Без категории</option>
        <?php foreach ($categories as $cat): ?>
            <option value="<?= $cat['id'] ?>"><?= $cat['name'] ?></option>
        <?php endforeach; ?>
    </select>
    <button>Добавить</button>
</form>

Результат:

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

Пример 2: Фильтрация и сортировка на стороне сервера (JSON)

Как реализовать фильтр задач по статусу и сортировку по дате?

В случае JSON-файла можно обрабатывать массив средствами PHP.

Пример

<?php
$tasks = loadTasks(); // загружаем массив

// Фильтрация
$filterStatus = $_GET['status'] ?? 'all'; // 'all', 'active', 'done'
if ($filterStatus === 'active') {
    $tasks = array_filter($tasks, fn($t) => !$t['done']);
} elseif ($filterStatus === 'done') {
    $tasks = array_filter($tasks, fn($t) => $t['done']);
}

// Сортировка (по названию, по статусу)
$sortBy = $_GET['sort'] ?? 'title';
$order = $_GET['order'] ?? 'asc';
if ($sortBy === 'status') {
    usort($tasks, function($a, $b) use ($order) {
        $cmp = $a['done'] <=> $b['done'];
        return $order === 'desc' ? -$cmp : $cmp;
    });
} else {
    usort($tasks, function($a, $b) use ($order) {
        $cmp = strcmp($a['title'], $b['title']);
        return $order === 'desc' ? -$cmp : $cmp;
    });
}
?>
<!-- HTML часть для выбора параметров -->
<a href="?status=active&sort=status&order=desc">Активные, сначала выполненные</a>

Для наглядности выведем отфильтрованный список:

  • [⬜] Купить молоко
  • [✅] Позвонить клиенту

Пример 3: Использование транзакций в PDO (MySQL)

Как гарантировать целостность данных при массовом обновлении задач?

Предположим, нужно пометить все задачи как выполненные.

Пример

<?php
$pdo->beginTransaction();
try {
    $pdo->exec('UPDATE tasks SET status = 1 WHERE status = 0');
    // Другие операции (логирование, обновление счётчика)
    $pdo->exec('UPDATE stats SET last_batch = NOW()');
    $pdo->commit();
    echo 'Все активные задачи выполнены.';
} catch (Exception $e) {
    $pdo->rollBack();
    echo 'Ошибка: ' . $e->getMessage();
}

Результат:

Все активные задачи выполнены.

Пример 4: AJAX для добавления задачи без перезагрузки (JSON + jQuery)

Как улучшить UX, используя асинхронные запросы?

Клиентский код отправляет POST на сервер, сервер возвращает JSON с обновлённым списком.

Пример

// server.php (отдельный скрипт)
header('Content-Type: application/json');
$action = $_POST['action'] ?? '';
if ($action === 'add') {
    $title = trim($_POST['title']);
    if ($title) {
        $_SESSION['tasks'][] = ['title' => $title, 'done' => false];
        echo json_encode(['success' => true, 'tasks' => $_SESSION['tasks']]);
        exit;
    }
}
echo json_encode(['success' => false]);

JavaScript:

Пример

$('#form').on('submit', function(e) {
    e.preventDefault();
    $.post('server.php', { action: 'add', title: $('#title').val() }, function(data) {
        if (data.success) {
            // Обновляем список на странице
            renderTasks(data.tasks);
        }
    }, 'json');
});

Результат:

При отправке формы страница не перезагружается, новый элемент появляется мгновенно.

Управление задачами - comments

En
Task index php index (php)