Карточки в PHP: подробное руководство для веб-разработчика

Раздел: Веб-разработка -> создание карточек

Карточки PHP: подходы к формированию веб-элементов

Основное решение: карточки через PDO и цикл foreach

Цель: получение записей из базы данных MySQL и вывод каждой записи в виде карточки.

Шаги:

  1. Подключение к базе с помощью PDO.
  2. Подготовка и выполнение запроса SELECT.
  3. Извлечение всех строк в массив.
  4. Проход по массиву циклом foreach и формирование HTML для каждой карточки.

Пример кода:


$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query('SELECT id, title, content, image FROM cards');
$cards = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($cards as $card) {
    echo '<div class="card">';
    echo '<img src="' . htmlspecialchars($card['image']) . '" alt="...">';
    echo '<h3>' . htmlspecialchars($card['title']) . '</h3>';
    echo '<p>' . nl2br(htmlspecialchars($card['content'])) . '</p>';
    echo '</div>';
}

Cards php card (карточки php)

Пояснение:

PDO предоставляет единый интерфейс для работы с разными СУБД. Метод query() выполняет запрос, fetchAll() извлекает все строки в ассоциативный массив. Цикл foreach перебирает каждую запись. Функция htmlspecialchars() защищает от XSS-атак. nl2br() преобразует переводы строк в теги <br>. Для изображений используется атрибут alt с описанием.

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

  • Ошибка подключения: PDOException. Решение: обернуть код в try-catch, использовать настройки error mode.
  • XSS-уязвимости: если не использовать htmlspecialchars(), злоумышленник может внедрить скрипты. Решение: всегда экранировать вывод.
  • Отсутствие обработки пустого результата: если запрос не вернул строк, карточки не отобразятся. Решение: проверять count($cards) и выводить сообщение.

Как создать карточки с помощью процедурного mysqli?

Цель: использовать традиционный MySQLi при работе с устаревшими проектами.

Пример кода:


$conn = mysqli_connect('localhost', 'user', 'pass', 'test');
if (!$conn) { die('Connection failed'); }
$result = mysqli_query($conn, 'SELECT * FROM cards');
while ($row = mysqli_fetch_assoc($result)) {
    echo '<div class="card">';
    echo '<h3>' . htmlspecialchars($row['title']) . '</h3>';
    echo '<p>' . htmlspecialchars($row['content']) . '</p>';
    echo '</div>';
}
mysqli_close($conn);

Пояснение: mysqli_connect() открывает соединение, mysqli_query() выполняет запрос. Цикл while перебирает строки. В конце соединение закрывается. В отличие от PDO, здесь нет автоматической подстановки параметров, поэтому для защищённых запросов нужны prepared statements.

Типичные проблемы:

  • SQL-инъекции при использовании конкатенации. Решение: использовать prepared statements (mysqli_prepare, mysqli_stmt_bind_param).
  • Утечка ресурсов при незакрытом соединении. Решение: закрывать соединение после завершения работы.

Случаи использования:

  • Поддержка легаси-кода.
  • Проекты, где PDO недоступен.

Как вывести карточки из статического JSON-файла?

Цель: прототипирование без базы данных, хранение данных в JSON.

Пример кода:


$json = file_get_contents('cards.json');
$cards = json_decode($json, true);
foreach ($cards as $card) {
    echo '<div class="card">';    
    echo '<h3>' . htmlspecialchars($card['name']) . '</h3>';
    echo '<p>' . htmlspecialchars($card['description']) . '</p>';
    echo '</div>';
}

Пояснение: file_get_contents() читает файл, json_decode() преобразует в массив. Важно проверять успешность декодирования.

Ошибки и решения:

  • Файл не найден: file_get_contents() возвращает false. Решение: проверять на !== false.
  • Некорректный JSON: json_decode() возвращает null. Решение: использовать json_last_error() для диагностики.

Случаи использования:

  • Макеты, прототипы.
  • Небольшие объёмы данных.

Как организовать карточки с помощью шаблонизатора Twig?

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

Установка через Composer:


composer require twig/twig

Пример кода (index.php):


require 'vendor/autoload.php';
$loader = new \Twig\Loader\FilesystemLoader('templates');
$twig = new \Twig\Environment($loader);

echo $twig->render('cards.html.twig', ['cards' => $cards]);

Шаблон templates/cards.html.twig:


{% for card in cards %}
<div class="card">
    <h3>{{ card.title|e }}</h3>
    <p>{{ card.content|e|nl2br }}</p>
</div>
{% endfor %}

Пояснение: Twig автоматически экранирует вывод через |e. Фильтр nl2br аналогичен функции PHP. Шаблон сохраняется в отдельном файле.

Проблемы и решения:

  • Ошибка загрузки шаблона: не найден файл. Решение: проверить путь в FilesystemLoader.
  • Не экранированный вывод: если забыть |e, возможен XSS. Решение: использовать автоэкранирование (по умолчанию включено для HTML).

Случаи использования:

  • Сложные проекты с множеством представлений.
  • Команда разработчиков, где дизайнеры работают с шаблонами.

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

Цель: улучшить пользовательский опыт, загружать данные динамически.

PHP endpoint (api/cards.php):


header('Content-Type: application/json');
$pdo = new PDO(...);
$stmt = $pdo->query('SELECT * FROM cards');
$cards = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($cards);

Клиентский JavaScript (index.html):


fetch('api/cards.php')
    .then(response => response.json())
    .then(cards => {
        const container = document.getElementById('cards');
        cards.forEach(card => {
            const div = document.createElement('div');
            div.className = 'card';
            div.innerHTML = `

${card.title}

${card.content}

`; container.appendChild(div); }); });

Пояснение: PHP возвращает JSON. JavaScript обрабатывает его и динамически создаёт карточки. Важно экранировать данные на клиенте (использовать innerText или создать элементы).

Ошибки и решения:

  • CORS: если API на другом домене, браузер блокирует запрос. Решение: установить заголовки Access-Control-Allow-Origin.
  • XSS через innerHTML: если данные содержат теги, возможна инъекция. Решение: использовать document.createTextNode() или sanitize.

Случаи использования:

  • Одностраничные приложения (SPA).
  • Бесконечная прокрутка или ленивая загрузка.

Расширенные примеры создания карточек

Пример 1: Пагинация и поиск с использованием PDO

Код (index.php):

Пример

$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 6;
$offset = ($page - 1) * $perPage;
$search = isset($_GET['search']) ? trim($_GET['search']) : '';

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$where = '';
$params = [];
if ($search) {
    $where = 'WHERE title LIKE :search';
    $params[':search'] = '%' . $search . '%';
}

// Общее количество для пагинации
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM cards $where");
$countStmt->execute($params);
$total = $countStmt->fetchColumn();
$totalPages = ceil($total / $perPage);

// Карточки для текущей страницы
$stmt = $pdo->prepare("SELECT * FROM cards $where LIMIT :limit OFFSET :offset");
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
foreach ($params as $key => $val) {
    $stmt->bindValue($key, $val);
}
$stmt->execute();
$cards = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($cards as $card) {
    echo '<div class="card">';
    echo '<h3>' . htmlspecialchars($card['title']) . '</h3>';
    echo '<p>' . htmlspecialchars($card['content']) . '</p>';
    echo '</div>';
}
// Вывод ссылок пагинации
for ($i = 1; $i <= $totalPages; $i++) {
    $link = "?page=$i&search=" . urlencode($search);
    echo "<a href='$link'>$i</a> ";
}

Результат (пример вывода):

<div class="card"><h3>Карточка 1</h3><p>Описание 1</p></div>
<div class="card"><h3>Карточка 2</h3><p>Описание 2</p></div>
<a href='?page=1&search='>1</a> <a href='?page=2&search='>2</a>

Пояснение: Используются подготовленные запросы с именованными параметрами. Пагинация вычисляется через LIMIT и OFFSET. Поиск реализован с помощью LIKE. Важно экранировать URL в ссылках.

Пример 2: Кэширование готового HTML карточек

Цель: снизить нагрузку на базу данных и ускорить загрузку.

Код с использованием кэша:

Пример

$cacheFile = 'cache/cards.html';
$cacheTime = 3600; // 1 час

if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheTime)) {
    echo file_get_contents($cacheFile);
    exit;
}

ob_start();
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$cards = $pdo->query('SELECT * FROM cards')->fetchAll(PDO::FETCH_ASSOC);
foreach ($cards as $card) {
    echo '<div class="card">';
    echo '<h3>' . htmlspecialchars($card['title']) . '</h3>';
    echo '<p>' . htmlspecialchars($card['content']) . '</p>';
    echo '</div>';
}
$html = ob_get_clean();
file_put_contents($cacheFile, $html);
echo $html;

Результат:

Сгенерированный HTML выводится мгновенно при повторном запросе в течение часа. Файл cache/cards.html обновляется только после истечения времени или при ручной очистке.

Пояснение: ob_start() и ob_get_clean() захватывают вывод. file_put_contents() сохраняет в файл. При следующем запросе, если кэш актуален, файл читается напрямую.

Пример 3: ООП с абстрактным классом Card

Цель: переиспользовать код для разных типов карточек (товары, пользователи, статьи).

Код:

Пример

abstract class Card {
    protected $data;
    public function __construct(array $data) {
        $this->data = $data;
    }
    abstract public function render(): string;
}

class ProductCard extends Card {
    public function render(): string {
        return sprintf(
            '<div class="card product"><h3>%s</h3><p>Цена: %s руб.</p></div>',
            htmlspecialchars($this->data['name']),
            htmlspecialchars($this->data['price'])
        );
    }
}

class UserCard extends Card {
    public function render(): string {
        return sprintf(
            '<div class="card user"><h3>%s</h3><p>Email: %s</p></div>',
            htmlspecialchars($this->data['username']),
            htmlspecialchars($this->data['email'])
        );
    }
}

// Использование
$products = [['name' => 'Ноутбук', 'price' => 50000], ['name' => 'Мышь', 'price' => 1500]];
$users = [['username' => 'ivan', 'email' => 'ivan@ex.ru']];

foreach ($products as $p) {
    $card = new ProductCard($p);
    echo $card->render();
}
foreach ($users as $u) {
    $card = new UserCard($u);
    echo $card->render();
}

Результат:

<div class="card product"><h3>Ноутбук</h3><p>Цена: 50000 руб.</p></div>
<div class="card product"><h3>Мышь</h3><p>Цена: 1500 руб.</p></div>
<div class="card user"><h3>ivan</h3><p>Email: ivan@ex.ru</p></div>

Пояснение: Абстрактный класс определяет интерфейс. Каждый конкретный класс реализует свою верстку. Такой подход упрощает добавление новых типов карточек.

Пример 4: Бесконечная прокрутка (infinite scroll) с PHP API

Цель: загружать дополнительные карточки по мере прокрутки страницы.

PHP endpoint (api/load.php):

Пример

$page = (int)($_GET['page'] ?? 1);
$perPage = 4;
$offset = ($page - 1) * $perPage;

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM cards LIMIT :limit OFFSET :offset');
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$cards = $stmt->fetchAll(PDO::FETCH_ASSOC);

echo json_encode($cards);

Клиентская часть (JavaScript):

Пример

let page = 1;
let loading = false;

window.addEventListener('scroll', () => {
    if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100 && !loading) {
        loading = true;
        page++;
        fetch(`api/load.php?page=${page}`)
            .then(response => response.json())
            .then(cards => {
                cards.forEach(card => {
                    const div = document.createElement('div');
                    div.className = 'card';
                    div.innerHTML = `

${card.title}

${card.content}

`; document.getElementById('cards').appendChild(div); }); loading = false; }); } });

Результат:

При прокрутке вниз автоматически подгружаются следующие 4 карточки, добавляясь в конец списка. Страница не перезагружается.

Пояснение: Клиент отслеживает положение скролла и отправляет запрос с номером страницы. PHP возвращает следующую порцию данных в JSON. Важно установить флаг loading для предотвращения одновременных запросов.

карточки PHP - comments

En
Cards php card (php)