Všechny větší součásti systému budu řešit jako komponenty. A úplně ideálně i menší součásti. Dokonalá aplikace by vlastně byla složena výhradně z komponent. Vyrobíme tedy 2 komponenty. Jednu pro přidání stránek, druhou pro editaci existující stránky. Ale zvolna - upravíme si pár drobností, aby se nám líp pracovalo.
V aplikaci se postupně bude objevovat spousty míst s různými formuláři. Administrace jich bude plná. Vytvoříme si tedy nějakou továrnu pro vytváření formulářů. Velmi jednoduchou. Ve složce src/BaseModule/Components vytvoř složku Form. V ní soubor FormFactory.php s obsahem:
<?php
namespace BaseModule\Components;
use Nette;
use Nette\Application\UI\Form;
class FormFactory
{
use Nette\SmartObject;
/**
* @return Form
*/
public function create()
{
$form = new Form;
return $form;
}
}
Ještě přidáme novou službu do src/BaseModule/DI/services.neon:
services:
- BaseModule\Components\FormFactory
No a do souboru src/BaseModule/DI/BaseExtension.php přidáme metodu, která načte konfigurační soubor a zaregistruje tak služby:
/**
* Method loadConfiguration
*/
public function loadConfiguration()
{
$builder = $this->getContainerBuilder();
$this->compiler->loadDefinitions(
$builder,
$this->loadFromFile(__DIR__ . '/services.neon')['services'],
$this->name
);
}
Od této chvíle už budeme moci injectnout továrny a sekat jeden formulář za druhým.
Opět uvažuju tak, že při úpravách v každém modulu, budu potřebovat například ID upravovaného záznamu. Takže doplníme do BasePresenteru řádek:
/** @var int */
protected $id;
Ve složce src/PageModule/AdminModule/Components/Page už máme složku Grid a v ní komponentu. Takže vytvoř Složku Add a v ní budeme zase vytvářet 3 soubory, které bude komponenta potřebovat. Začneme s tím nejjednodušším - rozhraním IPageAddFactory.php
<?php
namespace PageModule\AdminModule\Components\Page\Add;
interface IPageAddFactory
{
/**
* @param int $parentId
* @return PageAdd
*/
public function create($parentId);
}
Metoda bude přijímat id stránky, která bude v roli rodiče právě přidávané stránky. A nyní samotná komponenta. Vytvoř soubor PageAdd.php s obsahem:
<?php
namespace PageModule\AdminModule\Components\Page\Add;
use BaseModule\Components\FormFactory;
use Nette\Utils\Strings;
use PageModule\Model\Entity\Page;
use PageModule\Model\Facade\PageFacade;
use Nette;
use Nette\Application\UI\Form;
class PageAdd extends Nette\Application\UI\Control
{
/** @var FormFactory */
private $factory;
/** @var string */
private $templateFile = __DIR__ . '/Page.add.latte';
/** @var PageFacade */
public $facade;
/** @var int */
public $parentId;
/**
* PageAdd constructor.
*
* @param int $parentId
* @param FormFactory $factory
* @param PageFacade $facade
*/
public function __construct(
$parentId,
FormFactory $factory,
PageFacade $facade
) {
parent::__construct();
$this->factory = $factory;
$this->facade = $facade;
$this->parentId = $parentId;
}
/**
* Method render
*/
public function render()
{
$this->template->setFile($this->templateFile);
$this->template->render();
}
Prozatím jde jen o obecné vytvoření komponenty, která zatím nic nedělá. To hned napravíme a vyrobíme formulář pro přidání stránky:
/**
* Method create
*
* @return Form
*/
public function createComponentAddForm()
{
$form = $this->factory->create();
$form->addHidden('parent')
->setDefaultValue($this->parentId);
$form->addText('name', 'Název')
->setAttribute('placeholder', 'Název stránky')
->setAttribute('class', 'form-control input-sm')
->setRequired('Zadejte prosím Název');
$form->addText('url', 'URL')
->setAttribute('class', 'form-control input-sm')
->setAttribute('placeholder', 'URL stránky');
$form->addCheckbox('status', 'Aktivní')
->setAttribute('class', 'bootstrap');
$form->addCheckbox('on_homepage', 'Na úvodní stranu')
->setAttribute('class', 'bootstrap');
$form->addCheckboxList(
'in_menu',
'Položka menu:',
[
'topMenu' => 'Horní menu',
'footerMenu' => 'Menu v patičce',
'sideMenu' => 'Postranní menu',
'otherMenu' => 'Jiné menu',
]
)
->setAttribute('class', 'bootstrap');
$form->addText('menu_title', 'Název do menu')
->setAttribute('class', 'form-control input-sm')
->setAttribute('placeholder', 'Název v menu');
$form->addText('title', 'Titulek')
->setAttribute('class', 'form-control input-sm')
->setAttribute('placeholder', 'Titulek v prohlížeči');
$form->addText('description', 'Description')
->setAttribute('class', 'form-control input-sm')
->setAttribute('placeholder', 'Popisek');
$form->addTextArea('perex', 'Perex')
->setAttribute('class', 'form-control input-sm w-100')
->setAttribute('placeholder', 'Perex');
$form->addTextArea('text', 'Text')
->setAttribute('class', 'form-control input-sm tinymcetext')
->setAttribute('placeholder', 'Text stránky');
$form->addSubmit('save', 'Uložit')
->setAttribute('class', 'btn btn-primary');
$form->addSubmit('saveandstay', 'Uložit a zůstat')
->setAttribute('class', 'btn btn-default');
$form->onSuccess[] = function (Form $form) {
$values = $form->getValues();
$page = new Page();
$page->hydrate($values);
$page->setUrl($this->setUrl($values));
$page->setInMenu(json_encode($values->in_menu));
$page->setParent($this->parentId);
if ($this->facade->countUrl($page->getUrl()) > 0) {
$form->addError('Nepodařilo se uložit stránku. Toto URL se již používá');
} else {
$this->facade->insert($page);
}
};
return $form;
}
Všimni si, že už používáme tu hydrataci, co jsme si minule vyrobili v IdentifiedEntity.php. Jinak bychom museli volat jednotlivě všechny settery dané entity. Sice po té hydrataci volám ještě 2 settery, ale to je proto, že první potřebuje výsledek metody setUrl() a druhý musí nejprve enkodovat json.
Jednoduše vyrobíme formulářové pole pro každou vlastnost, kterou chceme nastavit. Po odeslání formuláře vytvoříme novou entitu stránky a naplníme ji odeslanými daty. Je ale zapotřebí zajistit, aby každá stránka měla jedinečné URL. K tomu slouží metoda setUrl():
/**
* @param array $values
* @return string
*/
private function setUrl($values)
{
$url = '/';
if (!empty($values->url)) {
if ($values->url === '/') {
return $values->url;
}
}
if ($values->url === '') {
if (!empty($values->name)) {
$url = Strings::webalize($values->name);
}
}
return $url;
}
Činnost metody je jednoduchá - pokud je URL nastaveno na "/", jde o homepage a URL se nemění. Jestli je URL prázdné, sestaví se z názvu stránky. Před uložením do databáze se ještě zkontroluje, jestli náhodou vygenerovaná či zadaná URL adresa už neexistuje. Pokud ano, informujeme uživatele a stránku neuložíme.
Na závěr jako třetí soubor komponenty vyrobíme šablonu Page.add.latte:
<div class="row" id="snippet--grid">
<div class="col-sm-12" id="snippet-grid-grid">
<div class="panel panel-default datagrid datagrid-grid">
<div class="panel-body">
<div class="box box-success">
<div class="box-header with-border">
<h3 class="box-title">Přidání stránky</h3>
</div>
{control addForm}
</div>
</div>
</div>
</div>
</div>
Teď ještě zaregistrujeme nové služby. Čili do souboru src/PageModule/DI/services.neon vlož řádek:
- PageModule\AdminModule\Components\Page\Add\IPageAddFactory
No a hurá na úpravu presenteru a šablony. Do presenteru Page.php (už nebudu vypisovat pokaždé cestu) doplníme řádek, který injectne továrnu pro přidání stránky:
/** @var IPageAddFactory @inject */
public $pageAddFactory;
Pak přidáme metodu, která do ID doplní identifikátor stránky, která bude sloužit jako rodič nové stránky. Pokud není žádný uveden, předpokládáme, žerodičem bude hlavní kořenová stránka:
/**
* Method renderAdd
*
* @param int $id
*/
public function actionAdd($id = 1)
{
$this->id = $id;
}
No, a pak ještě vyrobíme tu komponentu, čili formulář ro přidání stránky:
/**
* @return PageAdd
*/
protected function createComponentPageAdd()
{
$control = $this->pageAddFactory->create($this->id);
$control['addForm']->onSuccess[] = function () {
$this->redirect(':Page:Admin:Page:');
};
return $control;
}
Nyní si vyrobíme šablonu presenteru, která komponentu zobrazí. Ve složce src/PageModule/AdminModule/Presenters/Page/templates/Page vyrob soubor s názvem add.latte a obsahem:
{block content}
<section class="content-header">
<h1>
Stránky
<small>Nová stránka</small>
</h1>
<ol class="breadcrumb">
<li><a n:href="Page:"><i class="fa fa-address-card-o"></i> Stránky</a></li>
<li class="active">Nová stránka</li>
</ol>
</section>
{control pageAdd}
{/block}
{block title}Nová stránka | {/block}
Poslední věcí bude doplnění routy do souboru PageExtension.php. Do metody loadConfiguration() přidej řádek, aby routy vypadaly takto:
$this->appendRoute('Page:Admin', 'admin', 'Page:default');
$this->appendRoute('Page:Admin', 'admin/page[/<action>][/<id>]', 'Page:default');
$this->appendRoute('Page:Front', '/<url>', 'Page:view');
Teď už by se měl po zadání adresy http://rsrs.loc/admin/page/add měl zobrazit formulář pro přidání stránky. Zatím tedy nevypadá moc hezky, ale funkční už bude. Zkus něco vyplnit a odeslat.
Minule jsem zapomněl na stažení aplikace, tak tedy dnes nezapomenu. Můžeš si tedy opět stáhnout dnešní změny. Příště se podíváme na editaci stránky.