Урок 18

Создаём функции для работы с данными

25 мин

Зачем нужны функции для работы с данными?

Вместо того чтобы писать SQL-запросы в каждом файле, мы создадим функции:

  • getAllPosts() — получить все посты
  • getPostById($id) — получить пост по ID
  • getUserById($id) — получить пользователя

Это делает код чище и проще в поддержке.

Создаём файл с функциями

Создайте файл public/includes/database.php:

<?php
declare(strict_types=1);

/**
 * Функции для работы с базой данных
 * 
 * Используется глобальная переменная $pdo из config.php
 */

/**
 * Получить все посты
 * 
 * @param int $limit Максимальное количество постов
 * @return array Массив постов
 */
function getAllPosts(int $limit = 10): array {
    global $pdo;
    
    $stmt = $pdo->prepare("
        SELECT posts.*, users.username as author_name
        FROM posts 
        JOIN users ON posts.user_id = users.id
        ORDER BY posts.created_at DESC
        LIMIT ?
    ");
    $stmt->execute([$limit]);
    
    return $stmt->fetchAll();
}

/**
 * Получить пост по ID
 * 
 * @param int $id ID поста
 * @return array|false Массив с данными поста или false
 */
function getPostById(int $id): array|false {
    global $pdo;
    
    $stmt = $pdo->prepare("
        SELECT posts.*, users.username as author_name
        FROM posts 
        JOIN users ON posts.user_id = users.id
        WHERE posts.id = ?
    ");
    $stmt->execute([$id]);
    
    return $stmt->fetch();
}

/**
 * Создать новый пост
 * 
 * @param int $userId ID автора
 * @param string $title Заголовок
 * @param string $content Содержимое
 * @return int ID созданного поста
 */
function createPost(int $userId, string $title, string $content): int {
    global $pdo;
    
    $stmt = $pdo->prepare("
        INSERT INTO posts (user_id, title, content) 
        VALUES (?, ?, ?)
    ");
    $stmt->execute([$userId, $title, $content]);
    
    return $pdo->lastInsertId();
}

/**
 * Обновить пост
 * 
 * @param int $id ID поста
 * @param string $title Новый заголовок
 * @param string $content Новое содержимое
 * @return int Количество обновлённых записей
 */
function updatePost(int $id, string $title, string $content): int {
    global $pdo;
    
    $stmt = $pdo->prepare("
        UPDATE posts 
        SET title = ?, content = ?
        WHERE id = ?
    ");
    $stmt->execute([$title, $content, $id]);
    
    return $stmt->rowCount();
}

/**
 * Удалить пост
 * 
 * @param int $id ID поста
 * @return int Количество удалённых записей
 */
function deletePost(int $id): int {
    global $pdo;
    
    $stmt = $pdo->prepare("DELETE FROM posts WHERE id = ?");
    $stmt->execute([$id]);
    
    return $stmt->rowCount();
}

/**
 * Получить пользователя по ID
 */
function getUserById(int $id): array|false {
    global $pdo;
    
    $stmt = $pdo->prepare("SELECT id, username, email, created_at FROM users WHERE id = ?");
    $stmt->execute([$id]);
    
    return $stmt->fetch();
}

/**
 * Получить пользователя по username или email
 */
function getUserByLogin(string $login): array|false {
    global $pdo;
    
    $stmt = $pdo->prepare("
        SELECT * FROM users 
        WHERE username = ? OR email = ?
    ");
    $stmt->execute([$login, $login]);
    
    return $stmt->fetch();
}

/**
 * Создать пользователя
 */
function createUser(string $username, string $email, string $password): int {
    global $pdo;
    
    // Хешируем пароль!
    $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
    
    $stmt = $pdo->prepare("
        INSERT INTO users (username, email, password) 
        VALUES (?, ?, ?)
    ");
    $stmt->execute([$username, $email, $hashedPassword]);
    
    return $pdo->lastInsertId();
}

/**
 * Проверить пароль пользователя
 */
function verifyPassword(array $user, string $password): bool {
    return password_verify($password, $user["password"]);
}

/**
 * Получить количество постов
 */
function getPostsCount(): int {
    global $pdo;
    
    $stmt = $pdo->query("SELECT COUNT(*) as count FROM posts");
    $result = $stmt->fetch();
    
    return $result["count"];
}
?>

Подключаем в config.php

Добавьте в конец config.php:

// Подключаем функции для работы с БД
require_once __DIR__ . "/database.php";

Обновляем главную страницу

Теперь перепишем public/index.php с использованием реальных данных:

<?php
require_once "includes/config.php";
$page_title = "Главная — $site_name";

// Получаем посты из базы данных!
$posts = getAllPosts(10);

include "includes/header.php";
?>

<h2>Добро пожаловать в блог!</h2>
<p><?= escape($site_description) ?></p>

<h3>Последние публикации</h3>

<?php if (empty($posts)): ?>
    <p>Пока нет ни одной публикации.</p>
<?php else: ?>
    <?php foreach ($posts as $post): ?>
    <article style="background: #f9f9f9; padding: 20px; margin: 15px 0; border-radius: 8px;">
        <h4 style="margin-bottom: 10px;">
            <a href="post.php?id=<?= $post["id"] ?>" style="color: #333; text-decoration: none;">
                <?= escape($post["title"]) ?>
            </a>
        </h4>
        <small style="color: #888;">
            <?= formatDate($post["created_at"]) ?> • 
            Автор: <?= escape($post["author_name"]) ?>
        </small>
        <p style="margin-top: 10px;">
            <?= truncate(escape($post["content"]), 150) ?>
        </p>
        <a href="post.php?id=<?= $post["id"] ?>" style="color: #F36049;">Читать далее →</a>
    </article>
    <?php endforeach; ?>
<?php endif; ?>

<?php include "includes/footer.php"; ?>

Страница отдельного поста

Создайте public/post.php:

<?php
require_once "includes/config.php";

// Получаем ID из URL
$id = isset($_GET["id"]) ? (int)$_GET["id"] : 0;

// Получаем пост
$post = getPostById($id);

// Если пост не найден
if (!$post) {
    http_response_code(404);
    $page_title = "Пост не найден — $site_name";
    include "includes/header.php";
    echo "<h2>Пост не найден</h2>";
    echo "<p>Запрашиваемая публикация не существует.</p>";
    echo '<p><a href="index.php">← Вернуться на главную</a></p>';
    include "includes/footer.php";
    exit;
}

$page_title = escape($post["title"]) . " — $site_name";
include "includes/header.php";
?>

<article>
    <h2><?= escape($post["title"]) ?></h2>
    
    <div style="color: #888; margin-bottom: 20px;">
        <?= formatDate($post["created_at"]) ?> • 
        Автор: <?= escape($post["author_name"]) ?>
    </div>
    
    <div style="line-height: 1.8;">
        <?= nl2br(escape($post["content"])) ?>
    </div>
    
    <p style="margin-top: 30px;">
        <a href="index.php" style="color: #F36049;">← Вернуться к списку постов</a>
    </p>
</article>

<?php include "includes/footer.php"; ?>

Проверяем

  1. Откройте http://localhost:8080 — должны увидеть посты из базы данных
  2. Кликните на заголовок поста — откроется страница поста
  3. Попробуйте несуществующий ID: http://localhost:8080/post.php?id=999 — увидите "Пост не найден"

Удаляем тестовый файл

Файл test_db.php больше не нужен — можете удалить его.

Модуль 3 завершён!

Вы научились:
✅ Понимать что такое база данных
✅ Работать с phpMyAdmin
✅ Писать SQL-запросы
✅ Подключать PHP к MySQL через PDO
✅ Создавать функции для работы с данными

Теперь у вас настоящий блог с данными из базы!
В следующем модуле добавим авторизацию пользователей.

Мы используем файлы cookie для улучшения работы сайта и персонализации контента. Продолжая использовать сайт, вы соглашаетесь с использованием cookies в соответствии с нашей Политикой конфиденциальности.