MySQL ve PHP ile Sayfalama (Pagination) Uygulaması: Ubuntu 18.04 Üzerinde Detaylı Rehber
Giriş
Web uygulamalarında büyük veri setlerini kullanıcıya sunarken, tüm veriyi tek seferde yüklemek hem sunucu performansı hem de kullanıcı deneyimi açısından verimsizdir. Bu noktada “sayfalama” (pagination) devreye girer. Sayfalama, verileri daha küçük, yönetilebilir parçalara bölerek her sayfada belirli sayıda kayıt gösterme işlemidir. Bu rehberde, Ubuntu 18.04 işletim sistemi üzerinde PHP ve MySQL kullanarak sayfalama işlemini adım adım nasıl uygulayacağınızı detaylı kod örnekleriyle öğreneceksiniz. Sayfalama, uygulamanızın daha hızlı çalışmasını sağlamakla kalmaz, aynı zamanda kullanıcıların aradıkları bilgiye daha kolay ulaşmalarına yardımcı olur.
Ortam Kurulumu ve Veritabanı Hazırlığı
Sayfalama uygulamasını geliştirmeye başlamadan önce, Ubuntu 18.04 sunucunuzda gerekli ortamın kurulu olduğundan emin olmalıyız. Apache web sunucusu, PHP ve MySQL veritabanı sunucusu temel gereksinimlerdir.
sudo apt update
sudo apt install apache2 php libapache2-mod-php php-mysql mysql-server -y
sudo mysql_secure_installation # MySQL güvenliğini yapılandırmak için
Kurulum tamamlandıktan sonra, sayfalama örneğimiz için bir veritabanı ve örnek bir tablo oluşturalım. MySQL’e giriş yapın:
sudo mysql -u root -p
Şimdi veritabanını ve tabloyu oluşturalım:
CREATE DATABASE pagination_db;
USE pagination_db;
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Örnek veri ekleyelim (en az 50-100 kayıt eklemeniz önerilir)
INSERT INTO products (name, description, price) VALUES
('Akıllı Telefon X', 'Yüksek performanslı akıllı telefon.', 999.99),
('Dizüstü Bilgisayar Y', 'İnce ve hafif dizüstü bilgisayar.', 1250.00),
('Akıllı Saat Z', 'Nabız ölçer ve bildirim özellikli.', 199.50),
('Kablosuz Kulaklık A', 'Gürültü önleyici özellikli kulaklık.', 120.00),
('Taşınabilir Şarj Cihazı B', '10000 mAh kapasiteli.', 35.75),
('Oyun Konsolu C', 'En yeni nesil oyun konsolu.', 499.00),
('4K Televizyon D', 'Geniş ekran, canlı renkler.', 750.00),
('Robot Süpürge E', 'Otomatik temizlik robotu.', 300.25),
('Kahve Makinesi F', 'Espresso ve filtre kahve yapabilen.', 85.00),
('Akıllı Ampul G', 'Renk değiştirebilen LED ampul.', 15.99);
-- Daha fazla veri eklemek için bu satırı birçok kez çalıştırabilirsiniz.
-- Örnek:
INSERT INTO products (name, description, price)
SELECT CONCAT('Ürün ', id + 10), CONCAT('Bu harika bir üründür: ', id + 10), ROUND(RAND() * 1000 + 10, 2)
FROM products;
-- Bu sorguyu birkaç kez çalıştırarak yüzlerce kayıt oluşturabilirsiniz.
-- Örneğin, 3 kez çalıştırmak 10 -> 20 -> 40 -> 80 kayıt yapar.
-- 6-7 kez çalıştırmak 1000'den fazla kayıt oluşturur.
Sayfalama Mantığı ve MySQL Sorguları
Sayfalama işleminin temelinde MySQL’in LIMIT ve OFFSET anahtar kelimeleri yatar.
* LIMIT: Bir sorgudan dönecek maksimum kayıt sayısını belirler. Örneğin, LIMIT 10 sadece ilk 10 kaydı getirir.
* OFFSET: Sorgunun başlangıç noktasını belirler. Belirtilen sayı kadar kaydı atlar ve sonrasındaki kayıtları getirir. Örneğin, OFFSET 20 ilk 20 kaydı atlayıp 21. kayıttan itibaren getirir.
Sayfalama için bu iki anahtar kelimeyi birleştirerek kullanırız: LIMIT [kayıt_sayısı] OFFSET [atlama_sayısı].
Örnek: Her sayfada 10 kayıt göstermek istediğimizi varsayalım:
Sayfa 1: SELECT FROM products LIMIT 10 OFFSET 0; (İlk 10 kayıt)
Sayfa 2: SELECT FROM products LIMIT 10 OFFSET 10; (11. kayıttan itibaren 10 kayıt)
Sayfa 3: SELECT FROM products LIMIT 10 OFFSET 20; (21. kayıttan itibaren 10 kayıt)
Genel formül şöyledir:
OFFSET = (current_page - 1) * records_per_page
Ayrıca, toplam sayfa sayısını belirlemek için veritabanındaki toplam kayıt sayısını bilmemiz gerekir. Bunun için COUNT(*) fonksiyonunu kullanırız:
SELECT COUNT(*) FROM products;
Toplam kayıt sayısını (örneğin total_records) ve her sayfada gösterilecek kayıt sayısını (records_per_page) bildiğimizde, toplam sayfa sayısını şu formülle hesaplayabiliriz:
total_pages = ceil(total_records / records_per_page) (PHP’deki ceil() fonksiyonu yukarı yuvarlama yapar).
PHP ile Sayfalama Uygulaması
Şimdi bu mantığı PHP koduna dökelim. index.php adında bir dosya oluşturalım ve Apache’nin belge kök dizinine (genellikle /var/www/html/) yerleştirelim.
1. Veritabanı Bağlantısı (db_config.php)
Güvenli ve tekrar kullanılabilir bir veritabanı bağlantısı için PDO (PHP Data Objects) kullanacağız. db_config.php adında bir dosya oluşturalım:
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>
Önemli: $pass kısmına kendi MySQL root şifrenizi yazmayı unutmayın. Güvenlik açısından root kullanıcısı yerine ayrı bir veritabanı kullanıcısı oluşturmanız önerilir.
2. Ana Sayfalama Kodu (index.php)
Şimdi index.php dosyamızı oluşturalım ve sayfalama mantığını uygulayalım:
prepare("SELECT COUNT(*) FROM products");
$stmt->execute();
$total_records = $stmt->fetchColumn();
$total_pages = ceil($total_records / $records_per_page);
// Mevcut sayfa numarasının toplam sayfa sayısından büyük olmamasını sağla
if ($current_page > $total_pages && $total_pages > 0) {
$current_page = $total_pages;
} elseif ($total_pages == 0) { // Hiç kayıt yoksa, sayfa 1 olarak kalsın
$current_page = 1;
}
// --- OFFSET Değerini Hesaplama ---
$offset = ($current_page - 1) * $records_per_page;
// --- Verileri Çekme ---
$stmt = $pdo->prepare("SELECT * FROM products ORDER BY id DESC LIMIT :limit OFFSET :offset");
$stmt->bindValue(':limit', $records_per_page, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$products = $stmt->fetchAll();
} catch (\PDOException $e) {
echo "Veritabanı hatası: " . $e->getMessage();
exit();
}
?>
PHP MySQL Sayfalama Örneği
Ürün Listesi
0): ?>
= htmlspecialchars($product['name']) ?>
= htmlspecialchars($product['description']) ?>
Fiyat: = htmlspecialchars($product['price']) ?> TL
Oluşturulma Tarihi: = htmlspecialchars($product['created_at']) ?>
Gösterilecek ürün bulunmamaktadır.
1): ?>
Önceki
1) {
echo '1';
if ($start_page > 2) {
echo '...';
}
}
for ($i = $start_page; $i <= $end_page; $i++):
?>
= $i ?>
...';
}
echo '' . $total_pages . '';
}
?>
Sonraki
Kod Açıklamaları:
1. db_config.php Dahil Etme: PDO ile veritabanı bağlantısı kurulur ve $pdo nesnesi index.php‘ye aktarılır.
2. Sayfalama Ayarları: $records_per_page değişkeni, her sayfada kaç kayıt gösterileceğini belirler.
3. Mevcut Sayfa: URL’den $_GET['page'] parametresi ile mevcut sayfa numarası alınır. Geçersiz veya eksikse varsayılan olarak 1 atanır. Güvenlik için is_numeric() kontrolü ve (int) ile tip dönüşümü yapılır.
4. Toplam Kayıt ve Sayfa Sayısı:
SELECT COUNT() sorgusu ile veritabanındaki toplam kayıt sayısı öğrenilir.
* ceil($total_records / $records_per_page) ile toplam sayfa sayısı hesaplanır. ceil() fonksiyonu, küsuratlı sonuçları yukarı yuvarlayarak tam sayfa sayısını verir (örneğin, 21 kayıt ve 10 kayıt/sayfa için 3 sayfa).
* $current_page değeri, $total_pages değerini aşarsa, son sayfaya yönlendirilir.
5. OFFSET Hesaplama: $offset değeri, mevcut sayfa numarası ve sayfa başına kayıt sayısı kullanılarak hesaplanır.
6. Verilerin Çekilmesi:
SELECT FROM products ORDER BY id DESC LIMIT :limit OFFSET :offset sorgusu hazırlanır. ORDER BY id DESC ile en yeni kayıtların önce gelmesi sağlanır.
* PDO’nun prepared statement özelliği sayesinde :limit ve :offset yer tutucularına bindValue() ile güvenli bir şekilde değerler atanır. Bu, SQL Injection saldırılarını önlemek için kritik bir adımdır.
* fetchAll() ile ilgili sayfanın tüm ürünleri çekilir.
7. HTML ve Veri Gösterimi: Çekilen ürünler bir foreach döngüsü ile HTML içinde listelenir. htmlspecialchars() fonksiyonu, kullanıcıdan gelen verileri tarayıcıda güvenli bir şekilde göstermek için kullanılır (XSS saldırılarına karşı koruma).
8. Sayfalama Bağlantıları Oluşturma:
* “Önceki” ve “Sonraki” bağlantıları, mevcut sayfa numarasına göre etkinleştirilir veya devre dışı bırakılır.
* Sayfa numaralarını gösteren dinamik bağlantılar oluşturulur. max() ve min() fonksiyonları kullanılarak, mevcut sayfanın etrafında belirli sayıda sayfa (örneğin, 2 sayfa önce ve 2 sayfa sonra) gösterilir. Bu, çok fazla sayfa olduğunda bağlantı listesinin çok uzun olmasını engeller.
* current-page sınıfı ile mevcut sayfa vurgulanır.
* disabled sınıfı ile geçersiz (örneğin ilk sayfada “Önceki”) bağlantılar etkisiz hale getirilir.
Bu dosyaları /var/www/html/ dizinine kaydettikten sonra, web tarayıcınızda http://localhost/index.php adresine giderek sayfalama uygulamasını çalıştırabilirsiniz.
Sonuç ve Sıkça Sorulan Sorular (SSS)
Bu rehberde, PHP ve MySQL kullanarak temel bir sayfalama sistemini Ubuntu 18.04 üzerinde nasıl kuracağınızı ve uygulayacağınızı öğrendiniz. Sayfalama, büyük veri setlerini yönetmek ve kullanıcı deneyimini iyileştirmek için modern web uygulamalarının vazgeçilmez bir parçasıdır.
Sıkça Sorulan Sorular (SSS):
1. Sayfalama performansını nasıl artırabilirim?
* İndeksler Kullanın: ORDER BY ve WHERE koşullarında kullandığınız sütunlara indeks eklemek (örneğin products tablosundaki id sütunu zaten PRIMARY KEY olarak indekslidir) sorgu hızını önemli ölçüde artırır.
* Doğru LIMIT/OFFSET Kullanımı: Çok büyük OFFSET değerleri, MySQL’in daha fazla satırı taramasına neden olabilir. Mümkünse “keyset pagination” veya “cursor-based pagination” gibi alternatif yöntemleri araştırabilirsiniz. Bu yöntemler, son görülen kaydın ID’sini kullanarak bir sonraki sayfayı getirir ve OFFSET‘in performans sorununu ortadan kaldırır.
COUNT() yerine COUNT(id): Performans açısından genellikle fark etmese de, bazı durumlarda COUNT(primary_key_column) daha hızlı olabilir.
2. Büyük veri setleri için OFFSET tabanlı sayfalama neden sorunlu olabilir?
OFFSET tabanlı sayfalama, veritabanının OFFSET değerine kadar olan tüm kayıtları tarayıp sonra LIMIT kadarını seçmesini gerektirdiği için büyük veri setlerinde yavaşlayabilir. Örneğin, 1.000.000 kaydı olan bir tabloda 100.000. kayıttan itibaren 10 kayıt almak için veritabanı ilk 100.000 kaydı okumak zorunda kalır.
3. Güvenlik konusunda nelere dikkat etmeliyim?
* SQL Injection: Kullanıcıdan gelen verileri (örneğin $_GET['page']) doğrudan SQL sorgularına eklemeyin. Prepared statement’lar (PDO’da prepare() ve bindValue()) kullanarak SQL Injection saldırılarını önleyin.
* XSS (Cross-Site Scripting): Kullanıcıdan gelen veya veritabanından çekilen verileri HTML çıktısına basarken htmlspecialchars() gibi fonksiyonlarla temizleyin.
* Yetkilendirme: Veritabanı bağlantısı için root kullanıcısı yerine, sadece gerekli yetkilere sahip ayrı bir MySQL kullanıcısı oluşturun.
4. Sayfalama bağlantılarını dinamik olarak nasıl oluştururum?
Yukarıdaki örnekte, for döngüsü ve max(), min() fonksiyonları kullanılarak mevcut sayfanın etrafında belirli sayıda sayfa bağlantısı (örneğin 1 ... 3 4 [5] 6 7 ... 100) dinamik olarak oluşturulmuştur. Bu, çok sayıda sayfa olduğunda kullanıcı arayüzünün daha temiz kalmasını sağlar.
5. Sayfalama ile birlikte arama veya filtreleme nasıl entegre edilir?
Arama veya filtreleme parametrelerini de URL’ye $_GET değişkenleri olarak ekleyebilirsiniz. Örneğin: ?page=2&search=telefon&category=elektronik. PHP kodunuzda WHERE koşuluna bu parametreleri ekleyerek sorguyu güncellemeli ve sayfalama bağlantılarını oluştururken mevcut arama/filtreleme parametrelerini de korumalısınız:
// Örnek: Arama parametresi
$search_query = isset($_GET['search']) ? $_GET['search'] : '';
$base_url = "?";
if (!empty($search_query)) {
$base_url .= "search=" . urlencode($search_query) . "&";
}
// Sayfalama bağlantıları oluşturulurken:
// = $i ?>
Bu şekilde, kullanıcı arama veya filtreleme yaparken sayfalama da düzgün bir şekilde çalışmaya devam eder.
Umarım bu rehber, PHP ve MySQL ile sayfalama uygulamasını başarıyla hayata geçirmenize yardımcı olur!