Administrace stránek - přidání stránky

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.

Komponenta Form

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.

Úprava BasePresenteru

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;

Přidání stránky

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 |&nbsp;{/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.

Celý seriál o vývoji redakčního systému

Komentáře

O mně

Jmenuji se Rudolf Svátek - lektor výpočetní techniky, trochu PHP programátor a SEO konzultant na volné noze.

Adresa

Příčná 326/3
736 01 Havířov

Kontakty

Email: office@rudolfsvatek.cz
Telefon: +420 777 828 353
Skype: svatekr