Metin2Games Discord sunucusuna katılmak ve anlık destek almak için tıkla!
1

Sayfa Yenilemeden Yazı Yüklemek: PHP ve HTMX ile Daha Fazla Yükle Butonu, Sonsuz Kaydırma ve Canlı Yıl Filtresi Yapmak

Kayıtlı
1 Yıl 9 Ay 26 Gün
Katılım
10 Ocak 2025
Konular
163
Mesajlar
257
Tepkime puanı
36
Online süresi
11h 19m
Puan
53
Konum
Türkiye
Web sitesi
www.zyncra.shop
Kısa Bakış: JavaScript Yazmadan Sayfanın Bir Kısmını Güncellemek: HTMX Özniteliklerini PHP Sunucusuyla Çalışan Küçük Bir Blog Listesinde Denemek Blog listenizde 10 yazı gösteriyorsunuz. Ziyaretçi 11. yazıya ulaşmak için sayfa numarasına tıklıyor, tarayıcı da baştan sona her şeyi yeniden y...

📌 Konu İçeriği — Hızlı Bakış (📂 cPanel / Web • ⏱ ~9 dk okuma • 💻 Kod: HTML)
01. 2. Adım: HTMX'i npm ile İndirip Kendi Sunucunuzdan Yüklemek ve PHP Test Sunucusunu Açmak
02. 3. Adım: Sunucu Tarafında Örnek Yazı Verisini ve Sayfalama Fonksiyonunu Hazırlamak
03. 4. Adım: Daha Fazla Yükle Butonunu hx-get ve hx-swap ile Çalıştırmak
04. 5. Adım: revealed Olayıyla Sayfa Aşağı Kaydıkça Yazıları Kendiliğinden Yüklemek
05. Canlıya Almadan Önce: Önbellek, Güvenlik ve Pratik İpuçları

konu-0c505e9975.jpg

JavaScript Yazmadan Sayfanın Bir Kısmını Güncellemek: HTMX Özniteliklerini PHP Sunucusuyla Çalışan Küçük Bir Blog Listesinde Denemek

Blog listenizde 10 yazı gösteriyorsunuz. Ziyaretçi 11. yazıya ulaşmak için sayfa numarasına tıklıyor, tarayıcı da baştan sona her şeyi yeniden yüklüyor. Bunu "Daha fazla yükle" butonuna çevirmek için ya React gibi bir çatı kurmanız ya da fetch, DOM ve hata yönetimiyle uğraşmanız gerekiyor. HTMX bu işi HTML özniteliklerine bırakıyor. Sunucu zaten HTML üretiyor, siz sadece "şu tıklanınca şu adrese git, gelen parçayı şuraya koy" diyorsunuz.

Bu konuda küçük bir PHP blog listesi yapacağız. Üç davranış ekleyeceğiz: Daha fazla yükle butonu, sonsuz kaydırma ve sayfa yenilenmeden çalışan yıl filtresi. Veritabanı kullanmıyoruz, veri bir diziden geliyor. Böylece dikkat HTMX'te kalıyor.

1. Adım: HTMX'in Mantığını Anlamak, Tek Satır HTML ile Sunucudan Gelen Parçayı Sayfaya Yerleştirmek

HTMX'te her istek üç soruya cevap verir: ne zaman gönderilecek, ne gönderilecek, gelen cevap ne olacak. İstek türünü hx-get, hx-post, hx-put, hx-patch ve hx-delete belirler. Tetikleyici varsayılan olarak elemanın doğal olayıdır: input, textarea ve select için change, form için submit, geri kalan her şey için click. Değiştirmek için hx-trigger kullanırsınız. Özel değerleri de var: load, revealed (eleman görünür alana girince) ve intersect.

Cevabın nereye gideceğini hx-target (CSS seçici) ve hx-swap belirler. hx-swap değerleri şunlar: innerHTML (varsayılan), outerHTML, beforebegin, afterbegin, beforeend, afterend, delete ve none. Sunucu JSON değil HTML döndürür.

HTML:
<!-- Tıklayınca GET atar, butonun kendisini gelen cevapla değiştirir -->
<button hx-get="/merhaba.php" hx-swap="outerHTML">Tıkla</button>

<!-- Üzerine gelince GET atar, cevabı #kutu elemanının sonuna ekler -->
<span hx-get="/urunler.php" hx-trigger="mouseenter"
      hx-target="#kutu" hx-swap="beforeend">Üstüme gel</span>

Form dışındaki elemanlardan POST atarken gövdeye en yakın form içindeki alanlar girer. Daha fazlası gerekirse hx-include ve hx-params var. İsteği kodla değiştirmek istediğinizde htmx:configRequest olayını dinlersiniz.

2. Adım: HTMX'i npm ile İndirip Kendi Sunucunuzdan Yüklemek ve PHP Test Sunucusunu Açmak

Eski anlatımlarda HTMX'in CDN'den, 1.9 sürümüyle yüklendiğini görürsünüz. Şu an güncel çizgi 2.x. Dosyayı npm ile indirip kendi sunucunuzdan vermek hem sürümü sabitler hem de üçüncü taraf adrese bağımlılığı kaldırır. Bir de 2.x'te DELETE isteklerinin parametreleri gövdede değil URL'de gider, eski örnekleri kopyalarken bunu aklınızda tutun.

Bash:
npm init -y
npm install htmx.org@2
mkdir assets
cp node_modules/htmx.org/dist/htmx.min.js assets/htmx.min.js
php -S localhost:8000

Sayfanın head kısmına, src değeri /assets/htmx.min.js olan ve defer taşıyan bir betik etiketi eklemeniz yeterli. Derleme aracı gerekmiyor. Bir Vite ya da webpack projesindeyseniz htmx.org paketini giriş dosyanızda import etmeniz de olur.

3. Adım: Sunucu Tarafında Örnek Yazı Verisini ve Sayfalama Fonksiyonunu Hazırlamak

Küçük bir not: Bu konudaki üç PHP dosyasının kod bloklarında PHP'nin açılış etiketini yazmadım, çünkü forum o etiketi kod içinde kabul etmiyor. Dosyaları oluştururken yazilar.php, parcalar.php ve index.php dosyalarının en üst satırına PHP açılış etiketini (soru işaretli, php yazan standart etiket) kendiniz ekleyin. Kapanış etiketine ise hiç gerek yok.

yazilar.php dosyasında 35 yazılık sahte bir liste üretiyoruz. sayfaGetir() yıla göre süzüp istenen sayfayı veriyor. e() fonksiyonu da çıktıyı kaçışlıyor, bunu sonra hep kullanacağız.

PHP:
// yazilar.php - örnek veri ve sayfalama
// (dosyanın ilk satırına PHP açılış etiketini ekleyin)

function e(string $metin): string {
    return htmlspecialchars($metin, ENT_QUOTES, 'UTF-8');
}

function sayfaGetir(?int $yil, int $sayfa, int $adet = 10): array {
    $hepsi = [];
    for ($i = 1; $i <= 35; $i++) {
        $hepsi[] = ['baslik' => "Örnek Yazı $i", 'yil' => 2022 + ($i % 3)];
    }
    if ($yil !== null) {
        // yıl süzgeci
        $hepsi = array_values(array_filter($hepsi, fn($y) => $y['yil'] === $yil));
    }
    $toplam = max(1, (int) ceil(count($hepsi) / $adet));
    return [array_slice($hepsi, ($sayfa - 1) * $adet, $adet), $toplam];
}

konu-99f9a22196.jpg

4. Adım: Daha Fazla Yükle Butonunu hx-get ve hx-swap ile Çalıştırmak

Buton bir sonraki sayfayı ister, cevap olarak yeni kartlar ve (varsa) yeni bir buton gelir. Bu cevap eski butonun yerine geçer. Adresi http_build_query ile kurup e() ile kaçışlıyoruz. Eski örneklerde sorgu değeri şablona olduğu gibi basılıyordu, orada URL kodlama ve HTML kaçışı eksik kalıyor.

PHP:
// parcalar.php - sayfanın yenilenen kısımları
// (dosyanın ilk satırına PHP açılış etiketini ekleyin)

function kartlar(array $yazilar): void {
    foreach ($yazilar as $y) {
        echo '<div class="kart">' . e($y['baslik']) . ' (' . $y['yil'] . ')</div>';
    }
}

function dahaFazla(int $sayfa, int $toplam, ?int $yil): void {
    echo '<div class="daha-fazla">';
    if ($sayfa < $toplam) {
        $url = '/index.php?' . http_build_query([
            'sayfa' => $sayfa + 1, 'yil' => $yil, 'parca' => 'liste',
        ]);
        echo '<button hx-get="' . e($url) . '" hx-target=".daha-fazla"'
           . ' hx-swap="outerHTML">Daha fazla yükle</button>';
    }
    echo '</div>';
}

function liste(array $yazilar, int $sayfa, int $toplam, ?int $yil): void {
    kartlar($yazilar);
    dahaFazla($sayfa, $toplam, $yil);
}

function icerik(array $yazilar, int $sayfa, int $toplam, ?int $yil): void {
    liste($yazilar, $sayfa, $toplam, $yil);
}

Şimdi index.php. HTMX her isteğe HX-Request: true başlığını ekler. Bu başlık varsa sadece parça, yoksa tam sayfa döndürüyoruz. Ayrıca parca=liste parametresiyle "sadece liste" isteğini, yıl filtresinin isteğinden ayırıyoruz.

Sayfa iskeletini PHP ile HTML'i iç içe yazmak yerine bir heredoc içinde basıyoruz. ob_start() ile ob_get_clean() da icerik() fonksiyonunun çıktısını bir değişkene alıyor. Davranış aynı, dosya da tek bir PHP bloğundan oluşuyor.

PHP:
// index.php - giriş noktası
// (dosyanın ilk satırına PHP açılış etiketini ekleyin)

require __DIR__ . '/yazilar.php';
require __DIR__ . '/parcalar.php';

$yilHam = filter_var($_GET['yil'] ?? null, FILTER_VALIDATE_INT);
$yil    = $yilHam === false ? null : $yilHam;
$sayfa  = max(1, (int) ($_GET['sayfa'] ?? 1));
[$yazilar, $toplam] = sayfaGetir($yil, $sayfa);

header('Vary: HX-Request');
$htmxMi = ($_SERVER['HTTP_HX_REQUEST'] ?? '') === 'true';

if ($htmxMi) {
    if (($_GET['parca'] ?? '') === 'liste') {
        liste($yazilar, $sayfa, $toplam, $yil);
    } else {
        icerik($yazilar, $sayfa, $toplam, $yil);
    }
    exit;
}

// tam sayfa: içeriği önce değişkene alıyoruz
ob_start();
icerik($yazilar, $sayfa, $toplam, $yil);
$icerikHtml = ob_get_clean();

echo <<<HTML
<!doctype html>
<html lang="tr">
<head>
  <meta charset="utf-8">
  <title>Blog</title>
  <!-- htmx.min.js dosyasını burada bağlayın -->
</head>
<body>
  <h1>Blog</h1>
  <span id="yukleniyor" class="htmx-indicator">Yükleniyor</span>
  <div id="icerik">{$icerikHtml}</div>
</body>
</html>
HTML;

Buton hx-target=".daha-fazla" ve outerHTML ile kendi kutusunu değiştiriyor. Cevapta yeni kartlar ve yeni bir .daha-fazla kutusu olduğu için son sayfaya kadar zincir devam ediyor. Son sayfada kutu boş geliyor ve buton kayboluyor.

5. Adım: revealed Olayıyla Sayfa Aşağı Kaydıkça Yazıları Kendiliğinden Yüklemek

Sonsuz kaydırma için buton gerekmiyor. Listenin son kartına hx-trigger="revealed" veriyoruz. Kart ekrana girince bir sonraki sayfa istenir ve cevap afterend ile kartın hemen arkasına eklenir. kartlar() ile liste() fonksiyonlarını şöyle değiştirin:

PHP:
function kartlar(array $yazilar, int $sayfa, int $toplam, ?int $yil): void {
    $son = array_key_last($yazilar);
    foreach ($yazilar as $i => $y) {
        $ek = '';
        if ($i === $son && $sayfa < $toplam) {
            $url = '/index.php?' . http_build_query([
                'sayfa' => $sayfa + 1, 'yil' => $yil, 'parca' => 'liste',
            ]);
            // son karta tetikleyici ekle
            $ek = ' hx-get="' . e($url) . '" hx-trigger="revealed" hx-swap="afterend"';
        }
        echo '<div class="kart"' . $ek . '>' . e($y['baslik']) . '</div>';
    }
}

function liste(array $yazilar, int $sayfa, int $toplam, ?int $yil): void {
    kartlar($yazilar, $sayfa, $toplam, $yil);
}

dahaFazla() artık kullanılmıyor, silebilirsiniz. Olay bir kez tetiklendiği için eski son kart tekrar istek atmaz, yeni gelen son kart devralır.

6. Adım: Yıl Filtresini Sayfayı Yenilemeden Çalıştırmak ve hx-indicator ile Yükleniyor Göstergesi Eklemek

Filtrede listeyi eklemek yerine tamamen değiştiriyoruz, aktif butonun da güncellenmesi için filtre çubuğu cevabın içinde geliyor. Bu yüzden butonlar #icerik kutusunu hedefliyor ve parca parametresi göndermiyor. hx-indicator istek sürerken #yukleniyor elemanına htmx-request sınıfını ekler, htmx-indicator sınıfı da onu o sırada görünür yapar.

PHP:
function filtre(?int $aktif): void {
    echo '<div class="filtre">';
    foreach ([null, 2022, 2023, 2024] as $y) {
        $url = '/index.php?' . http_build_query(['yil' => $y]);
        $sinif = $y === $aktif ? 'aktif' : '';
        echo '<button class="' . $sinif . '" hx-get="' . e($url) . '"'
           . ' hx-target="#icerik" hx-swap="innerHTML"'
           . ' hx-indicator="#yukleniyor">' . ($y ?? 'Tümü') . '</button>';
    }
    echo '</div>';
}

function icerik(array $yazilar, int $sayfa, int $toplam, ?int $yil): void {
    filtre($yil);
    liste($yazilar, $sayfa, $toplam, $yil);
}

Yıl seçilince yil parametresi, sonraki sayfa adreslerine de taşınıyor. Yani 2023 filtresindeyken kaydırmak sadece 2023 yazılarını getiriyor.

Canlıya Almadan Önce: Önbellek, Güvenlik ve Pratik İpuçları

Önce parçaların doğru geldiğini curl ile görün:

Bash:
# Parça isteği: sadece kartlar gelmeli
curl -s -H "HX-Request: true" "http://localhost:8000/index.php?parca=liste&sayfa=2"
# Normal istek: tam sayfa gelmeli
curl -s "http://localhost:8000/index.php?sayfa=2"

Kontrol listesi:

  1. Aynı adres hem tam sayfa hem parça döndürüyorsa Vary: HX-Request başlığını mutlaka gönderin. Yoksa Nginx, CDN ya da tarayıcı önbelleği, kullanıcıya parçayı tam sayfa diye verebilir.
  2. Adres çubuğunun ve geri tuşunun çalışması için filtre butonlarına hx-push-url="true" ekleyebilirsiniz. Önbellek konusunu yukarıdaki maddeyle birlikte düşünün.
  3. Sayfa numarasını ve yıl gibi parametreleri sunucuda doğrulayın. Bu örnekteki filter_var ve max kullanımı bunun için.
  4. Kullanıcıdan gelen her şeyi e() ile kaçışlayın. Kaçışlanmamış içerikte hx- öznitelikleri de çalışır, yani saldırgan sayfanıza istek atan eleman koyabilir.
  5. POST, PUT ve DELETE isteklerinde CSRF belirtecini unutmayın. Belirteci başlığa eklemek için htmx:configRequest olayını dinleyebilirsiniz.
  6. 4xx ve 5xx cevaplar varsayılan olarak sayfaya basılmaz. Hata durumunda kullanıcıya ne göstereceğinizi ayrıca düşünün.
  7. JavaScript kapalıyken butonlar çalışmaz. Önemli gezinti için butonun yerine gerçek bir bağlantı kullanıp üzerine hx-get eklemek hem JS'siz hem JS'li çalışan bir çözüm verir.
  8. Sonsuz kaydırmada sayfanın altındaki bağlantılara ulaşmak zorlaşır. Alt bilgi önemliyse Daha fazla yükle butonunu tercih edin.
  9. HTMX dosyasını kendi sunucunuzdan, sürümünü sabitleyerek verin ve güncelleme yaparken sürüm notlarına bakın.

Bir sonraki adım olarak yorum formunu hx-post ile gönderip yeni yorumu beforeend ile listenin sonuna eklemeyi deneyin. Aynı mantık, tek fark istek türü. Takıldığınız yer olursa kodunuzu ve aldığınız cevabı konunun altına yazın, birlikte bakalım.

Sıkça Sorulan Sorular (SSS)
Soru 1: Paylaşılan HTML örneği canlı (production) ortamda kullanılabilir mi?
Evet. Konudaki kod bloğu bellek güvenliği, hata yakalama ve performans kriterleri gözetilerek hazırlanmıştır; kendi proje mimarinize uyarlayarak doğrudan kullanabilirsiniz.

Soru 2: Sayfa Yenilemeden Yazı Yüklemek: PHP ve HTMX ile Daha Fazla Yükle Butonu, Sonsuz Kaydırma ve Canlı Yıl Filtresi Yapmak uygulamasında en sık yapılan hata nedir?
Girdi doğrulamasını (validation) ve zaman aşımı/kaynak sınırlarını varsayılan değerlerde bırakmaktır. Rehberdeki yapılandırma adımlarını eksiksiz uygulayarak olası darboğazları önleyebilirsiniz.
 
Community platform by XenForo® © 2010-2026 XenForo Ltd.
© 2020-2026 Metin2Games - Tüm Hakları Saklıdır.
Geri
Üst