Nette Framework Quick Start VIII.
Při mnoha záznamech v databázi nám současná podoba aplikace vypíše na jednu stránku všechny záznamy z tabulky. To nebývá ani praktické, ani hezké. A když už jsme v těch úpravách, pohrajeme si také trochu s rozdělením kódu do různých presenterů.
Proč? Důvodem je snaha o přehlednost. Můžeme mít veškerý kód ve
výchozím presenteru, ale s rostoucími požadavky na funkčnost aplikace bude
výchozí presenter nabývat na rozsáhlosti a – začne se stávat
nepřehledným. My si do budoucna ulehčíme práci a vytvoříme si dva nové
presentery, BasePresenter.php a
TablePresenter.php.
Presenter BasePresenter.php nám zajistí připojení
k databázi a nastavení základního filtru pro šablony. Jeho kód tedy bude
vypadat takto:
<?php
class BasePresenter extends /*Nette::Application::*/Presenter
{
/** @var DibiConnection */
protected $db;
protected function startup()
{
// připojíme se k souboru s databází
require_once LIBS_DIR . '/dibi/dibi.php';
$this->db = new DibiConnection(array(
'driver' => 'sqlite',
'database' => APP_DIR . '/models/sample.sdb',
));
}
protected function beforeRender()
{
$this->template->registerFilter(/*Nette::Templates::*/'TemplateFilters::curlyBrackets');
}
}
Heuréka. Právě jsme objevili znovupoužitelnost kódu. Každý presenter,
v kterém chceme využívat služeb databáze a filtru
TemplateFilter::curlyBrackets, musí splnit jedinou věc – být
potomkem presenteru BasePresenter.php.
Jako první změníme podobu našeho výchozího presenteru
DefaultPresenter.php. Bude potomkem základního presenteru a bude
mít na starosti jedinou věc – vypsat seznam všech tabulek v databázi.
Výsledný kód:
<?php
require_once dirname(__FILE__) . '/BasePresenter.php';
class DefaultPresenter extends BasePresenter
{
public function renderDefault()
{
$this->template->tables = $this->db->getDatabaseInfo()->getTableNames();
}
}
Nyní zbývá vylepšit a usnadnit práci se zobrazenými záznamy tabulky.
Stránkování a řazení podle sloupců
Všechen kód, mající na starost zobrazení záznamů, umístíme do
presenteru TablePresenter.php. Nahlédnutím do kódu zjistíte,
že přibyla funkce stránkování a řazení podle sloupců.
<?php
require_once dirname(__FILE__) . '/BasePresenter.php';
class TablePresenter extends BasePresenter
{
/** @persistent */
public $page = 0;
/** @persistent */
public $order = '';
public function renderDefault($table, $orderBy)
{
// sorting
parse_str($this->order, $list);
if ($orderBy) {
if (!isset($list[$orderBy])) {
$list[$orderBy] = 'a';
} elseif ($list[$orderBy] === 'd') {
unset($list[$orderBy]);
} else {
$list[$orderBy] = 'd';
}
}
$this->order = http_build_query($list, '', '&');
// paging
$rowsPerPage = 15;
$numOfRows = $this->db->select('count(*)')->from($table)->fetchSingle();
$numOfPages = (int) ceil($numOfRows / $rowsPerPage);
$this->page = max(0, min($this->page, $numOfPages - 1));
// paginator
if ($numOfPages > 1) {
$steps = range(max(0, $this->page - 3), min($numOfPages - 1, $this->page + 3));
$steps = array_merge($steps, range(0, $numOfPages - 1, (int) ceil($numOfPages / 5)));
$steps[] = $numOfPages - 1;
sort($steps);
$steps = array_unique($steps);
} else {
$steps = array();
}
// retrieving data
$query = $this->db->select('*')
->from($table)
->offset($this->page * $rowsPerPage)
->limit($rowsPerPage);
$i = 1;
foreach ($list as $field => $dir) {
$query->orderBy($field, $dir === 'a' ? 'ASC' : 'DESC');
$list[$field] = array($dir, $i++);
}
$rowset = $query->execute();
$this->template->table = $table;
$this->template->order = $list;
$this->template->currentOrder = $this->order;
$this->template->currentPage = $this->page;
$this->template->numOfPages = $numOfPages;
$this->template->steps = $steps;
$this->template->rows = $rowset->fetchAll();
$this->template->columns = $rowset->getColumnNames();
}
}
Úprava šablon
Nyní je na čase vytvořit šablonu, která se nám postará o zobrazení
záznamů z tabulky. Názvy sloupců jsou aktivní a zpřístupňují řazení.
Obsah šablony Table.default.phtml je následující:
<h2>Table „{$table}“</h2>
<p><a href="{plink Default:}">Return to home</a></p>
{if $numOfPages > 1}
<div class="paginator">
{if $currentPage > 0}
<a href="{link this, $table, 'page' => $currentPage - 1}">« Previous</a>
{else}
<span class="button">« Previous</span>
{/if}
{foreach $steps as $num}
{if isset($lastNum) && $num - $lastNum > 1}<span>…</span>{/if}
{if $num == $currentPage}
<span class="current">{$num + 1}</span>
{else}
<a href="{link this, $table, 'page' => $num}">{$num + 1}</a>
{/if}
{? $lastNum = $num }
{/foreach}
{if $currentPage < $numOfPages - 1}
<a href="{link this, $table, 'page' => $currentPage + 1}">Next »</a>
{else}
<span class="button">Next »</span>
{/if}
</div>
{/if}
<table class="grid" border="1">
<tr>
{foreach $columns as $column}
<th><a href="{link this, $table, $column}"
{if isset($order[$column])} class="{$order[$column][0] === 'a' ? 'asc' : 'desc'}"{/if}
>{$column}{if count($order) > 1 && isset($order[$column])} <span>{$order[$column][1]}</span>{/if}</a></th>
{/foreach}
</tr>
{foreach $rows as $num => $row}
<tr{!$num % 2 ? ' class="alt"' : ''}>
{foreach $row as $value}
<td>{=substr($value, 0, 100)}</td>
{/foreach}
</tr>
{/foreach}
</table>
Původní šablonu Default.table.phtml nyní můžeme vymazat.
Nesmíme zapomenout na jednu důležitou věc – v seznamu tabulek
z databáze se odkazujeme na neexistující pohled Table. Musíme tento odkaz
upravit na nově vytvořený presenter Table.
<h2>Tables:</h2>
<ul>
{foreach $tables as $table}
<li><a href="{plink Table: $table}">{$table}</a></li>
{/foreach}
<ul>
Rozdíl nemusí být na první pohled patrný. Odpovědí je dvojtečka!
Nyní máme vytvořenu základní kostru aplikace, včetně inteligentního zobrazní dat. Co nyní? Rozvineme trochu znovupoužitelnost kódu a ukážeme si, jakým způsobem vytvořit komponentu.
Aplikaci si můžete opět stáhnout.
