Paginacja (stronicowanie) wyników z bazy danych
- Baza danych
- poziom: średni
- aktualizacja: 12 lipca 2026
Gdy rekordów są setki, nie wyświetla się ich wszystkich naraz — dzieli się je na strony. Ten skrypt pokazuje kompletną paginację: liczy rekordy, pilnuje zakresu numeru strony, pobiera właściwy wycinek danych i rysuje eleganckie linki z wielokropkami.
PHP
115 linii
Pobierz .php
<?php
/**
* Paginacja wyników z bazy — skryptyPHP.com
* Zapisz jako: artykuly.php
*
* Przykładowa tabela:
* CREATE TABLE artykuly (
* id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
* tytul VARCHAR(200) NOT NULL,
* dodano DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
*/
require 'baza.php'; // skrypt „Połączenie z bazą (PDO)"
$pdo = polacz_z_baza();
// ── USTAWIENIA ──────────────────────────────────────────────
$na_strone = 10; // ile rekordów na jednej stronie
// ── OBLICZENIA ──────────────────────────────────────────────
$ile_rekordow = (int) $pdo->query('SELECT COUNT(*) FROM artykuly')->fetchColumn();
$ile_stron = max(1, (int) ceil($ile_rekordow / $na_strone));
// Numer strony z adresu — przycięty do dozwolonego zakresu
$strona = (int) ($_GET['strona'] ?? 1);
$strona = max(1, min($strona, $ile_stron));
$offset = ($strona - 1) * $na_strone;
// LIMIT i OFFSET podajemy jako parametry typu INT (nie sklejamy z SQL-em)
$stmt = $pdo->prepare(
'SELECT id, tytul, dodano
FROM artykuly
ORDER BY dodano DESC
LIMIT :limit OFFSET :offset'
);
$stmt->bindValue(':limit', $na_strone, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$artykuly = $stmt->fetchAll();
/**
* Zwraca listę numerów stron do wyświetlenia, np. [1, '…', 4, 5, 6, '…', 20].
* $otoczenie — ile numerów pokazać z każdej strony bieżącej.
*/
function numery_paginacji(int $biezaca, int $ostatnia, int $otoczenie = 2): array
{
$numery = [];
$poprzedni = 0;
for ($i = 1; $i <= $ostatnia; $i++) {
$pokaz = $i === 1 // pierwsza
|| $i === $ostatnia // ostatnia
|| abs($i - $biezaca) <= $otoczenie; // okolice bieżącej
if ($pokaz) {
if ($poprzedni !== 0 && $i - $poprzedni > 1) {
$numery[] = '…'; // przerwa w numeracji
}
$numery[] = $i;
$poprzedni = $i;
}
}
return $numery;
}
?>
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Artykuły — strona <?= $strona ?></title>
<style>
body { font-family: sans-serif; max-width: 640px; margin: 20px auto; }
.paginacja { display: flex; gap: 6px; margin-top: 20px; flex-wrap: wrap; }
.paginacja a, .paginacja span {
padding: 6px 12px; border: 1px solid #ccc; border-radius: 4px;
text-decoration: none; color: #333;
}
.paginacja .biezaca { background: #333; color: #fff; border-color: #333; }
.paginacja .kropki { border: none; }
</style>
</head>
<body>
<h1>Artykuły (<?= $ile_rekordow ?>)</h1>
<ul>
<?php foreach ($artykuly as $a): ?>
<li><?= htmlspecialchars($a['tytul']) ?>
<small>(<?= htmlspecialchars($a['dodano']) ?>)</small></li>
<?php endforeach; ?>
</ul>
<nav class="paginacja" aria-label="Wybór strony">
<?php if ($strona > 1): ?>
<a href="?strona=<?= $strona - 1 ?>">« poprzednia</a>
<?php endif; ?>
<?php foreach (numery_paginacji($strona, $ile_stron) as $n): ?>
<?php if ($n === '…'): ?>
<span class="kropki">…</span>
<?php elseif ($n === $strona): ?>
<span class="biezaca"><?= $n ?></span>
<?php else: ?>
<a href="?strona=<?= $n ?>"><?= $n ?></a>
<?php endif; ?>
<?php endforeach; ?>
<?php if ($strona < $ile_stron): ?>
<a href="?strona=<?= $strona + 1 ?>">następna »</a>
<?php endif; ?>
</nav>
</body>
</html>
Jak użyć tego skryptu
- Podłącz plik
baza.php(skrypt „Bezpieczne połączenie z bazą MySQL") i zmień nazwę tabeli oraz kolumny na swoje. - Ustaw liczbę rekordów na stronę w zmiennej
$na_strone. - Numer strony przekazywany jest w adresie jako
?strona=3— funkcja przycinająca zakres sama obsłuży wartości spoza zakresu.
Warto wiedzieć
- Wartości
LIMITiOFFSETwiążemy przezbindValue(..., PDO::PARAM_INT)— MySQL nie pozwala przekazać ich jako tekstu, a sklejanie liczby z zapytaniem to prosta droga do błędów. - Numer strony zawsze przycinamy funkcjami
max()/min()— użytkownik może wpisać w adres cokolwiek. - Przy bardzo dużych tabelach (setki tysięcy wierszy)
OFFSETrobi się wolny — wtedy stosuje się tzw. keyset pagination (WHERE id < ostatnie_id), ale dla typowych stron ten skrypt w zupełności wystarczy. - Jeśli strony z paginacją mają trafiać do Google, dodaj w
<head>znacznikrel="canonical"wskazujący pierwszą stronę listy.
#paginacja
#stronicowanie
#pdo
#limit
#offset
Podobne skrypty
Baza danych
Bezpieczne połączenie z bazą MySQL (PDO)
Wzorcowe połączenie z bazą MySQL przez PDO: kodowanie utf8mb4, obsługa błędów przez wyjątki, wyłączona emulacja zapytań i przykład zapytania przygotowanego.
początkujący Zobacz kod → Baza danychCRUD — dodawanie, edycja i usuwanie rekordów (PDO)
Kompletny przykład CRUD w jednym pliku: lista rekordów, dodawanie, edycja i usuwanie z zapytaniami przygotowanymi PDO oraz tokenem CSRF chroniącym operacje zmieniające dane.
średni Zobacz kod → Baza danychWyszukiwarka na stronie z podświetlaniem wyników
Prosta, bezpieczna wyszukiwarka rekordów w MySQL: LIKE z poprawnym maskowaniem znaków specjalnych, obsługa wielu słów naraz i podświetlanie znalezionej frazy.
średni Zobacz kod →