Veritabanı sonuçlarını sayfalama
Web uygulamaları geliştirirken, sayfada görüntülenen öğe sayısını sınırlama gereksinimiyle çok sık karşılaşırsınız.
Tüm verileri sayfalama olmadan listelediğimiz durumdan başlayalım. Veritabanından veri seçmek için, yapıcıya ek
olarak, yayınlanan tüm makaleleri yayın tarihine göre azalan sırada döndüren findPublishedArticles
metodunu
içeren bir ArticleRepository sınıfımız var.
namespace App\Model;
use Nette;
class ArticleRepository
{
public function __construct(
private Nette\Database\Connection $database,
) {
}
public function findPublishedArticles(): Nette\Database\ResultSet
{
return $this->database->query('
SELECT * FROM articles
WHERE created_at < ?
ORDER BY created_at DESC',
new \DateTime,
);
}
}
Presenter'da model sınıfını enjekte ederiz ve render metodunda yayınlanan makaleleri talep ederiz, bunları şablona iletiriz:
namespace App\Presentation\Home;
use Nette;
use App\Model\ArticleRepository;
class HomePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ArticleRepository $articleRepository,
) {
}
public function renderDefault(): void
{
$this->template->articles = $this->articleRepository->findPublishedArticles();
}
}
default.latte
şablonunda makalelerin listelenmesini sağlarız:
{block content}
<h1>Makaleler</h1>
<div class="articles">
{foreach $articles as $article}
<h2>{$article->title}</h2>
<p>{$article->content}</p>
{/foreach}
</div>
Bu şekilde tüm makaleleri listeleyebiliriz, ancak makale sayısı arttığında bu sorun yaratmaya başlar. Bu noktada bir sayfalama mekanizması uygulamak faydalı olacaktır.
Bu, tüm makalelerin birkaç sayfaya bölünmesini ve yalnızca geçerli bir sayfanın makalelerini görüntülememizi sağlar. Toplam sayfa sayısı ve makalelerin dağılımı, toplamda kaç makalemiz olduğuna ve sayfa başına kaç makale görüntülemek istediğimize bağlı olarak utils:Paginator tarafından hesaplanır.
İlk adımda, depodaki makaleleri almak için metodu, yalnızca bir sayfa için makaleleri döndürebilecek şekilde değiştiririz. Ayrıca, Paginator'u ayarlamak için ihtiyaç duyacağımız veritabanındaki toplam makale sayısını bulmak için bir metot ekleriz:
namespace App\Model;
use Nette;
class ArticleRepository
{
public function __construct(
private Nette\Database\Connection $database,
) {
}
public function findPublishedArticles(int $limit, int $offset): Nette\Database\ResultSet
{
return $this->database->query('
SELECT * FROM articles
WHERE created_at < ?
ORDER BY created_at DESC
LIMIT ?
OFFSET ?',
new \DateTime, $limit, $offset,
);
}
/**
* Yayınlanan toplam makale sayısını döndürür
*/
public function getPublishedArticlesCount(): int
{
return $this->database->fetchField('SELECT COUNT(*) FROM articles WHERE created_at < ?', new \DateTime);
}
}
Ardından, presenter'ı düzenlemeye başlarız. Render metoduna, görüntülenen geçerli sayfanın numarasını ileteceğiz. Bu numaranın URL'nin bir parçası olmadığı durumlar için, ilk sayfanın varsayılan değerini ayarlarız.
Ayrıca, render metodunu Paginator örneğini almak, ayarlamak ve şablonda görüntülenecek doğru makaleleri seçmek için genişletiriz. HomePresenter, düzenlemelerden sonra şöyle görünecektir:
namespace App\Presentation\Home;
use Nette;
use App\Model\ArticleRepository;
class HomePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ArticleRepository $articleRepository,
) {
}
public function renderDefault(int $page = 1): void
{
// Yayınlanan toplam makale sayısını bulalım
$articlesCount = $this->articleRepository->getPublishedArticlesCount();
// Paginator örneğini oluşturalım ve ayarlayalım
$paginator = new Nette\Utils\Paginator;
$paginator->setItemCount($articlesCount); // toplam makale sayısı
$paginator->setItemsPerPage(10); // sayfa başına öğe sayısı
$paginator->setPage($page); // geçerli sayfa numarası
// Veritabanından Paginator hesaplamasına göre sınırlı bir makale kümesi çekelim
$articles = $this->articleRepository->findPublishedArticles($paginator->getLength(), $paginator->getOffset());
// bunu şablona iletelim
$this->template->articles = $articles;
// ve ayrıca sayfalama seçeneklerini görüntülemek için Paginator'ın kendisini
$this->template->paginator = $paginator;
}
}
Şablonumuz artık yalnızca bir sayfanın makaleleri üzerinde yineleniyor, sadece sayfalama bağlantılarını eklememiz gerekiyor:
{block content}
<h1>Makaleler</h1>
<div class="articles">
{foreach $articles as $article}
<h2>{$article->title}</h2>
<p>{$article->content}</p>
{/foreach}
</div>
<div class="pagination">
{if !$paginator->isFirst()}
<a n:href="default, 1">İlk</a>
|
<a n:href="default, $paginator->page-1">Önceki</a>
|
{/if}
Sayfa {$paginator->getPage()} / {$paginator->getPageCount()}
{if !$paginator->isLast()}
|
<a n:href="default, $paginator->getPage() + 1">Sonraki</a>
|
<a n:href="default, $paginator->getPageCount()">Son</a>
{/if}
</div>
Bu şekilde, Paginator kullanarak sayfaya sayfalama yeteneği ekledik. Veritabanı katmanı olarak Nette Database Core yerine Nette Database Explorer kullanırsak, Paginator kullanmadan da sayfalama
uygulayabiliriz. Nette\Database\Table\Selection
sınıfı, Paginator'dan alınan sayfalama mantığına sahip page metodunu içerir.
Bu uygulama yöntemiyle depo şöyle görünecektir:
namespace App\Model;
use Nette;
class ArticleRepository
{
public function __construct(
private Nette\Database\Explorer $database,
) {
}
public function findPublishedArticles(): Nette\Database\Table\Selection
{
return $this->database->table('articles')
->where('created_at < ', new \DateTime)
->order('created_at DESC');
}
}
Presenter'da Paginator oluşturmamıza gerek yok, bunun yerine deponun döndürdüğü Selection
sınıfının
metodunu kullanırız:
namespace App\Presentation\Home;
use Nette;
use App\Model\ArticleRepository;
class HomePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ArticleRepository $articleRepository,
) {
}
public function renderDefault(int $page = 1): void
{
// Yayınlanan makaleleri çekelim
$articles = $this->articleRepository->findPublishedArticles();
// ve şablona yalnızca page metodunun hesaplamasına göre sınırlanmış bir kısmını gönderelim
$lastPage = 0;
$this->template->articles = $articles->page($page, 10, $lastPage);
// ve ayrıca sayfalama seçeneklerini görüntülemek için gerekli verileri
$this->template->page = $page;
$this->template->lastPage = $lastPage;
}
}
Şimdi şablona Paginator göndermediğimiz için, sayfalama bağlantılarını gösteren kısmı düzenleriz:
{block content}
<h1>Makaleler</h1>
<div class="articles">
{foreach $articles as $article}
<h2>{$article->title}</h2>
<p>{$article->content}</p>
{/foreach}
</div>
<div class="pagination">
{if $page > 1}
<a n:href="default, 1">İlk</a>
|
<a n:href="default, $page - 1">Önceki</a>
|
{/if}
Sayfa {$page} / {$lastPage}
{if $page < $lastPage}
|
<a n:href="default, $page + 1">Sonraki</a>
|
<a n:href="default, $lastPage">Son</a>
{/if}
</div>
Bu şekilde, Paginator kullanmadan sayfalama mekanizmasını uyguladık.