/
pagination.texy
274 lines (213 loc) · 7.68 KB
/
pagination.texy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
Paginarea rezultatelor bazei de date
************************************
.[perex]
Atunci când dezvoltați aplicații web, vă confruntați adesea cu cerința de a imprima un număr restrâns de înregistrări pe o pagină.
Ieșim din starea în care ne aflăm atunci când enumerăm toate datele fără paginare. Pentru a selecta datele din baza de date, avem clasa ArticleRepository, care conține constructorul și metoda `findPublishedArticles`, care returnează toate articolele publicate, sortate în ordinea descrescătoare a datei de publicare.
```php
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,
);
}
}
```
În Presenter vom injecta apoi clasa model, iar în metoda render vom cere articolele publicate pe care le vom trece în șablon:
```php
namespace App\UI\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();
}
}
```
Șablonul `default.latte` se va ocupa apoi de listarea articolelor:
```latte
{block content}
<h1>Articles</h1>
<div class="articles">
{foreach $articles as $article}
<h2>{$article->title}</h2>
<p>{$article->content}</p>
{/foreach}
</div>
```
În acest fel, putem scrie toate articolele, dar acest lucru va cauza probleme atunci când numărul de articole crește. În acel moment, va fi util să implementăm mecanismul de paginare.
Acest lucru va asigura că toate articolele sunt împărțite în mai multe pagini și vom afișa doar articolele de pe o singură pagină curentă. Numărul total de pagini și distribuția articolelor este calculat chiar de [utils:Paginator], în funcție de câte articole avem în total și câte articole dorim să afișăm pe pagină.
În primul pas, vom modifica metoda de obținere a articolelor din clasa repository pentru a returna numai articole de o singură pagină. De asemenea, vom adăuga o nouă metodă pentru a obține numărul total de articole din baza de date, de care vom avea nevoie pentru a seta un Paginator:
```php
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,
);
}
/**
* Returns the total number of published articles
*/
public function getPublishedArticlesCount(): int
{
return $this->database->fetchField('SELECT COUNT(*) FROM articles WHERE created_at < ?', new \DateTime);
}
}
```
Următorul pas este să modificăm prezentatorul. Vom transmite numărul paginii afișate în prezent către metoda de randare. În cazul în care acest număr nu face parte din URL, trebuie să stabilim valoarea implicită la prima pagină.
De asemenea, extindem metoda de randare pentru a obține instanța Paginator, configurând-o și selectând articolele corecte pentru a fi afișate în șablon. HomePresenter va arăta astfel:
```php
namespace App\UI\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
{
// Vom afla numărul total de articole publicate
$articlesCount = $this->articleRepository->getPublishedArticlesCount();
// Vom crea instanța Paginator și o vom configura
$paginator = new Nette\Utils\Paginator;
$paginator->setItemCount($articlesCount); // numărul total de articole
$paginator->setItemsPerPage(10); // articole pe pagină
$paginator->setPage($page); // numărul actual de pagini
// Vom găsi un set limitat de articole din baza de date pe baza calculelor efectuate de Paginator
$articles = $this->articleRepository->findPublishedArticles($paginator->getLength(), $paginator->getOffset());
// pe care le vom transmite șablonului
$this->template->articles = $articles;
// și, de asemenea, Paginator însuși pentru a afișa opțiunile de paginare
$this->template->paginator = $paginator;
}
}
```
Șablonul itera deja peste articole într-o singură pagină, trebuie doar să adăugăm linkuri de paginare:
```latte
{block content}
<h1>Articles</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">First</a>
|
<a n:href="default, $paginator->page-1">Previous</a>
|
{/if}
Page {$paginator->getPage()} of {$paginator->getPageCount()}
{if !$paginator->isLast()}
|
<a n:href="default, $paginator->getPage() + 1">Next</a>
|
<a n:href="default, $paginator->getPageCount()">Last</a>
{/if}
</div>
```
Acesta este modul în care am adăugat paginarea folosind Paginator. Dacă [Nette |database:core] [Database Explorer |database:explorer] este utilizat în locul [Nette Database Core |database:core] ca strat de bază de date, putem implementa paginarea chiar și fără Paginator. Clasa `Nette\Database\Table\Selection` conține metoda [page |api:Nette\Database\Table\Selection::_ page] cu logica de paginare preluată din Paginator.
Depozitul va arăta astfel:
```php
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');
}
}
```
Nu trebuie să creăm Paginator în Presenter, în schimb vom folosi metoda obiectului `Selection` returnat de depozit:
```php
namespace App\UI\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
{
// Vom găsi articole publicate
$articles = $this->articleRepository->findPublishedArticles();
// și partea lor limitată de metoda de calcul a paginii pe care o vom trece la șablonul
$lastPage = 0;
$this->template->articles = $articles->page($page, 10, $lastPage);
// și datele necesare pentru a afișa și opțiunile de paginare
$this->template->page = $page;
$this->template->lastPage = $lastPage;
}
}
```
Deoarece nu folosim Paginator, trebuie să modificăm secțiunea care arată legăturile de paginare:
```latte
{block content}
<h1>Articles</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">First</a>
|
<a n:href="default, $page - 1">Previous</a>
|
{/if}
Page {$page} of {$lastPage}
{if $page < $lastPage}
|
<a n:href="default, $page + 1">Next</a>
|
<a n:href="default, $lastPage">Last</a>
{/if}
</div>
```
În acest fel, am implementat un mecanism de paginare fără a utiliza un Paginator.
{{priority: -1}}
{{sitename: Best Practices}}