Nastavení aplikace

Rudolf Svátek 2016-07-21 14:52

Zase si můžeš stáhnout celou aplikaci: kapitola13.zip.

Bude vhodné mít nějakou možnost nastavit si různé vlastnosti aplikace. Třeba název webových stránek, logo, počet novinek ve výpisu, kontakty na majitele stránek apod. Fantazii se meze nekladou. Cokoli budeš chtít mít v nastaveních, to si můžeš snadno kdykoli přidat.

Už asi nebudu podrobně opakovat co musíš všechno zajistit - modul, presenter, továrny formulářů, služby, a šablony. Víš, nebo ti prozradím, že se modul týká tabulky Settings. Od toho se budou odvíjet názvy souborů, tříd a služeb. Jen tě uklidním, že v případě administrace nastavení nebudeš v modulu (tedy v Entity, Mapperu, ani Repository) potřebovat nic, co už neumí Base třídy. Takže Entitu vytvoříš podle toho, cos už viděl v případě uživatelů, stránek i novinek. No a pak v Mapperu a Repository necháš jen constructor. Jestli se ti to nechce psát, použij třeba model z novinek a jen přepiš názvy souborů, tříd apod.

A nebo jo - vypíšu tu kódy.

ISettingsEntity.php:

<?php

namespace App\Model;

interface ISettingsEntity 
{

   public function id();

   public function field();

   public function value();

}

SettingsEntity.php:

<?php

namespace App\Model;

class SettingsEntity extends \Nette\Object implements ISettingsEntity
{

   private $id;
   private $field;
   private $value;

    public function id($id = null) {
        if (!is_null($id))
            $this->id = $id;
        return $this->id;
    }

    public function field($field = null) {
        if (!is_null($field))
            $this->field = $field;
        return $this->field;
    }

    public function value($value = null) {
        if (!is_null($value))
            $this->value = $value;
        return $this->value;
    }

}

SettingsMapper.php:

<?php

namespace App\Model;
 
use Dibi\Connection;

/**
 * Class SettingsMapper
 * @package App\Model
 */
class SettingsMapper extends BaseMapper 
{
 
	/**
	 * SettingsMapper constructor.
	 * @param Connection $db
	 */
	public function __construct(Connection $db) {
		parent::__construct($db);
	}
 
}

SettingsRepository.php:

<?php

namespace App\Model;

/**
 * Class SettingsRepository
 * @package App\Model
 */
class SettingsRepository extends BaseRepository 
{

	/** @var SettingsMapper */
	private $mapper;

	/**
	 * SettingsRepository constructor.
	 * @param SettingsMapper $mapper
	 */
	public function __construct(SettingsMapper $mapper) {
		parent::__construct($mapper);
		$this->mapper = $mapper;
	}

}

Tak vidíš, že to nic nebylo :-)

To presenter je už trochu zajímavější. Do této chvíle jsme v presenterech zařizovali zobrazení a zpacování formulářů pro editaci jednoho záznamu, nebo přidání nového. V nastaveních ale nebudeme přidávat žádný záznam. Jen budeme chtít editovat všechny záznamy z tabulky Settings. Takže vznikne jen formulář pro editaci. Do něj musíme načíst všechny záznamy a vyplnit formulář současnými hodnotami. Presenter tedy vypadá takto:

<?php
  
namespace App\AdminModule\Presenters;
  
use App\Model\SettingsRepository;
use App\Forms\EditSettingsFormFactory;
use Nette\Utils\Finder;

/**
 * Class SettingsPresenter
 * @package App\AdminModule\Presenters
 */
class SettingsPresenter extends BasePresenter
{

	/** @var SettingsRepository @inject */
	public $settingsRepository;
 
	/** @var EditSettingsFormFactory @inject */
	public $editSettingsFormFactory;

	/**
	 * Inicializace třídních proměnných
	 */
	public function startup() {
		parent::startup();
	}

	/**
	 *
	 */
	public function renderEdit() {
		/** @var Form $form */
		$form = $this['editForm'];
		if (!$form->isSubmitted()) {
			$rows = $this->settingsRepository->getAll()->fetchPairs('field', 'value');
			$form->setDefaults($rows);

			if(isset($rows['logo']) && $rows['logo'] > '')
				$this->getTemplate()->logo = $rows['logo'];
		}
	}

	/**
	 * @return Form
	 */
	protected function createComponentEditForm() {
		$form = $this->editSettingsFormFactory->create();
		$form->setTranslator($this->translator);
		$form->onSuccess[] = function () {
			$this->flashMessage('Změny byly uloženy', 'success');
			$this->redirect('this');
		};
		return $form;
	}

}

Aby to ale fungovalo, zase potřebujeme factory formuláře:

<?php

namespace App\Forms;

use App\Model\SettingsEntity;
use App\Model\SettingsRepository;
use App\Model\PagesRepository;
use Nette\Application\UI\Form;
use Kdyby\BootstrapFormRenderer\BootstrapRenderer;

/**
 * Class EditSettingsFormFactory
 * @package App\AdminModule\Presenters
 */
class EditSettingsFormFactory
{

	/** @var FormFactory */
	private $factory;
  
	public function __construct(FormFactory $factory, 
			SettingsRepository $settingsRepository,
			PagesRepository $pagesRepository) {
		$this->factory = $factory;
		$this->settingsRepository = $settingsRepository;
		$this->pagesRepository = $pagesRepository;
	}

	/**
	 * @return Form
	 */
	public function create() {
		$form = $this->factory->create();
		$pages = $this->pagesRepository->getAll()->fetchPairs('id', 'name');

		$form->addText('siteName', "messages.settings.siteName")
			->setAttribute('placeholder', "messages.settings.siteName")
			->setAttribute('class', 'form-control input-sm')
			->setRequired("messages.settings.plsSiteName");
		$form->addText('adminMail', "messages.settings.adminMail")
			->setAttribute('placeholder', "messages.settings.adminMail")
			->setAttribute('class', 'form-control input-sm')
			->setRequired("messages.settings.plsAdminMail");
		$form->addText('companyMail', "messages.settings.companyMail")
			->setAttribute('placeholder', "messages.settings.companyMail")
			->setAttribute('class', 'form-control input-sm')
			->setRequired("messages.settings.plsCompanyMail");
		$form->addText('companyPhone', "messages.settings.companyPhone")
			->setAttribute('placeholder', "messages.settings.companyPhone")
			->setAttribute('class', 'form-control input-sm')
			->setRequired("messages.settings.plsCompanyPhone");
		$form->addText('companyAddress', "messages.settings.companyAddress")
			->setAttribute('placeholder', "messages.settings.companyAddress")
			->setAttribute('class', 'form-control input-sm')
			->setRequired("messages.settings.plsCompanyAddress");
		$form->addText('noreplyMail', "messages.settings.noreplyMail")
			->setAttribute('placeholder', "messages.settings.noreplyMail")
			->setAttribute('class', 'form-control input-sm');
		$form->addText('motto1', "messages.settings.motto1")
			->setAttribute('placeholder', "messages.settings.motto1")
			->setAttribute('class', 'form-control input-sm');
		$form->addText('motto2', "messages.settings.motto2")
			->setAttribute('placeholder', "messages.settings.motto2")
			->setAttribute('class', 'form-control input-sm');
		$form->addText('smtpHost', "messages.settings.smtpServer")
			->setAttribute('placeholder', "messages.settings.smtpServer")
			->setAttribute('class', 'form-control input-sm');
		$form->addText('smtpUsername', "messages.settings.smtpUsername")
			->setAttribute('placeholder', "messages.settings.smtpUsername")
			->setAttribute('class', 'form-control input-sm');
		$form->addText('smtpPassword', "messages.settings.smtpPassword")
			->setAttribute('placeholder', "messages.settings.smtpPassword")
			->setAttribute('class', 'form-control input-sm');
		$form->addText('smtpSecure', "messages.settings.smtpSecure")
			->setAttribute('placeholder', "messages.settings.smtpSecure")
			->setAttribute('class', 'form-control input-sm');
		$form->addCheckbox('useMail', "messages.settings.useMail")
			->setAttribute('placeholder', "messages.settings.useMail")
			->setAttribute('class', 'bootstrap');
		$form->addTextArea('ownScript', "messages.settings.footerScript")
			->setAttribute('placeholder', "messages.settings.footerScript")
			->setAttribute('class', 'form-control input-sm')
			->setAttribute('rows', '20');
		$form->addSelect('thanksPage',  "messages.settings.thanksPage", $pages)
			->setAttribute('class', 'form-control input-sm select2');
		$form->addCheckbox('showAsBlog', "messages.settings.showAsBlog")
			->setAttribute('class', 'bootstrap');
		$form->addText('pagesPerPage', "messages.settings.pagesPerPage")
			->setAttribute('placeholder', "messages.settings.pagesPerPage")
			->setAttribute('class', 'form-control input-sm');
		$form->addText('rowInRss', "messages.settings.rowInRss")
			->setAttribute('placeholder', "messages.settings.rowInRss")
			->setAttribute('class', 'form-control input-sm');
		$form->addText('logo', "messages.settings.logo")
			->setAttribute('placeholder', "messages.settings.logo")
			->setAttribute('onclick', 'openKCFinder(this)')
			->setAttribute('class', 'form-control input-sm');
		$form->addText('sliderInterval', "messages.settings.interval")
			->setAttribute('placeholder', "messages.settings.interval")
			->setAttribute('class', 'form-control input-sm');

		$form->addSubmit('process', "messages.settings.btnSubmit")
			->setAttribute('class', 'btn btn-primary');

		$form->setRenderer(new BootstrapRenderer);

		$form->onSuccess[] = function (Form $form) {
			$this->formSucceeded($form);
		};
		return $form;
	}

	/**
	 * @param Form $form
	 */
	public function formSucceeded(Form $form) {
		$values = $form->getValues();
		foreach($values as $id => $value) {
			/** @var SettingsEntity $item */
			$item = $this->settingsRepository->getOneWhere(['field' => $id]);
			if(is_null($item)) {
				$item = new SettingsEntity();
				$item->field($id);
			}
			$item->value($value);
			$this->settingsRepository->save($item);
		}
	}

}

Mimochodem - stojí za povšimnutí, že tento formulář je připraven k překladu. Malý test - co udělat, aby mluvil česky?

Tak jo, ještě zbývá vytvořit služby v configu:

	- App\Forms\EditSettingsFormFactory
	settingsMapper: App\Model\SettingsMapper
	- App\Model\SettingsRepository(@settingsMapper)

a pak ještě šablonu pro editaci edit.latte. Stačilo by opět jednoduché vložení dvou řádků, ale já se rozhodl vypisovat formulář ručně. Je to víc práce, ale odměnou je pěkný vzhled:

{block content}
<div class="row">
	<div class="col-sm-12">
		<div class="panel panel-default">
			<div class="panel-heading">
				{_ messages.settings.title}
			</div>
			<div class="panel-body">
				<ul class="nav nav-tabs" id="myTab">
					<li class="active"><a data-toggle="tab" aria-expanded="true" href="#application">Aplikace</a></li>
					<li><a data-toggle="tab" href="#smtp">SMTP server</a></li>
					<li><a data-toggle="tab" href="#owncsript">Vlastní javascript</a></li>
				</ul>
				<form n:name=editForm class="form-horizontal">
					<div class="tab-content">
						<div>&nbsp;</div>
						<div id="application" class="tab-pane active">
							<div class="form-group">
								<div class="col-sm-6">
									<div class="col-sm-12 input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.siteNameTitle}"
												data-toggle="popover"
												data-trigger="focus" data-content="{_ messages.settings.siteNameContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input siteName}
									</div>
								</div>
								<div class="col-sm-6">
									<div class="col-sm-12 input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.adminMailTitle}"
												data-toggle="popover"
												data-trigger="focus" data-content="{_ messages.settings.adminMailContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input adminMail}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-6">
									<div class="col-sm-12 input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.companyAddressTitle}"
												data-toggle="popover"
												data-trigger="focus" data-content="{_ messages.settings.companyAddressContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input companyAddress}
									</div>
								</div>
								<div class="col-sm-3">
									<div class="col-sm-12 input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.companyMailTitle}"
												data-toggle="popover"
												data-trigger="focus" data-content="{_ messages.settings.companyMailContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input companyMail}
									</div>
								</div>
								<div class="col-sm-3">
									<div class="col-sm-12 input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.companyPhoneTitle}"
												data-toggle="popover"
												data-trigger="focus" data-content="{_ messages.settings.companyPhoneContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input companyPhone}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-6">
									<div class="col-sm-12 input-group margin-bottom-sm">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.motto1Title}"
												data-toggle="popover" data-trigger="focus"
												data-content="{_ messages.settings.motto1Content}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input motto1}
									</div>
								</div>
								<div class="col-sm-6">
									<div class="col-sm-12 input-group margin-bottom-sm">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.motto2Title}"
												data-toggle="popover" data-trigger="focus"
												data-content="{_ messages.settings.motto2Content}"">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input motto2}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-6">
									<div class="col-sm-12 input-group margin-bottom-sm">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.thanksPageTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.thanksPageContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input thanksPage}
									</div>
								</div>
								<div class="col-sm-6">
									<div class="col-sm-12 input-group margin-bottom-sm">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.rowInRssTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.rowInRssContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input rowInRss}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-3">
									<input title="" type="checkbox" n:name=showAsBlog data-off-icon-cls="glyphicon-thumbs-down"
											 data-on-icon-cls="glyphicon-thumbs-up" data-group-cls="btn-group-xs">
									<label n:name=showAsBlog > {_ messages.settings.showAsBlog}</label>
								</div>
								<div class="col-sm-3">
									<div class="col-sm-12 input-group margin-bottom-sm">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.pagesPerPageTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.pagesPerPageContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input pagesPerPage}
									</div>
								</div>
								<div class="col-sm-3">
									<div class="col-sm-12 input-group margin-bottom-sm">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.sliderIntervalTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.sliderIntervalContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input sliderInterval}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-4">
									<div class="col-sm-12 input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.logoTitle}" data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.logoContent}">
												<i class="glyphicon glyphicon-picture"></i>
											</a>
										</span>
										{input logo}
									</div>
									<div id="pictureContainer">
										<a n:ifset="$logo" data-footer="{_ messages.settings.logo}" data-toggle="lightbox"
											href="{$logo}"
											title="{_ messages.settings.logoTitle}">
											<img src="{$logo}" alt="{_ messages.settings.logo}">
										</a>
									</div>
								</div>
							</div>
						</div>
						<div id="smtp" class="tab-pane fade">
							<div class="form-group">
								<div class="col-sm-12">
									<div class="col-sm-12 input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.smtpServerTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.smtpServerContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input smtpHost}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-12">
									<div class="input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.smtpUsernameTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.smtpUsernameContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input smtpUsername}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-12">
									<div class="input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.smtpPasswordTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.smtpPasswordContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input smtpPassword}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-12">
									<div class="input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.smtpSecureTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.smtpSecureContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input smtpSecure}
									</div>
								</div>
							</div>
							<div class="form-group">
								<div class="col-sm-4">
									<input title="" type="checkbox" n:name=useMail data-off-icon-cls="glyphicon-thumbs-down"
											 data-on-icon-cls="glyphicon-thumbs-up" data-group-cls="btn-group-xs">
									<label n:name=useMail > {_ messages.settings.useMail}</label>
								</div>
							</div>
						</div>
						<div id="owncsript" class="tab-pane fade">
							<div class="form-group">
								<div class="col-sm-12">
									<div class="col-sm-12 input-group">
										<span class="input-group-addon">
											<a href="javascript:void(0)" title="{_ messages.settings.footerScriptTitle}"
												data-toggle="popover"
												data-trigger="focus"
												data-content="{_ messages.settings.footerScriptContent}">
												<i class="fa fa-question"></i>
											</a>
										</span>
										{input ownScript}
									</div>
								</div>
							</div>
						</div>
						<div class="form-group">
							<div class="col-sm-12">
								{input process}
							</div>
						</div>
					</div>
				</form>
			</div>
		</div>
	</div>
	<script type="text/javascript">

		function openKCFinder(field) {
			window.KCFinder = {
				callBack: function (url) {
					field.value = url;
					window.KCFinder = null;
				}
			};
			window.open({$basePath} + "/kcfinder/browse.php?type=images&dir=images", 'kcfinder_textbox',
				'status=0, toolbar=0, location=0, menubar=0, directories=0, ' +
				'resizable=1, scrollbars=0, width=800, height=600'
				);
		}

		jQuery(function ($) {
			$('#myTab').find('a').click(function (e) {
				e.preventDefault();
				$(this).tab('show');
			});

			$("ul.nav-tabs > li > a").on("shown.bs.tab", function (e) {
				var id = $(e.target).attr("href").substr(1);
				$.cookie('activeTab', id);
			});

			// on load of the page: switch to the currently selected tab
			var hash = $.cookie('activeTab');
			if (location.hash != '')
				$('#myTab').find('a[href="' + location.hash + '"]').tab('show');
			else if (hash != null)
				$('#myTab').find('a[href="#' + hash + '"]').tab('show');
		});
	</script>
	{/block}

	{block title}{_ messages.settings.title} |&nbsp;{/block}

V šabloně budou opět karty, otazníčky s nápovědou atd. Uprav si to jak chceš. Třeba do slovníku vlož příslušné překlady, pozměň otazníčky s nápovědou za jiné symboly apod. Nějaká pole formuláře vyžaduji jako povinné a jiná ne. V tom máš absolutní svobodu. Experimentuj.

V každém případě, pokud formulář vyplníš a odešleš, zapíší se hodnoty do tabulky Settings. Ony se vlastně zapíší i názvy polí. Proto je celkem snadné doplnit další nastavení bez utnosti měnit strukturu databáze. To by se totiž neobešlo  bez změnu Entity. Takhle jen přidáš políčko do definice formuláře a do šablony a zbytek neřešíš.

Asi budeš chtít upravit i šablonu @layout.latte, aby se v horním menu objevil odkaz na editaci nastavení. Stačí tedy přidat řádek:

 

Super - dnes je to vše. Zase si můžeš stáhnout celou aplikaci: kapitola13.zip.

Redakční systém RS::RS Předchozí kapitola

Něco málo o Ajaxu

Redakční systém RS::RS Celý seriál

Vývoj redakčního systému v PHP

Redakční systém RS::RS Následující kapitola

Foto galerie

Komentáře (0)

Přidej svůj komentář

O mně

Jmenuji se Rudolf Svátek. Jsem lektor výpočetní techniky a PHP programátor. Stavím firemní stránky a eshopy. Aby se mi to dělalo pohodlně, vytvořil jsem redakční systém RS::RS, který ti tu nabízím k použití.

Rychlý kontakt na mně

  • Rudolf Svátek
  • Telefon:
    +420 777 828 353
  • Email:
  • Adresa:
    Josefa Hory 1097/5
    736 01 Havířov
    ČR



Tyto stránky používají Cookies. Používáním stránek s tím souhlasíte Další informace