Hello World
Každá aplikace v PClib začíná dvěma řádky: připojením knihovny a vytvořením objektu aplikace pclib\App, který obsahuje služby a údaje sdílené různými komponentami aplikace nebo se vztahující k aplikaci jako celku. To je vše, ihned poté můžete začít tvořit váš projekt!
<?php
include 'vendor/autoload.php';
$app = new pclib\App('nazev_aplikace');
// Zde nasleduje vas kod...
Například formulář můžeme vypsat takto:
$form = new pclib\Form('tpl/sablona_formulare.tpl');
print $form;
Pro výpis údajů z databáze pomocí datagridu se musíme nejprve připojit k databázi:
$app->db = new pclib\Db('pdo_mysql://user:password@localhost/testovaci_databaze');
A pak vytvořit objekt grid a vypsat data z databázové tabulky:
$grid = new pclib\Grid('tpl/sablona_gridu.tpl');
$grid->setQuery('select * from tabulka');
print $grid;
Struktura aplikace
Ačkoliv lze psát kód přímo pod řádek s objektem aplikace, v reálných projektech se kód člení do jednotlivých podadresářů a souborů. Doporučenou strukturu pclib aplikace můžete najít v šabloně https://github.com/lenochware/pclib-app. Podle architektury MVC (Model / View / Controller) se pak akce volané uživatelem definují v příslušném controlleru.
Například k zobrazení formuláře bychom vytvořili akci show:
<?php
...
public function showAction()
{
$form = new pclib\Form('tpl/home/sablona_formulare.tpl');
return $form;
}
...
Po zadání adresy http://localhost/moje-aplikace/home/show se v prohlížeči vypíše příslušný formulář.
Při práci s MVC bude v souboru index.php pouze konfigurace aplikace a volání aplikační metody run() a out(), která vykoná příslušný kód a vypíše výsledek do prohlížeče. Níže je obvyklá podoba index.php, která by měla vyhovovat většině aplikací a kterou budeme předpokládat v následujících příkladech.
<?php
include 'vendor/autoload.php';
$app = new pclib\App('test_app');
$app->addConfig('./config.php');
$app->db = new pclib\Db('pdo_mysql://user:password@localhost/test_app');
$app->setLayout('tpl/layout.tpl');
$app->run();
$app->out();
Vytvoříme objekt $app, načteme konfiguraci ze souboru config.php, připojíme se k databázi test_app a nastavíme šablonu layoutu do které se budou vypisovat všechny výstupy. Nakonec se podle zavolané url vykoná příslušná akce (metoda controlleru) a vypíše se výsledek.
test-app/ ├── controllers/ ├── models/ ├── tpl/ ├── css/ ├── js/ ├── vendor/ ├── index.php └── config.php
V adresáři controllers/ jsou jednotlivé controllery (potomci třídy pclib\Controller) pro každý modul aplikace. Jestliže používáte pclib ORM, mohou být v models/ databázové modely (potomci třídy pclib\Model). Adresář tpl/ obsahuje soubory šablon obvykle rozdělené do podadresářů pro každý modul. vendor/ je adresář s nainstalovanými knihovnami, spravovaný composerem a css a js obsahuje soubory stylů a javascriptu.
Adresářová struktura není závazná, místa, kde pclib hledají příslušné soubory, lze změnit v konfiguraci.
Konfigurace
📖 Seznam konfiguračních parametrů
Konfigurační soubor načteme při inicializaci aplikace v index.php příkazem $app->addConfig('./config.php'). Konfigurační parametry jsou pak uložené v poli $app->config. Samotný konfigurační soubor tvoří obyčejné php pole $config obsahující konfigurační parametry. Může obsahovat i další obdobná pole $develop a $production.
$config = [
'konfiguracni-klic-1' => 'hodnota',
'konfiguracni-klic-2' => 'hodnota',
];
$develop = [
'konfiguracni-klic-2' => 'hodnota pro develop',
];
$production = [
'konfiguracni-klic-2' => 'hodnota pro produkcni server',
];
Hodnoty v poli $develop se uplatní na vývojovém serveru, $production slouží pro ostrý provoz. Implicitně se $develop načte při spuštění z localhostu a $production všude jinde. Pole $config se načítá vždy.
O jaké prostředí jde, udává hodnota $app->environment. $app->environment = 'production' nastaví produkční prostředí. K nastavení podle IP lze použít funkci $app->environmentIp(['ip-adresa' => 'develop', 'ip-adresa' => 'production', ...])
$develop = [ 'pclib.errors' => ['display' => true, 'develop' => true], ]; $production = [ 'pclib.errors' => ['display' => true, 'develop' => false, 'log' => true, /*'template' => 'tpl/error.tpl' */], ];
Pomocí konfiguračního klíče pclib.app můžete nastavit základní konfiguraci objektu aplikace, jako je šablona layoutu, implicitní routa a automaticky spouštěné služby (další možnost je nastavení pomocí php v souboru index.php).
$config = [
'pclib.app' => [
'language' => 'cs',
'default-route' => 'products',
'layout' => 'tpl/layout.tpl',
'autostart' => ['db', 'auth', 'fileStorage'],
],
'service.db' => ['dsn' => 'pdo_mysql://user:password@localhost/test-app'],
'service.fileStorage' => ['rootDir' => './uploaded'],
];
Po otevření aplikace se zobrazí stránka products/index, jazyk je nastavený na češtinu a layout se načte z příslušného souboru. Automaticky se spustí služba databáze, autorizační systém a služba pro ukládání souborů.
Ke každé službě můžete přistupovat pomocí $app->jmeno_sluzby např. $app->db. Služby lze konfigurovat klíčem 'service.jmeno_sluzby'.
Layout
Layout aplikace je základní šablona aplikace, do které se vkládá veškerý obsah. Nastavíme ji v konfiguraci nebo příkazem $app->setLayout('tpl/layout.tpl'); Layout může vypadat například takto:
<?elements
head HEAD scripts "css/website.css,js/global.js,{pclib}/www/pclib.js"
messages PRECONTENT noescape
string CONTENT noescape
?>
<!doctype html>
<html lang="cs">
<head>
<meta charset="utf-8">
<title>{TITLE}</title>
{HEAD}
</head>
<body>
<main>
{PRECONTENT}{CONTENT}
</main>
</body>
</html>
Výrazy ve složených závorkách jako{CONTENT} jsou elementy šablony, které jsou nahrazeny vloženým obsahem. V sekci <?elements ?> lze doplnit typ a parametry vypisovaného elementu. Viz šablonovací systém. Layout obsahuje tři hlavní elementy: HEAD pro vkládání stylů a skriptů, PRECONTENT slouží k výpisu notifikačních a varovných hlášení aplikace a CONTENT je vlastní obsah. Hodnota vrácená controllerem bude před vypsáním vložená do layoutu jako CONTENT:
public function helloAction()
{
return "Hello world!";
}
K layoutu aplikace lze přistupovat pomocí $app->layout a pracovat s ním jako s každou šablonou.
$app->layout->values['TITLE'] = 'Titulek aplikace';
Pokud potřebujeme podmíněně přidat styly a skripty jen pro některou stránku, můžeme to provést pomocí metody $app->layout->addScripts():
$app->layout->addScripts('js/editor.js', 'css/editor.css');
Pokud nějaká část webu používá odlišný layout než zbytek, lze ho snadno změnit:
public function helloAction()
{
$this->app->setLayout('tpl/hello-layout.tpl');
return "Hello world!";
}
Controllers
Kontrolery obsahují veškeré akce, které aplikace vykonává. Po stisknutí tlačítka nebo zadání webové adresy router rozhodne, která metoda kontroleru se zavolá a předá jí případné parametry. Kontroler je třída odvozená od pclib\Controller, její název končí na Controller a je uložená ve stejnojmeném souboru v adresáři controllers/. Obvykle každému modulu aplikace odpovídá příslušný controller.
Například eshop by mohl obsahovat HomeController, ProductsController a OrdersController pro úvodní obrazovku, přehled a detail produktů a zpracování objednávek - v odpovídajících souborech HomeController.php, ProductsController.php a OrdersController.php
class ProductsController extends pclib\Controller {
public function indexAction()
{
$grid = new pclib\Grid('tpl/products/grid.tpl');
$grid->setQuery("select * from products");
return $grid;
}
public function showAction($id)
{
$product = $this->app->db->select('products', ['id' => $id]);
if (!$product) $this->app->error("Produkt nenalezen!", "alert alert-danger");
return $this->template('tpl/products/detail.tpl', $product);
}
}
Metodě kontroleru končící na Action odpovídá url při jehož zadání se zavolá. Například showAction() odpovídá url test-app/index.php?r=products/show nebo při použití friendly url: test-app/products/show. indexAction je speciální a volá se implicitně. Akce mohou mít parametry - například akce show, která zobrazí detail produktu, vyžaduje jako parametr id produktu.
| Route | Url | Volaná akce |
|---|---|---|
| products | /products | ProductsController->indexAction() |
| products/show/id:1 | /products/show?id=1 | ProductsController->showAction(1) |
| products/moje-oblibene | /products/moje-oblibene | ProductsController->mojeOblibeneAction() |
| products/list/sleva:1/kategorie:pc | /products/list?sleva=1&kategorie=pc | ProductsController->listAction(1, 'pc') |
Uvnitř kontroleru můžete přistupovat k objektu aplikace pomocí $this->app. Například ke službě databáze lze přistupovat pomocí $this->app->db. Aplikační funkce $app->error() vypíše chybové hlášení a ukončí aplikaci /druhý parametr jsou css třídy k nastylování messageboxu/
Kontroler obsahuje několik pomocných metod. Funkce $this->template('tpl/products/detail.tpl', $product); vytvoří šablonu produktu a naplní ji hodnotami z pole $product.
Často používaná je funkce redirect(), která přesměruje na příslušnou routu. Takto po aktualizaci produktu přesměrujeme na stránku detailu produktu:
...
public function updateAction($id)
{
$form = new pclib\Form('tpl/products/form.tpl');
if (!$form->validate()) {
$this->app->error("Chybně zadané parametry.", "alert alert-danger");
};
$form->update('products', ['id' => $id]);
$this->app->message('Položka byla uložena.', "alert alert-success");
$this->redirect('products/show/id:' . $id);
}
Načte se formulář, zkontroluje se, jestli je správně vyplněný (podle validačních pravidel v šabloně), uloží se do databáze, nastaví se informační hlášení a nakonec se provede přesměrování.
Pokud uživatel zadá neexistující routu (např. products/xyz) , můžete ji zpracovat, pokud nadefinujete funkci defaultAction():
public function defaultAction($action)
{
$this->app->error("Stránka neexistuje: " . $action->path, "alert alert-danger");
}
public function deleteAction($id)
{
$this->authorize('products/delete');
$this->app->db->delete('products', ['id' => $id]);
$this->app->message('Položka byla smazána.', "alert alert-success");
$this->redirect('products');
}
Pokud používáte ORM (databázové modely) lze využít pomocné funkce model() a selection() k jejich získání a dotazování.
Získame ORM model produktu s id=1:
$product = $this->model('products', 1);
print $product->title;
Procházíme všechny produkty s cenou vyšší než 1000:
$sel = $this->selection('products')->where('price>1000');
foreach($sel as $product) {
print $product->title;
}
Objekt Aplikace
Objekt aplikace ($app) třídy pclib\App je obvykle první objekt, který v pclib vytváříme. Slouží jako fasáda poskytující přístup k různým službám a funkcím pclib. Z metody controlleru se na ní můžeme odkazovat pomocí atributu $this->app. Aplikace obsahuje tyto veřejné atributy:
| $app->name | string | Název aplikace |
| $app->config | array | Pole konfiguračních parametrů |
| $app->language | string (např . 'en', 'cs') | Jazyk aplikace |
| $app->environment | 'develop' | 'production' | Vývojové nebo produkční prostředí |
| $app->debugMode | true | false | Debugovací režim se zapnutým debugbarem |
| $app->layout | pclib\Layout | Šablona layoutu |
| $app->request | pclib\Request | Hodnoty požadavku, jako url, ip-adresa nebo hlavičky |
| $app->router->action | pclib\Action | Aktuální volaná akce controlleru |
Aplikace obsahuje metody k spuštění a vykonání požadavku a zobrazení výsledku a další pomocné metody, díky kterým nemusíte přistupovat k příslušným komponentám frameworku napřímo.
| $app->addConfig($path) | Načte konfigurační soubor |
| $app->setLayout($path) | Načte šablonu layoutu |
| $app->run($routeStr = null) | Spustí akci controlleru |
| $app->out() | Zobrazí výslednou stránku |
| $app->message($message, $cssClass = null) | Vypíše informační hlášení (flash-message) |
| $app->error($message, $cssClass = null) | Vypíše chybové hlášení a ukončí aplikaci |
| $app->log($category, $message_id, $message = null, $item_id = null) | Zapíše zprávu do logu |
| $app->redirect($route, $code = null) | Přesměruje aplikaci na zadanou routu |
Další atributy a metody třídy App
Objekt aplikace slouží také jako kontejner pro různé služby, jako je práce s databází, autentizace, práce se soubory, logování, překlad cizojazyčných textů a podobně. Každá služba je objekt, který je interně uložený v poli $app->services. Pro práci s ním lze použít metody getService(), setService(), ale obvykle s ním pracujeme pomocí $app->jmeno_sluzby (aplikace vytváří property, která interně volá tyto dvě metody)
//Vytvoření služby 'db' a práce s ní
$app->db = new pclib\Db('pdo_mysql://jmeno:heslo@host/jmeno_databaze');
$result = $app->db->select('tabulka', ['id' => 1]);
Služby můžeme inicializovat také pomocí konfiguračního klíče aplikace 'pclib.app':
$config = [
'pclib-app' => [
'autostart' => ['db']
],
'service.db' => ['dsn' => 'pdo_mysql://jmeno:heslo@host/jmeno_databaze'],
];
V poli 'autostart' jsou uvedené služby, které se mají automaticky spustit a 'service.jmeno_sluzby' obsahuje konfiguraci služby.
Pomocí parametru $app->request můžeme získat informace o aktuálním requestu, jako například url, clientIP, headers a další - např. $aktualniUrl = $app->request->url. Viz třída pclib\Request v referenčním manuálu.
Někdy potřebujeme aby aplikace měla konfigurační parametry, které mohou editovat správci v administračním rozhraní (v eshopu by to mohly být například notifikační emailová adresa, cena poštovného a podobně). K tomu slouží databázová tabulka APP_PARAMS. Příslušný parametr tam s popisem přidáme a správce pak může jeho hodnotu měnit v aplikaci padmin. V kódu k parametrům přistupujeme pomocí služby $app->params. Službu nejprve inicializujeme (např pomocí 'autostart' => ['params'] v konfiguraci) a pak můžeme číst parametry: $app->params->get('CENA_POSTOVNE') nebo rovnou $app->params->CENA_POSTOVNE.
Šablony
Šablony jsou HTML soubory obsahující proměnné šablon {POLE}, jejichž hodnoty se nastavují z kódu. Se šablonami pracuje třída pclib\Tpl a také třídy pclib\Grid a pclib\Form, které přidávají elementy datagridu (stránkování a řazení) resp. elementy formuláře, jako je input, select, textarea a podobně.
Jestliže šablona obsahuje například proměnnou {title}, nastavíme její hodnotu pomocí atributu values: $tpl->values['title'] = 'Titulek šablony'; Výsledné html získáme pomocí funkce html(): print $tpl->html();
Šablona nemusí, ale může, obsahovat sekci <?elements ... ?> kde lze definovat typ, způsob zobrazení, formátování a jiné parametry pro každou proměnnou šablony.
<?elements
string description format "n"
bind category lookup "categories"
string published_at date "d.m.Y"
number price format "2,."
block sleva noprint
?>
<h1>{title}</h1>
<p>{description}</p>
<p>Kategorie: {category}</p>
<p><img src="{@baseurl}{image_path}" class="product-image"></p>
<p>Datum vydání: {published_at}</p>
{if price}<p>Cena: {price} Kč</p>{/if}
{if not price}<p>Cena dohodou.</p>{/if}
{block sleva}
<div class="alert alert-info">Produkt je ve slevě.</div>
{/block}
V horní části je sekce elements, definující formátování některých polí, pod ní je html kód s proměnnými šablony, který se vypíše.
Formátování: V případě pole description se odřádkování zkonvertují na značku <br>, published_at se zformátuje jako datum a cena (price) bude zaokrouhlená na dvě místa s uvedenými oddělovači tisíců a desetinnou tečkou, např. 1,100.50 Kč.
Parametr {@baseurl} je proměnná dostupná v kterékoliv šabloně, obsahující hlavní (nejvyšší) url aplikace.
Jestliže máme databázovou tabulku products, která obsahuje sloupce id, title, description, price a published_at, můžeme výše uvedenou stránku detailu produktu vypsat takto:
$tpl = new pclib\Tpl("tpl/products/detail.tpl");
$product = $this->db->select('products', ['id' => $id]);
$tpl->values = $product;
return $tpl->html();
Nebo pomocí funkce controlleru template():
$product = $this->db->select('products', ['id' => $id]);
return $this->template("tpl/products/detail.tpl", $product);
Bloky
Někdy je potřeba vypsat části šablony podmíněně. K tomu slouží v šabloně bloky a "ify".
Blok {block name}...{/block} ohraničuje část šablony, je možné ho skrýt nebo hromadně nastavit atributy a hodnoty polí v něm. Blok sleva má nastavený atribut noprint, takže je skrytý. Aktivujeme ho v kódu pomocí $tpl->enable('sleva');
Je možné i nastavit proměnné viditelné pouze v bloku:
order.php:
$tpl->values['address'] = ['street' => 'Vodičkova', 'city' => 'Praha', ...];
order.tpl:
{block address}
<p>{street}</p>
<p>{city}</p>
{/block}
Sekce {if price}...{/if} se vypíše pouze pokud je nastavené pole "price". Jinak se vypíše "Cena dohodou."
Podšablony (include)
Šablonu je možné rozdělit na více částí a vkládat podšablony pomocí include. Například jestliže máme ve více formulářích checkboxy souhlas s obchodními podmínkami a zpracování osobních údajů, můžeme vytvořit podšablonu agreement a pak ji použít ve všech formulářích: <?elements
class form name "order-form"
include agreement file "tpl/partial/agreement.tpl"
...
?>
...definice objednávkového formuláře...
{agreement}
Pclib před vykreslením šablony vloží šablonu agreement a sloučí i definici elementů obou šablon.
Občas se může hodit vložit do šablony přímo i výsledek volání nějaké routy. To zajistí element action.
<?elements
action title_image route "catalog/image/id:{id}"
...
?>
{title_image}
Tento kód vloží do šablony obsah cesty catalog/image, tedy výsledek volání CatalogController->imageAction($id). Routa může obsahovat i parametry.
Vlastní výpis pole
Nastavením atributu onprint pro libovolné pole ho můžeme vypsat pomocí vlastní funkce.
string price onprint "printPrice"
function printPrice($tpl, $id, $sub, $value) {
if ($value) print "$value Kč";
else print "Cena dohodou";
}
Funkce printPrice() přebírá jako parametry odkaz na objekt šablony, id pole (zde "price"), id modifikátoru (např. 'lb') a hodnotu pole.
Do onprint lze přiřadit libovolný php callable, takže například použít funkci controlleru můžeme takhle:
class ProductsController extends pclib\Controller {
public function showAction($id)
{
$product = $this->app->db->select('products', ['id' => $id]);
if (!$product) $this->app->error("Produkt nenalezen!", "alert alert-danger");
$tpl = new pclib\Tpl('tpl/products/detail.tpl');
$tpl->values = $product;
//nastavime atribut 'onprint' pro pole 'price'
$tpl->elements['price']['onprint'] = [$this, 'printPrice'];
return $tpl;
}
public function printPrice($tpl, $id, $sub, $value)
{
if ($value) print "$value Kč";
else print "Cena dohodou";
}
}
Grid
Třída Grid (potomek Tpl) slouží k výpisu záznamů, jako je seznam produktů, ve stránkovatelném seznamu. Podle jednotlivých sloupců lze řadit a seznam lze filtrovat.
public function indexAction()
{
$grid = new pclib\Grid('tpl/products/grid.tpl', 'products');
$grid->setQuery("select * from products");
return $grid;
}
Vytvoříme šablonu (druhý parametr konstruktoru říká, že řazení, stránkování a filtr se mají pamatovat v session proměnné 'products'. To zajistí, že se nastavení bude pamatovat i když přejdete na jinou stránku. Je nutné v index.php inicializovat sessions pomocí session_start() ). Následuje dotaz, který je zdrojem záznamů. Za předpokladu, že v tabulce products jsou sloupce id, title, price atd. můžeme je použít v šabloně gridu.
<?elements
class grid name "products"
string title lb "Název produktu" sort
string description format "n" skip
bind category lookup "categories" lb "Kategorie" sort
string published_at date "d.m.Y" lb "Publikováno" sort
number price format "2,." lb "Cena" sort
link lndetail route "products/show/id:{id}" lb "Detail"
pager pager pglen "20"
?>
<table>
<tr>{grid.labels}</tr>
{block items}
<tr>{grid.fields}</tr>
{block else}
<tr><td colspan="10" align="center">Nenalezeny žádné výsledky.</td></tr>
{/block}
</table>
<div class="pager">{pager}</div>
Šablona gridu obsahuje speciální blok items, který se vypíše pro každý řádek přehledu. Pokud nebudou nalezeny žádné položky, vypíše se sekce {block else}.
Každá položka obsahuje atribut lb, který se použije jako popisek sloupce a přidáním atributu sort zajistíme, že po kliknutí se grid podle sloupce seřadí. Specifickým elementem gridu je {pager}, který zobrazí komponentu navigace mezi stránkami. Délka stránky je nastavená na 20 řádků. Element link vytvoří odkaz na detail produktu (link lze použít i v obyčejných šablonách).
V této šabloně používáme zkrácený zápis {grid.labels} a {grid.fields}, který vypíše všechny sloupce uvedené v sekci elements (sloupec lze vynechat přidáním atributu skip). Pokud potřebujete složitější layout, můžete pole v šabloně rozepsat i jednotlivě:
<table>
<tr>
<th>{title.lb}</th>
<th>{category.lb}</th>
<th>{price.lb}</th>
</tr>
{block items}
<tr class="link" onclick="{lndetail.js}">
<td>{title}</td>
<td>{category}</td>
<td>{price} Kč</td>
</tr>
{/block}
</table>
Pokud chceme datagrid filtrovat, použijeme k tomu atribut $grid->filter. Například $grid->filter['category'] = 1; zapne filtr podle kategorie. Dotaz gridu pak musíme upravit takto:
$grid->setQuery(
"select * from products
where 1
~ and category='{category}'"
);
Parametr {category} se nahradí hodnotou z $grid->filter. Vlnovka (~) na začátku znamená podmíněné použití. Jestliže není proměnná category nastavená, filtr na kategorii se nevykoná (řádek s vlnovkou bude vynechán).
Grid je možné mimo jiné exportovat do formátu csv, nebo do formátu vhodného k otevření v excelu: $grid->exportCsv('soubor.csv') nebo $grid->exportExcel('soubor.csv')
Formuláře
Třída Form (potomek Tpl) slouží k zobrazení, uložení a validaci formulářů. Šablony formulářů umí vytvářet všechny běžné formulářové komponenty včetně nastavení validačních pravidel, ošetřují vstupy, umožňují flexibilitu pokud jde o podobu formuláře a zároveň zachovávají práci s formuláři v kódu jednoduchou a přehlednou.
public function editAction($id)
{
$product = $this->app->db->select('products', ['id' => $id]);
if (!$product) $this->app->error("Produkt nenalezen!", "alert alert-danger");
$form = new pclib\Form('tpl/products/form.tpl');
$form->values = $product;
$form->enable('update');
return $form;
}
Tento kód vytvoří a zobrazí editační formulář produktu. Nejdřív načteme data produktu z databáze do proměnné $product, pak jimi naplníme formulář (nastavením $form->values) a zobrazíme. Stránka se zobrazí po zadání routy obsahující id produktu - například products/edit?id=1
Šablona formuláře vypadá takto:
<?elements
class form route "products/id:{GET.id}" html5
input title lb "Název produktu:" required
text description lb "Popis:" size "50x4" required
select category lb "Kategorie:" lookup "categories" required
input price lb "Cena:" size "10"
input image_path file accept "image/*" size_mb "2" lb "Obrázek"
check published lb "Publikováno" default "1"
input published_at lb "Datum vydání:" date
button insert lb "Přidat" noprint
button update lb "Uložit" noprint
button delete lb "Smazat" confirm "Opravdu smazat?" noprint
?>
<table>
{form.fields}
</table>
První řádek určuje že se formulář odesílá do ProductsControlleru, konkrétní akci udává název tlačítka. Při aktualizaci je zapnuté pouze tlačítko update, routa tedy bude products/update. Id produktu převezmeme z url pomocí parametru id:{GET.id} Atribut html5 zapíná html5 klientskou validaci formuláře.
Následující položky vytvoří pole formuláře INPUT, TEXTAREA, SELECT a CHECKBOX. Atribut required znamená povinné pole, lb jeho popisek, size uvádí velikost pole, date provádí validaci datumu a default je implicitní předvyplněná hodnota pole. Tyto atributy lze použít u kteréhokoliv pole formuláře.
Pokud chcete mít ve formuláři pole, které se nebude ukládat, zajistí to parametr nosave, needitovatelné pole (disabled) vytvoříme atributem noedit. Například input order_no lb "Číslo zakázky" noedit nosave.
Select musí obsahovat zdroj dat, odkud se načtou položky výběrového pole - zde se používá číselník categories. Položky lze načíst i pomocí dotazu (query), textového seznamu (list) nebo funkcí php (datasource). Stejným způsobem lze vytvořit i pole checkboxů nebo radiobuttonů.
Pole input file slouží k uploadování souborů. Zde je nastavená validace pouze na obrázkové soubory s maximální velikostí souboru 2MB.
Tlačítka jsou implicitně vypnutá (noprint) a zapíná se pouze tlačítko update, které odešle formulář do akce ProductsController->updateAction($id).
V této šabloně používáme zkrácený zápis {form.fields}, který vypíše všechny pole uvedené v sekci elements (pole lze vynechat přidáním atributu skip). Pokud potřebujete složitější layout, můžete pole v šabloně rozepsat i jednotlivě:
<table>
<tr>
<td>{title.lb}</td>
<td>{title}</td>
</tr>
<tr>
<td>{description.lb}</td>
<td>{description}</td>
</tr>
...
</table>
Po odeslání formuláře jsou uživatelem vyplněné hodnoty načtené v poli $form->values. Lze je upravovat, vypsat nebo uložit do databáze buď samostatně, nebo pomocí formulářových funkcí $form->insert(), $form->update() a $form->delete().
public function updateAction($id)
{
$form = new pclib\Form('tpl/products/form.tpl');
if (!$form->validate()) {
$this->app->error("Chybně zadané parametry.", "alert alert-danger");
};
$form->update('products', ['id' => $id]);
$this->app->message('Položka byla uložena.', "alert alert-success");
$this->redirect('products/show/id:' . $id);
}
Tato akce se zavolá po odeslání formuláře, provede kontrolní validaci (ta uživatelská se vykoná už v prohlížeči před odesláním) a aktualizuje produkt v databázi. Následně provede přesměrování na products/show a zobrazí informační message.
Pokud je formulář odeslaný, je nastavená hodnota $form->submitted a obsahuje název odesílacího tlačítka.
Ukládání souborů
Pro nejjednodušší práci se soubory postačí základní nastavení:
input SOUBOR lb "Obrázek" file into "/uploaded" accept "image/*"
V atributu into je cesta k adresáři, kam se má soubor uložit a formulář řeší ukládání, mazání a aktualizování souboru sám. Pokud se formulář ukládá do databáze, musí být v tabulce sloupec SOUBOR (pro každý ukládaný soubor musí existovat příslušné pole v tabulce pojmenované jako input ve formuláři). Pak funkce $form->insert(), $form->update() a $form->delete() budou automaticky aktualizovat všechny soubory ve formuláři.
Pokud chceme mít ukládání souboru zcela ve vlastní režii, můžeme v šabloně nastavit parametr nosave a po odeslání formuláře získat soubor takto: $file = $form->getFile('SOUBOR');
Použití FileStorage
Služba fileStorage umožňuje ukládat libovolné množství souborů k libovolné položce aplikace. Veškeré informace o souborech jsou v databázové tabulce FILESTORAGE, což umožňuje prohledávání, hromadné zpracování apod. Vlastní soubory jsou ukládány do podadresářů ./uploaded/rok/měsíc/
Třída FileStorage dovoluje pracovat se soubory jednotným způsobem. Nejprve inicializujeme službu v konfiguračním souboru aplikace:
$config = [
'pclib.app' => [
'autostart' => ['db', 'fileStorage'],
],
'service.fileStorage' => ['rootDir' => './uploaded'],
];
Soubory budou pak ukládány do podadresářů ./uploaded/rok/měsíc/. Tedy např. uploaded/2025/10/clients_18urYQjU.jpeg.
Zavolání $form->update('clients', 1); uloží soubory ve formuláři pro klienta s id=1. Obdobně pracují i funkce $form->insert() a $form->delete().
Chceme-li získat všechny soubory nahrané k tomuto klientovi ['clients', 1] zavoláme:
$fs = $app->fileStorage; $files = $fs->getFiles(['clients', 1]);
Každý záznam pole $files obsahuje řádek z tabulky FILESTORAGE, kde je ID, FILEPATH (cesta k souboru), ORIGNAME (originální název), SIZE (velikost souboru) a další údaje.
Se soubory můžeme pracovat pomocí funkcí třídy FileStorage: getFile(), setFile(), deleteFile() pro jeden soubor, nebo getFiles(), setFiles(), addFiles(), deleteFiles() které vrátí, nastaví nebo smaže všechny soubory přiřazené konkrétní entitě.
Konkrétní soubor lze v kterékoliv funkci adresovat jako pole [typ_entity, id_entity, id_souboru] anebo jen pomocí id záznamu tabulky FILESTORAGE – např. $fs->getFile(['clients', 1, 'SOUBOR']) resp. $fs->getFile($id)
Jedná-li se o obrázek, nebo jiný zobrazitelný soubor, můžeme ho vypsat funkcí $fs->output(), která doplní potřebné hlavičky:
$fs->output(['clients', 1, 'SOUBOR']); //Vypiš soubor
| Další tipy: | |
|---|---|
| $file = $fs->getFile($id, true) | Načte soubor včetně obsahu $file['CONTENT']. |
| $fs->output(['HASH' => $hash]) | Vypíše soubor podle jeho hashe z tabulky FILESTORAGE - bezpečnější způsob neumožňující neoprávněný přístup k jiným souborům. |
| $files = $fs->postedFiles() | Manuální načtení souborů po odeslání formuláře. |
Databáze
Pclib\Db je služba pro zjednodušení práce s databází, která mj. ošetřuje parametry dotazů, tak, aby nebyl možný útok pomocí sql-injection. Inicializovat ji můžete v index.php příkazem $app->db = new pclib\Db('pdo_mysql://jmeno:heslo@host/jmeno_databaze'); nebo pomocí konfiguračního souboru. V metodě controlleru k ní lze přistupovat jako $this->app->db.
Připojovací řetězec je jednotný a obsahuje uživatelské jméno a heslo, adresu hosta a jméno databáze - například 'pdo_mysql://root:abc123@127.0.0.1/test-app'.
Ukázka použití:
| $product = $db->select('products', ['id' => 1]) | Vrátí záznam s id=1 z tabulky products |
| $products = $db->selectAll('products', ['price' => 1000]) | Vrátí všechny produkty s cenou 1000 |
| $product = $db->select('products:id,title,price', ['id' => 1]) | Vrátí vybraná pole produktu 1 |
| $id = $db->insert('products', ['title' => 'Nový produkt', 'price' => 1000, 'category' => 1]) | Vloží nový produkt a vrátí jeho id |
| $db->update('products', ['title' => 'Nový název', 'price' => 1000], ['id' => $id]) | Aktualizuje produkt s id=1 |
| $db->delete('products', ['id' => 1]) | Smaže produkt s id=1 |
| $res = $db->query("select * from products where id=1"); $row = $db->fetch($res); |
Obecný dotaz a vrácení jeho hodnoty |
Pokud potřebujeme zadat složitější podmínku s parametry můžeme využít zadání parametrů pomocí {nazev_parametru} nebo {#nazev_parametru} pro numerickou (integer) hodnotu.
| $products = $db->selectAll('products', "price > {#price}", ['price' => 1000]) | Vrátí produkty s cenou větší než 1000 |
| $products = $db->selectAll("select * from products where title like '%{search}%' order by title", ['search' => 'notebook']) | Vrátí produkty s názvem obsahujícím 'notebook' |
| $db->update('products', ['published' => 0], "title like '%notebook%'") | Nastaví notebooky jako nepublikované |
Existují i další varianty zadávání podmínek s parametry - viz referenční manuál
Jen tak zajistíte, že vstupem z parametru nebude možné podstrčit sql-injection kód.
ORM
Pro pokročilejší práci s databází lze využít vrstvu ORM. Ta se skládá ze dvou objektů: Selection a Model reprezentujících databázový dotaz a databázový záznam. Nejjednodušeji je můžeme získat v controlleru metodami $this->selection() a $this->model().
public function latestAction()
{
$sel = $this->selection('products')->where("active=1")->order("published_at desc")->limit(100);
$grid = new pclib\Grid('tpl/products/grid.tpl', 'products');
$grid->setSelection($sel);
return $grid;
}
public function showAction($id)
{
$product = $this->model('products', $id);
if (!$product) $this->app->error("Produkt nenalezen!", "alert alert-danger");
return $this->template('tpl/products/detail.tpl', $product->toArray());
}
Některé další funkce třídy Selection: $sel->first() - vrátí první záznam výběru, $sel->delete() - smaže výběr, $sel->toArray() - vrátí ho jako pole, $sel->toSql() - vrátí SQL kód výběru, $sel->count() - vrátí počet řádků, $sel->sum('price') - sumarizuje cenu ze všech řádků výběru.
Model reprezentuje řádek databázové tabulky. Pokud nenadefinujeme vlastní třídu, vytvoří se jako instance bázové třídy Model. S ní lze pracovat podle návrhového vzoru ActiveRecord.
$product = $this->model('products', $id);
$product->price = 1000; //Nastavíme pole price v tabulce products
$product->save(); //Uložíme změny do databáze
Hlavní síla modelu je v tom, že můžeme vytvořit šablonu definující např. vazby mezi modely v adresáři /models/templates nebo i vlastní třídu v adresáři /models. Pokud pclib najde soubory s odpovídajícím názvem (např. models/ProductsModel.php a models/templates/Products.tpl) použije je pro tabulku products.
<?elements
class model table "products"
relation category table "categories" key "category_id" owner
relation variants table "product_variants" key "product_id" many
relation images table "product_images" key "product_id" many
event ondelete delete "variants,images"
?>
Nyní lze v modelu používat vazby (relation) kategorie a varianty produktu, odkazující se na vazební tabulky. Můžeme vypsat titulek kategorie nebo vrátit všechny varianty produktu. Vazba 1:N vrací Selection, takže můžeme varianty řadit, filtrovat apod.
Pomocí event ondelete... můžeme určit, co se má stát v případě smazání záznamu. Atribut delete definuje navazující záznamy, které budou smazány spolu s produktem. Lze použít i atribut cancel_when "relace1,relace2,...", který odmítne smazat produkt, jestliže existuje navazující záznam.
$title = $product->category->title;
print "Počet variant: " . $product->variants->count();
foreach($product->variants->order('title') as $variant) {
print "<br>";
print $variant->title;
}
Kromě šablony můžeme definovat i třídu modelu s vlastními metodami, které lze použít všude tam, kde máme načtený model (např. produktu).
class ProductsModel extends pclib\orm\Model
{
public function getPriceInCurrency($currency) {}
protected function deleteFiles() {}
//Smazani priloh pri smazani modelu.
public function delete()
{
$ok = parent::delete();
if ($ok) $this->deleteFiles();
return $ok;
}
}
Některé další funkce třídy Model: $model->save() - Uloží změny do databáze, $model->delete() - Smaže záznam, $model->find($id) - Vyhledá model s primárním klíčem $id a načte ho, $model = Model::create('tabulka', $zaznam) - vytvoří nový záznam v databázi.
Další informace o pclib ORM naleznete v PDF dokumentu Pclib ORM.
Služba Auth
Systém autentizace a autorizace pracuje se třemi typy objektů: uživatelé, role a oprávnění. Role má přiřazená oprávnění a uživatel může mít přiřazenou jednu nebo více rolí. Je možné přiřadit oprávnění i přímo konkrétnímu uživateli. Uživatel se přihlašuje pomocí uživatelského jména a hesla. V aplikaci pak můžeme testovat oprávnění přihlášeného uživatele.
Službu inicializujeme buď v souboru index.php: $app->auth = new pclib\Auth nebo prostřednictvím konfigurace.
'service.auth' => [
'algo' => 'bcrypt',
'secret' => '',
],
'pclib.app' => [
'autostart' => ['db', 'auth'],
]
Parametr 'algo' udává hashovací algoritmus pro ukládání hesel. Možnosti jsou 'md5', 'bcrypt' a 'bcrypt-md5'. Při použití md5 je nutné ještě nastavit 'secret'. Jedná se o náhodný řetězec alespoň deseti znaků, který se využívá k posílení hesla. Před použitím Auth je potřeba spustit i službu databáze, kterou Auth využívá.
Přihlášení uživatele se provede příkazem $auth->login('jmeno', 'heslo'), odhlášení pomocí $auth->logout(). Funkce login() vrací false pokud se přihlášení nepodařilo a chyby /např. chybné heslo/ lze poté vyčíst z pole $auth->errors. Po úspěšném přihlášení je přihlášený uživatel dostupný přes $auth->loggedUser.
Pro přihlášení, odhlášení, registraci, zapomenuté heslo apod. je nejlépe vytvořit samostatný controller.
class UserController extends BaseController {
function signinAction()
{
return new pclib\Form('tpl/user/signin.tpl');
}
function loginAction()
{
$form = new pclib\Form('tpl/user/signin.tpl');
$ok = $this->app->auth->login($form->values['username'], $form->values['password']);
if ($ok) {
$this->app->message('Vítáme Vás na našich stránkách.', 'alert alert-success');
$this->redirect('home');
}
else {
$this->app->message('Chybné přihlašovací údaje.', 'alert alert-danger');
$this->redirect('user/signin');
}
}
function logoutAction()
{
$this->app->auth->logout();
$this->app->message('Byl jste odhlášen.', 'alert alert-success');
$this->redirect('home');
}
}
Akce user/signin zobrazí přihlašovací formulář, akce user/login resp. user/logout uživatele přihlásí resp. odhlásí a přesměruje na home.
Nyní můžeme testovat oprávnění přihlášeného uživatele v controlleru.
function editAction($id) {
if (!$this->app->auth->hasRight('products/edit') {
$this->app->error('Nemáte oprávnění editovat produkty');
}
...
}
Lze použít i pomocnou metodu authorize() která ukončí aplikaci se standardní chybovou hláškou, jestliže uživatel nemá oprávnění. Pokud je nepřihlášený, přesměruje ho na přihlašovací formulář, standardně na routu user/signin.
function editAction($id) {
$this->authorize('products/edit');
...
}
Často bývá výhodné vytvořit si v BaseControlleru zkratku k přihlášenému uživateli, aby byl dostupný ve všech našich controllerech.
class BaseController extends pclib\Controller {
protected $user;
function __construct($app)
{
parent::__construct($app);
$this->user = $this->app->auth->loggedUser;
}
}
Proměnná $this->user je buď null pro nepřihlášeného uživatele, nebo objekt typu AuthUser.
function ordersAction()
{
if (!$this->user) {
return "Nelze zobrazit objednávky pro nepřihlášeného uživatele.";
}
$orders = $this->app->db->selectAll('orders', ['user_id' => $this->user->id]);
return $this->template('tpl/orders/list.tpl', ['orders' => $orders, 'user' => $this->user->getValues() ]);
}
Lze vytvořit oprávnění, které obsahuje wildcard '*'. Pokud má uživatel přiřazené právo products/*, znamená to, že má povolená práva products/edit, products/delete, products/jakykoliv-retezec.
| Id oprávnění | Popis |
|---|---|
| products/edit | Editace produktů |
| products/delete | Smazání produktu |
| products/* | Všechna práva produkty |
| * | Všechna práva |
Nastavení rolí, uživatelů a oprávnění je nejjednodušší pomocí aplikace padmin. Lze to ovšem i programově pomocí třídy AuthManager.
Logování
Logování se standarně provádí do databázové tabulky LOGGER. K prohlížení, vyhledávání nebo mazání starších logů lze využít nástroj padmin. Před použitím službu logger inicializujeme v konfiguraci:
'pclib.app' => [
'autostart' => ['db', 'logger'],
]
V aplikaci se záznam do logu zapíše funkcí $logger->log(...), nebo nejčastěji v controlleru pomocí $this->app->log(...);
public function updateAction($id)
{
...
$this->app->log('action', 'products/update', null, $id);
$this->app->message('Položka byla uložena.', "alert alert-success");
$this->redirect('products/show/id:' . $id);
}
Parametry funkce log() jsou: kategorie, id zprávy, zpráva (volný text) a id položky. Kategorie a id zprávy jsou povinné a do databáze se ukládají z důvodu efektivity pomocí číselného id, které se odkazuje na číselník. Pokud id zprávy nebo kategorie dosud neexistuje, automaticky se založí.
Zprávu lze volitelně použít, pokud je potřeba více informací k logovanému záznamu: $this->app->log('action', 'mail/send', "10 mailů úspěšně odesláno.");
Poslední položka udává id objektu, kterého se záznam týká - zde id produktu. Kromě těchto informací se zaznamenává také čas, id přihlášeného uživatele, ip adresa, user agent a operační systém ze kterého byl požadavek odeslán.
Nastavením konfiguračního parametru pclib.errors['log'] na true se zapne logování chyb aplikace.
Debugging
K podpoře ladění aplikace slouží především několik nástrojů výpisu obsahu proměnných. Funkce dump($promenna1, $promenna2, ...) zobrazí vizuálně vylepšený výpis hodnoty proměnných (může se jednat i o pole nebo objekty) a ukončí běh skriptu.
Jestliže je v konfiguračním souboru zapnutý konfigurační klíč develop, pclib budou při chybě zobrazovat rozšířený výpis chyby včetně posloupnosti volání funkcí (stack trace).
$develop = [ 'pclib.errors' => ['display' => true, 'develop' => true], ];
Na produkčním serveru by naopak měl být develop vždy vypnutý, protože může zobrazovat bezpečnostně citlivé informace a naopak by mělo být aktivní ukládání chyb do logu. Pclib implicitně považují za develop localhost a production nastavení použijí kdekoliv jinde.
Ladící funkce pclib:| dump(...) | Vypíše obsah proměnných a ukončí aplikaci |
| jdump(...) | Vypíše obsah proměnných do javascript konzole prohlížeče |
| ddump(...) | Vypíše obsah proměnných do debugovací lišty pclib |
Při vývoji lze zapnout také debugovací lištu nastavením konfiguračního parametru pclib.app['debugmode'] na true. V liště se zobrazují volané požadavky, všechny databázové dotazy, hodnoty odeslané formulářem a debugovací výpisy pomocí funkce ddump().
Na serveru localhost je možné v debugovací konzoli spustit php kód v kontextu aplikace. Proměnná $app je zde objekt aplikace a $c je aktuální controller.
Vícejazyčnost
Překlad textu provedeme pomocí funkce $app->translate(), která vyhledá text v aktivním jazyce ($app->language) v tabulce textů a vrátí ho, pokud existuje. Pokud text nenajde, vypíše původní text.
$app->language = 'cs';
print $app->translate('Hello world!'); // Ahoj světe!
print $app->translate('Today is %s', date('d.m.Y')); //Dnes je ...
Tabulku textů lze načíst i ze souboru: $app->translator->useFile('localization/cs.php'); ale nejčastěji se čte z databázové tabulky. Zadávat texty pro různé jazyky můžete pomocí nástroje padmin, který také umožňuje exportovat a importovat tabulku textů ve formátu csv (excel).
Pclib automaticky zařazuje některé texty jako texty k překladu. Jsou to: zprávy aplikace (texty vypisované pomocí $app->message() a $app->error() ), popisky polí (atribut "lb") v šabloně, číselníky (lze vypnout atributem "notranslate") a veškerý text v šablonách ohraničený tagem <M>...</M>
Místo vkládání všech použitých textů do tabulky ručně, je možné nastavit jazyk na source: $app->language = 'source' a pclib potom při zobrazení textu k překladu automaticky zaznamenají zdrojovou podobu textu do databáze.
Události
Globální nastavení událostí je možné pomocí služby $app->events, nastavení pro konkrétní objekt zavoláním funkce on() na tomto objektu.
//Nastavení události pro konkrétní datagrid
$grid->on('grid.before-row', $function);
//Nastavení události pro všechny gridy
$app->events->on('grid.before-row', $function);
Výše uvedená událost spustí funkci $function před výpisem každého řádku gridu. Parametrem volané funkce je objekt $event typu pclib\Event. Ten obsahuje odkaz na volající objekt $event->target a parametry eventu $event->data.
$function = function($event) {
$grid = $event->target;
if ($event->data['row']['akcni_sleva']) $grid->print_Block('akce');
};
Strom
Třída pclib\Tree slouží k vytváření stromové struktury, využitelné například pro tvorbu menu nebo rozbalovacího stromu kategorií. Položky stromu lze načíst z databáze, případně exportovat nebo importovat do textového formátu nebo jako php-pole.
$tree = new pclib\Tree('tpl/tree.tpl');
$tree->load(1);
print $tree->html();
Vytvoříme strom ze šablony tpl/tree.tpl, načteme položky z databázové tabulky TREE_LOOKUPS (TREE_ID=1) a vypíšeme. Se šablonou můžeme pracovat obdobně jako u běžné šablony, např. nastavovat její proměnné pomocí values: $tree->values['CSS_CLASS'] = 'pctree';
Pokud máme stromovou strukturu uloženou například v tabulce categories (s odkazem na rodiče pomocí sloupce parent_id), načteme položky stromu takto: $tree->fromQuery("select id, label, url, parent_id from categories where active=1");
Pokud nezadáme cestu k šabloně, použije se implicitní šablona, jinak si ji můžeme definovat běžným způsobem:
{block root}
<ul id="tree1" class="{CSS_CLASS}">{items}</ul>
{/block}
{block folder}
<li id="i{ID}" class="folder {OPEN}">
<span>{LABEL}</span>
<ul>{items}</ul>
</li>
{/block}
{block item}
<li id="i{ID}"><a href="{URL}">{LABEL}</a></li>
{/block}
Šablona stromu obsahuje tři bloky: pro kořen stromu (root), pro složku (folder) a položku stromu (item). V tomto případě vytváříme html seznam <ul>, který už se dá libovolně nastylovat. Proměnné šablony pocházejí z tabulky nebo dotazu a v podstatě můžou být jakékoliv. Vyžadovány jsou obvykle LABEL (popisek - text), ID a URL.
Načtení z textového souboru a uložení do databáze provedeme následovně:
$tree->importText($text); $tree->save(1); //Ulozit jako TREE_ID=1
$text obsahuje parametry položek stromu oddělené '|' (zde cesta s popiskami a url). Cesta do podvětví (podadresářů) je oddělená lomítkem '/'.
PATH|URL MyShop MyShop/About Us MyShop/About Us/History|https://myshop.cz/history MyShop/About Us/Partners|https://myshop.cz/partners MyShop/Categories MyShop/Categories/Computer|https://myshop.cz/categories/computers
S položkami stromu je možné pracovat i programově:
$node = $tree->find('URL', 'https://myshop.cz/partners');
$node['LABEL'] = 'Our partners';
$tree->set($node['ID'], $node);
Mailer
Mailer slouží k managementu odesílání emailů. Lze definovat šablony mailů, naplánovat jejich odesílání a ukládat odeslané emaily. Správa šablon a emailových adres je k dispozici správcům v aplikaci pamin. Pro vlastní odesílání emailů je nutné nainstalovat emailovou knihovnu - v současnosti je podporován pouze phpmailer od verze 6.9. Nainstalujeme ho příkazem composer require phpmailer/phpmailer.
Nejdřív zapneme službu mailer a nastavíme připojení k SMTP serveru, popřípadě další parametry (layout pro všechny maily a odesílací adresa). Mailer používá databázi, kterou proto inicializujeme také.
'pclib.app' => [ 'autostart' => ['db', 'mailer'], ], 'service.mailer' => [ 'dsn' => 'smtp://user:password@smtp.host.com', 'from' =>'Moje aplikace <noreply@myapp.cz>', 'layout' =>'tpl/mails/layout.tpl', ], 'service.db' => ['dsn' => 'pdo_mysql://user:password@localhost/myshop'],
Nyní můžeme odesílat emaily:
$app->mailer->send('order-confirm.tpl', $order, ['to' => 'josef.novak@gmail.com']);
Tento příkaz odešle mail potvrzení objednávky (šablona order-confirm) na adresu zákazníka. Data objednávky jsou v poli $order (použijí se v šabloně). Šablona je standardní šablona, která umožňuje navíc v sekci elements definovat i emailová pole jako třeba předmět mailu. Šablony se hledají v adresáři tpl/mails/ což můžeme změnit pomocí konfiguračního parametru templates_path.
<?elements
class mail
mail_field subject default "Potvrzení objednávky"
?>
<p>Vaše objednávka číslo {ORDER_NO} byla přijata.</p>
Jak v šabloně, tak i ve funkci send() můžeme použít jakákoliv emailová pole (from, to, cc, bcc, subject, replyTo). Pokud je pole definované v šabloně i v kódu, upřednostní se hodnota z funkce send(). Pro zasílání notifikací nebo jiných "plain message" mailů si můžeme udělat jednoduchou šablonu:
$app->mailer->send('notify.tpl', ['body' => 'Zpráva z aplikace.'], ['to' => 'user@myshop.cz', 'subject' => 'MyShop Notifikace', 'cc' => 'admin@myshop.cz']);
<p>{body}</p>
Kterákoliv adresa může obsahovat také popisek: 'cc' => 'Josef Novák <admin@myshop.cz>'.
V aplikaci padmin si lze vypsat všechny emaily odeslané nebo naplánovaná za posledních deset dní (dobu uchování lze změnit konfiguračním parametrem keep_days).
Místo šablony uložené v souboru můžeme použít šablonu uloženou v databázi. Výhoda je, že tyto šablony mohou editovat správci v aplikaci padmin, aniž by bylo nutné zasahovat do kódu. Následující příkaz použije databázovou šablonu order_confirm, pokud existuje.
$app->mailer->send('order-confirm', $order, ['to' => 'josef.novak@gmail.com']);
Email se nemusí odeslat hned, ale lze ho pouze naplánovat k odeslání:
$app->mailer->schedule('order-confirm', $order, ['to' => 'josef.novak@gmail.com']);
Odeslání se provede zavoláním $app->mailer->dispatch(); např. pomocí cron-jobu.
Mail s přílohou odešleme takto:
$app->mailer->send('notify', ['body' => 'Všechno nejlepší k novému roku.'], ['to' => 'all@myshop.cz', 'subject' => 'Přání k novému roku', 'attachments' => ['uploaded/prani.pdf']);
Je možné přílohu zadat i v šabloně mailu (v sekci elements):
attachment prani default "uploaded/prani.pdf"
Mailer nabízí i další funkce jako automatické logování odeslaných mailů, události mailer.before-send, mailer.after-send nebo vývojářský režim, kdy se maily neodesílají, ale posílá se jejich preview na zadanou adresu: $app->mailer->setDeveloperOnlyMode('tester@myshop.cz');
Jobs
Pro pravidelné spouštění dávkových úloh se obvykle používá služba cron, ale ne vždy má správce přístup k nastavení cronu na webhostingu a jeho konfigurace nemusí být jednoduchá. Proto je možné spravovat dávkové úlohy pomocí padmin. Systém umožňuje úlohy snadno přidávat, zapínat a vypínat, zobrazuje čas posledního spuštění i výsledek, zaznamenává spuštění úloh do logu, lze nastavit periodu nebo úlohu spustit ručně.
K zprovoznění je nejprve nutné nastavit volání JobManageru pomocí cronu. Veškerou další správu je pak možné provádět pomocí administrátoru. V cronu nastavíme volání adresy https://moje-aplikace.cz/admin/?r=jobs/run&key=[api_key], kde [api_key] je hodnota aplikačního klíče nastavená v konfiguraci padminu.
Novou úlohu přidáme vytvořením souboru JobUloha.php v adresáři admin/jobs. Soubor obsahuje stejně pojmenovanou třídu odvozenou od třídy Job. Poté už jednoduše přidáme úlohu pomocí uživatelského rozhraní: menu Jobs -> Přidat úlohu a do kolonky "Příkaz" napíšeme název úlohy, tedy "JobUloha".
class JobCleanOrders extends Job
{
function run()
{
$res = $this->app->db->delete('orders', "status=-1 AND (created_at < NOW() - INTERVAL 1 WEEK)");
return "Smazáno ".$res->rowCount()." nedokončených objednávek.";
}
}
Výše uvedená úloha vymaže nedokončené objednávky starší než jeden týden. Vlastní kód vytvoříme implementací funkce run() jejíž návratová hodnota je výsledek úlohy, který se zobrazuje v administrátoru. Pomocí $this->app můžeme přistupovat k objektu aplikace a jeho službám.
Třída Str
Třída Str obsahuje utility pro práci s textovými řetězci. Používá multibyte funkce, takže funguje správně s utf-8 řetězci. Použití vypadá takto:
use pclib\Str; print Str::length($s); //Počet znaků utf-8 řetězceStr obsahuje jak funkce běžné v php (např: lower/upper - konverze na malá/velká písmena, lpad/rpad - doplní mezery nebo jiný znak zleva/zprava, strpos, substr, nebo contains - vrátí true pokud řetězec obsahuje podřetězec), tak i několik nových funkcí.
| Str::ascii($s) | Konverze na text bez diakritiky | Str::ascii('Řečiště'); // Reciste |
| Str::random($length) | Řetězec náhodných znaků o délce $length | Str::random(10); // Ac85ge7Qw1 |
| Str::webalize($s) | Konvertuje text na formát vhodný k použití v URL | Str::webalize('Akční nabídka'); // akcni-nabidka |
| Str::format($s, $params) | Řetězec s parametry | Str::fomat('Hello {name}!', ['name' => 'John']); // Hello John! |
| Str::filter($s, $regexp) | Zachová v řetězci pouze znaky definované regulárním výrazem | Str::filter("Zápis @porada! #5.docx", "\w."); // Zápisporada5.docx |
| Str::match($s, $regexp) | Vrátí nalezený řetězec podle regulárního výrazu | Str::match("Číslo je 123.", "/\d+/"); // 123 |
| Str::htmlTag($name, $atrib, $content) | Vytvoří html tag s atributy a obsahem | Str::htmlTag('a', ['href' => 'example.com'], 'Example link'); // <a href="example.com">Example link</a> |