Salut, in acest tutorial vom invata despre crearea unui sistem simplu de paginare al rezultatelor.Daca nu cunosteti acest termen, "paginare", ei bine, puteti cauta in dex ( serios ), glumesc desigur! Ei bine, problema apare cand avem un numar foarte mare de rezultate preluate dintr-un tabel din baza de date.Evident, avem nevoie sa afisam aceste rezultate, insa nu este o idee asa de buna sa le afiam pe toate unul dupa altul.
Un exemplu foarte simplu este afisarea unei liste cu toti utilizatorii inregistrati, ganditi-va o secunda, pot fi sute, mii de utilizatori, deci, avem o problema.Aceasta problema se poate rezolva prin crearea acestui sistem de paginare.
Idee dupa care vom crea acest sistem este simpla, vom afisa un numar de rezultate pe mai multe pagini, folosind un acelasi fisier.Pentru a tine evidenta numarului paginii, vom prelua din URL, mai precis din query string aceasta valoare.
Sa cream un folder tutorial, in interiorul acestui folder vom crea fisierul index.php si de asemenea, vom mai crea inca un folder, numit, includes.In interiorul acestui folder vom crea fisierul connect.php.Acest fisier este folosit pentru realizarea conexiunii cu baza de date folosind PDO.Vom mai avea de creat alte foldere, dar mai tarziu.
tutorial/includes/connect.php
<?php
try {
$conn = new PDO("mysql:host=localhost;dbname=tutorial", 'root', 'root');
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo $e->getMessage();
exit;
}
Dupa cum puteti vedea, baza de date folosita este "tutorial", asa ca este bine sa creati aceasta baza de date.
In continuare, ne aflam in fisierul tutorial/index.php si incepem prin adaugarea codului necesar gestionarii erorilor si de asemenea, includem fisierul tutorial/includes/connect.php
index.php
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require_once __DIR__ . '/includes/connect.php';
In continuare, folosindu-ne de obiectul PDO stocat in variabila $conn, vom crea tabelul ( atentie, numai daca acesta nu a fost creat deja ) pe care il vom folosi in acest tutorial.Daca nu stiati, putem crea tabele folosind direct PHP, fara a accesa o interfata precum phpmyadmin.
$sql = "CREATE TABLE IF NOT EXISTS users(
id int NOT NULL AUTO_INCREMENT,
username varchar(256) NOT NULL,
email varchar(256) NOT NULL,
created_at timestamp,
updated_at timestamp NULL,
PRIMARY KEY(id)
)";
// executam o propozitie SQL si primim ca raspuns numarul de randuri afectate
$table = $conn->exec($sql);
Dupa cum puteti observa tabelul users este foarte simplu.In continuare vrem sa adaugam ceva date in acest tabel.
$conn->exec("INSERT INTO users (username, email) VALUES ('Catalin', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Marian', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Vladut', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('John', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Cristian', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Hercule', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Zeus', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Hades', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Poseidon', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Afrodita', '[email protected]')");
$conn->exec("INSERT INTO users (username, email) VALUES ('Hera', '[email protected]')");
Acum, accesati adresa http://localhost/index.php, datele vor fi adaugate, ceea ce inseamna ca trebuie sa eliminam codul de mai sus.Fisierul tutorial/index.php trebuie sa fie identic cu ceea ce avem mai jos:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require_once __DIR__ . '/includes/connect.php';
$sql = "CREATE TABLE IF NOT EXISTS users(
id int NOT NULL AUTO_INCREMENT,
username varchar(256) NOT NULL,
email varchar(256) NOT NULL,
created_at timestamp,
updated_at timestamp NULL,
PRIMARY KEY(id)
)";
// executam o propozitie SQL si primim ca raspuns numarul de randuri afectate
$table = $conn->exec($sql);
In continuare preluam numarul paginii din adresa URL folosindu-ne de query string si $_GET
, il stocam in variabila $page
, insa daca nu avem acest numar in URL, stocam in variabila $page
valoarea 1, cu alte cuvinte, ne aflam in pagina 1.Folosindu-ne de type casting, ne asiguram ca valoarea preluata folosind $_GET
este de tip integer, cu alte cuvinte, convertim tipul de date al variabilei in integer.De asemenea, ne asiguram ca numarul paginii este mereu un numar pozitiv, astfel folosim functia abs()
.Aceasta intoarce valoarea absoluta ( pozitiva ) a unui numar.
// setam numarul paginii
if (isset($_GET['page'])) {
// folosind type casting, ne asiguram ca valoarea este de tip integer
$page = (int) $_GET['page'];
} else {
$page = 1;
}
// ne asiguram ca numarul paginii este mereu un numar pozitiv.
$page = abs($page);
Mai departe, avem nevoie de numarul total de rezultate.Vom folosi o interogare SQL prin care, folosind functia COUNT(*) AS total_results
, numaram toate rezultatele si stocam acest numar sub numele de "total_results".Evident, folosim PDO si preluam rezultele sub forma de matrice asociativa.
// preluam numarul total de rezultate
$results = $conn->query("SELECT COUNT(*) AS total_results FROM users");
$results = $results->fetch(PDO::FETCH_ASSOC);
$results = $results['total_results'];
Dupa cum puteti vedea, am stocat in variabila $results
numarul total de rezultate.Probabil va intrebati de ce avem nevoie de acest numar, ei bine, va voi explica imediat.
Cum am spus, avem nevoie de un numar de rezultate pe pagina, deci vom stoca in variabila `$resultsPerPage
valoarea 2, ceea ce inseamna ca maximul de rezultate afisate pe fiecare pagina este 2.
// setam un numar de rezultate pe pagina
$resultsPerPage = 2;
Acum explicam de ce avem nevoie de numarul total de rezultate.Ei bine, trebuie sa aflam cate pagini avem in total, ceea ce inseamna ca impartim numarul total de pagini la numarul de rezultate pe fiecare pagina.Insa, nu in ultimul rand, folosind functia ceil()
rotunjim in sus valoarea rezultata in urma impartirii.Daca stati sa va ganditi o secunda, rezultatul impartirii poate fi un numar cu virgula, nu putem avea 4.5 pagini.
// aflam numarul total de pagini
$totalPages = ceil($results/$resultsPerPage);
In continuare ne asiguram ca numarul paginii nu poate depasi numarul total de rezultate, cu alte cuvinte, daca acest lucru se intampla, rescriem valoarea variabilei $page
cu valoarea 1.De aseemenea, ne asiguram ca numarul paginii nu este 0.
// ne asiguram ca numarul pagini nu depaseste numarul total de pagini si nu are valorea 0
if ($page > $totalPages || $page === 0) {
$page = 1;
}
Ei bine, am ajuns in momentul in care trebuie sa preluam un numar limitat de rezultate pe fiecare pagina.
Pai este foarte simplu. in primul rand vreau sa preiau doar un anumit numar de rezultate, acest numar fiind stocat in variabila $resultsPerPage
si de asemenea, vreau mereu sa preiau, pe fiecare pagina, rezultatele incepand de la o anumita valoare.
De exemplu, am 10 rezultate si preiau pe fiecare pagina 2 rezultate, ceea ce inseamna ca am 5 pagini.Ei bine, pe prima pagina voi avea primele 2 rezultete, pe urmatoare pagina, urmatoarele 2 rezultate, si tot asa pana la final.
Pentru a putea face acest lucru, trebuie, in primul rand, sa inmultim numarul paginii, cu numarul de rezultate pe pagina si sa scadem numarul de rezultate pe pagina.
// preluam rezultate incepand cu rezultatul de la valoarea variabilei $start
$start = ($page * $resultsPerPage) - $resultsPerPage;
Propozitia SQL folosita este putin mai complexa, insa voi incerca sa va explic cat mai bine, SELECT * FROM users LIMIT :resultsPerPage OFFSET :start
.
Daca propozitia SQL este de forma SELECT * FROM users LIMIT 10
, inseamna ca vom prelua primele 10 rezultate LIMIT 10
incepand cu rezultatul 0.
Daca propozitia SQL este de forma SELECT * FROM users LIMIT 10 OFFSET 2
, insemna ca vom prelua primele 10 rezultate LIMIT 10
dar incepand cu rezultatul 2 OFFSET 2
, deci de la 2 in sus.
Acum intelegeti de ce avem nevoie de valoarea stocata in variabila $start
, acea valoare va creste mereu cu 2, ceea ce inseamna ca vom prelua urmatorele 2 rezultate ( 0 - 2, 2 - 4, 4 - 6, si asa mai departe ).
// preluam un numar limitat de rezultate
$users = $conn->prepare("SELECT * FROM users LIMIT :resultsPerPage OFFSET :start");
$users->bindValue(':resultsPerPage', $resultsPerPage, PDO::PARAM_INT);
$users->bindValue(':start', $start, PDO::PARAM_INT);
$users->execute();
$users = $users->fetchAll(PDO::FETCH_ASSOC);
De asemenea, lucram cu PDO, folosind prepared statements.Folosind metoda bindValue()
ne asiguram ca vom inlocui in propozitia SQL atat ":resultsPerPage" cat si ":start" cu valorile variabilelor $resultsPerPage
si $start
.Nu in ultimul rand, ne asiguram ca aceste valori sunt de tip integer adaugand ca ultim parametru pentru metoda bindValue`` constanta
PDO::PARAM_INT```.
Stocam rezultatele in variabila $users
, variabila ce este o matrice si contine fiecare rezultat sub forma de matrice.
In continuare, trebuie sa cream variabilele pentru a putea naviga catre pagina anterioara sau pagina urmatoare.
// verificam daca ne putem intoarce catre o pagina anterioara
if ($page > 1) {
$prevPage = $page - 1;
}
// verificam daca putem merge catre urmatoarea pagina
if ($page + 1 <= $totalPages) {
$nextPage = $page + 1;
}
Nimic complicat, in prima structura de control, cream variabila $prevPage
numai daca numarul pagini este mai mare de 1, 1 fiind valoare pentru prima pagina.Urmatoarea structura de control, este folosita pentru a verifica daca urmatoarea pagina nu este mai mare sau este egala cu numarul total de pagini, nu vrem sa depasim acest numar, asta este ideea.
In finalul fisierului tutorial/index.php trebuie sa adaugam partea de prezentare a datelor.
// includem partea ce se ocupa cu prezentarea datelor
require_once __DIR__ . '/views/home.php';
Deci, fisierul tutorial/index.php trebuie sa fie identic cu ce avem mai jos:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
require_once __DIR__ . '/includes/connect.php';
$sql = "CREATE TABLE IF NOT EXISTS users(
id int NOT NULL AUTO_INCREMENT,
username varchar(256) NOT NULL,
email varchar(256) NOT NULL,
created_at timestamp,
updated_at timestamp NULL,
PRIMARY KEY(id)
)";
// executam o propozitie SQL si primim ca raspuns numarul de randuri afectate
$table = $conn->exec($sql);
// setam numarul paginii
if (isset($_GET['page'])) {
// folosind type casting, ne asiguram ca valoarea este de tip integer
$page = (int) $_GET['page'];
} else {
$page = 1;
}
// ne asiguram ca numarul paginii este mereu un numar pozitiv.
$page = abs($page);
// preluam numarul total de rezultate
$results = $conn->query("SELECT COUNT(*) AS total_results FROM users");
$results = $results->fetch(PDO::FETCH_ASSOC);
$results = $results['total_results'];
// setam un numar de rezultate pe pagina
$resultsPerPage = 2;
// aflam numarul total de pagini
$totalPages = ceil($results/$resultsPerPage);
// ne asiguram ca numarul pagini nu depaseste numarul total de pagini si nu are valorea 0
if ($page > $totalPages || $page === 0) {
$page = 1;
}
// preluam rezultate incepand cu rezultatul de la valoarea variabilei $start
$start = ($page * $resultsPerPage) - $resultsPerPage;
// preluam un numar limitat de rezultate
$users = $conn->prepare("SELECT * FROM users LIMIT :resultsPerPage OFFSET :start");
$users->bindValue(':resultsPerPage', $resultsPerPage, PDO::PARAM_INT);
$users->bindValue(':start', $start, PDO::PARAM_INT);
$users->execute();
$users = $users->fetchAll(PDO::FETCH_ASSOC);
// verificam daca ne putem intoarce catre o pagina anterioara
if ($page > 1) {
$prevPage = $page - 1;
}
// verificam daca putem merge catre urmatoarea pagina
if ($page + 1 <= $totalPages) {
$nextPage = $page + 1;
}
// includem partea ce se ocupa cu prezentarea datelor
require_once __DIR__ . '/views/home.php';
Ei bine, trebuie sa cream si acest fisier pentru prezentare datelor, dar inainte, sa cream folderul tutorial/resources, in interiorul acestui folder vom crea folderul tutorial/resources/css, iar in interiorul acestui folder vom adauga fisierul style.css.Acest fisier este foia de stil, ce contine toate regulilele CSS folosite pentru a descrie prezentarea structurii HTML din fisierul views/home.php
tutorial/resources/css/style.css
body {
background-color: #edf0f1;;
font-family: 'Helvetica', sans-serif;
color: #444;
line-height: 1.5;
}
.container {
width: 80%;
margin: 0 auto;
}
.container h1 {
padding-left: 10px;
margin-bottom: 0;
}
.main {
width: 100%;
background-color: #fff;
padding: 10px;
box-shadow: 0 1px 2px #ccc;
}
.main table {
width: 100%;
text-align: center;
}
.main table tr:first-child {
color: #000;
}
.pagination {
list-style: none;
padding: 0;
margin: 0;
margin-top: 10px;
float: left;
}
.pagination li {
display: inline-block;
}
.pagination li a {
background: transparent;
display: block;
text-decoration: none;
color: #444;
padding: 5px 10px;
border-radius: 2px;
}
.pagination li a:hover {
background-color: #25b99a;
color: #fff;
}
.pagination li a.active {
background-color: #25b99a;
color: #fff;
transition: opacity .5s;
}
.pagination li a.active:hover {
opacity: 0.8;
}
Acum ca am ajuns aproape la final, sa cream folderul tutorial/views si in interiorul acestui foloder, views, sa cream fisierul home.php.
tutorial/views/home.php
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PHP - Sistem de paginare</title>
<link rel="stylesheet" type="text/css" href="resources/css/style.css">
</head>
<body>
<div class="container">
<h1>Users</h1>
<div class="main">
<?php if ($results): ?>
<table>
<tr>
<td>Username</td>
<td>Email</td>
<td>Register Date</td>
</tr>
<?php foreach ($users as $user): ?>
<tr>
<td><?php echo $user['username'];?></td>
<td><?php echo $user['email'];?></td>
<td><?php echo $user['created_at'];?></td>
</tr>
<?php endforeach; ?>
</table>
<?php else: ?>
<p>There are no more results</p>
<?php endif; ?>
</div>
<?php if ($results > 2): ?>
<ul class="pagination">
<?php if (isset($prevPage)): ?>
<li><a href="index.php?page=<?php echo $prevPage; ?>">➖</a></li>
<?php endif; ?>
<li><a class="active" href="index.php?page=<?php echo $page; ?>"><?php echo $page; ?></a></li>
<?php if (isset($nextPage)): ?>
<li><a href="index.php?page=<?php echo $nextPage; ?>">✚</a></li>
<?php endif; ?>
</ul>
<?php endif; ?>
</div>
</body>
</html>
Nimic complicat, in prima strucutura de control, din div-ul ce are clasa .main, verificam daca avem rezultate.Apoi, folosind un tabel si structura de iterare foreach, navigam prin toate elementele matricei `$users
, elemente ce sunt matrici asociative, de altfel, si afisam detalii despre fiecare utilizator.
Apoi, folosind urmatoarea structura de control, verificam daca avem mai mult de 2 rezultate, 2 fiind valoarea variabilei $resultsPerPage
, cu alte cuvinte, nu vrem sa paginam rezultele daca avem mai putin de doua.
In interiorul listei neordonate ul, ce are clasa pagination facem niste verificari pentru a putea naviga catre pagina anterioara sau urmatoarea pagina.
Daca exista variabila $prevPage, inseamna ca putem naviga catre pagina anterioara.Dupa cum puteti vedea, avem un link ce contine ca adresa valoarea index.php?page=<?php echo $prevPage; ?>
, ei bine, cum am spus, folosim fisierul index.php, insa preluand cu $_GET['page']
valoarea paginii din query string ( query string reprezinta tot cee ce se afla dupa semnul intrebarii ) aflam numarul paginii curente, in cazul nostru variabila $prevPage
contine numarul paginii anterioare ( am discutat cand am scris codul din fisierul index.php ).
<?php if (isset($prevPage)): ?>
<li><a href="index.php?page=<?php echo $prevPage; ?>">➖</a></li>
<?php endif; ?>
Apoi avem un buton pentru pagina curenta.
<li><a class="active" href="index.php?page=<?php echo $page; ?>"><?php echo $page; ?></a></li>
Ultimul buton este cel folosit pentru pagina urmatoare, exact ceea ce am discutat mai sus, cu exceptia faptului ca este vorba de variabila $nextPage
.
<?php if (isset($nextPage)): ?>
<li><a href="index.php?page=<?php echo $nextPage; ?>">✚</a></li>
<?php endif; ?>
Am pus o imagine mai joc cu versiunea finala a proiectului.
Inca un articol urias, insa eu cred ca cine doreste sa invete cu adevarat, nu tine cont de asa ceva!
Apropo, am folosit ceea ce am scris intr-un articol, si anume, am separat partea de prezentare a datelor de partea ce se ocupa cu gestionarea datelor, puteti gasi acest articol pe site.