Dependency injection decorator

In nette/di vendor, there is an extension called Nette\DI\Extensions\DecoratorExtension which is registered into DIC by default. What it does? Let’s say you have many classes of one type: interface IDataProvider { public function setLocale(string $locale): void; public function getData(): array; } abstract class AbsctractDataProvider { protected $locale; public function setLocale(string $locale): void { $this->locale = $locale; } } final class UserDataProvider extends AbsctractDataProvider implements IDataProvider { public function getData(): array { return []; } } final class ProductDataProvider extends AbsctractDataProvider implements IDataProvider { public function getData(): array { return []; } } final class CategoryDataProvider extends AbsctractDataProvider implements IDataProvider { public function getData(): array { return []; } } Now that you have all data providers registered in config.
17/07/2017

Multiplier

Multiplier is a tool for dynamic creation of components. Take a typical situation. Let’s have a list of products in an e-shop. Now we want to create a form for each of the products, that would add that specific product to a basket. One of the possible ways how to do that is to wrap whole product list to a single form. Much more convenient way however is using the Nette\Application\UI\Multiplier available starting Nette 2.
26/06/2016

Removing expired sessions

If you ever wondered why is the session being removed even if you extended its expiration in config.neon. The culprit is likely session removing in PHP itself. Garbage collection The PHP contains GC for session removing. There are few configuration directives which can affect that. We’re interested mainly in session.gc_maxlifetime, session.save_path, session.gc_probability and session.gc_divisor. The first, as name suggests, defines session lifetime. The last two then defines probability in which session GC is run.
02/03/2016

Registering latte filters globally

In this article, I would like to expand the idea of David Matejka. Only thing you have to do is simly register a latte fitler class in config.neon file and then adding just one method to this class each time you want to add different filter: services: latte.latteFactory: setup: - addFilter(null, [App\Filters, loader]) namespace App; class Filters { /** * @return mixed */ public static function loader(string $filter) { return (method_exists(__CLASS__, $filter) ?
02/02/2016

Cool urls and global route filters

This article was taken and translated from (paveljanda.com)[https://paveljanda.com]. Behaviour in dev mode There may be a request to use custom looking urls in your project. Maybe it is a e-commerce project with urls like en/category/another-category/super-tuper-hello-kitty-mug or it could be a blog reflecting all tags in the url: blog/kitten/cute/fluffy/2015-12-13-my-fluffy-day. Either way, you probably noticed that there may be a variable count of segments in particular url. To route application like that, we may use global filter in the Nette\Application\Routers\Route class.
13/12/2015

Remove section from Form

In form factory call: $user = $form->addContainer('user'); $user->addText('username', 'Username'); Everything added to container $user is now in own namespace. You can separate it in values processing like this: $userValues = array_pick($values, 'user'); And this is array_pick: /** * Pick element from the array by key and return its value. * @param array * @param string|int array key * @param mixed default value when item is not present * @return mixed * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided */ function array_pick(& $arr, $key, $default = NULL) { if (array_key_exists($key, $arr)) { $value = $arr[$key]; unset($arr[$key]); return $value; } else { if (func_num_args() < 3) { throw new Nette\InvalidArgumentException("Missing item '$key'.
28/09/2015

More Dibi connections in Neon

parameters: databases: one: username: # set in config.local.neon password: # set in config.local.neon two: username: # set in config.local.neon password: # set in config.local.neon extensions: dibi.one: Dibi\Bridges\Nette\DibiExtension22 dibi.two: Dibi\Bridges\Nette\DibiExtension22 dibi.one: driver: postgre port: 5433 database: xxxxx username: %databases.one.username% password: %databases.one.password% dibi.two: autowired: no # note this parameter driver: oracle database: "(DESCRIPTION = (...))" username: %databases.two.username% password: %databases.two.password% charset: UTF8 services: - App\Model\DataOne # does not need specify connection, the 'one' is autowired - App\Model\DataTwo(@dibi.
18/08/2015

More databases configuration in config.neon

If you need to connect to different database in different repositories. Follows Nette Sandbox configuration. config.neon database: db1: dsn: 'mysql:host=localhost;dbname=db1' user: root password: password db2: dsn: 'mysql:host=localhost;dbname=db2' user: root password: passsword services: - App\Model\Repositories\UserRepository(@database.db1.context) - App\Model\Repositories\PotatoRepository(@database.db2.context)
07/08/2015

ACL and permissions

Let’s start with creating an empty project: composer create-project nette/web-project myweb. Out of the box, you can ask Nette Framework if the user has some roles and whether he is logged in or not: <?php namespace App\Presenters; use Nette\Application\UI\Presenter; class HomepagePresenter extends Presenter { public function actionDefault(): void { dump($this->user->isLoggedIn()); // false dump($this->user->isInRole('admin')); // false die; } } But we would gone crazy if we have to ask for user’s role each and every time trying to find out user’s permission for particular action.
27/07/2015

How to change cache journal from SQLiteJournal to FileJournal

The nette/caching package uses SQLiteJournal as a default journal since its 2.4 version. Just to remind, the journal is used to store some metadata about cache records (like their tags or priority). Sometimes we might however want to use the original FileJournal (for example in case our hosting doesn’t support the SQLite). We achieve that by overwriting service cache.journal in our config.neon. services: cache.journal: factory: Nette\Caching\Storages\FileJournal(%tempDir%)
13/07/2015

texy!

For static Texy! use filter: public function templatePrepareFilters($template) { parent::templatePrepareFilters($template); $texy = new Texy(); $template->registerFilter(callback($texy, 'process')); } For dynamic Texy! use helper: protected function createTemplate($class = NULL) { $template = parent::createTemplate($class); $texy = new Texy(); $template->registerHelper('texy', callback($texy, 'process')); return $template; }
03/07/2015

Blank page after upload on web server

Try these troubleshooting steps: Clear your temp/ folder Check the write permissions for log/ and temp/ Try to uncomment RewriteBase in your .htaccess file Check the logs (folder logs/) Enable Tracy
03/07/2015

Use curly `{}` braces in templates

Curly braces {} are the default delimiters in Latte. If you don’t want the curly braces interpreted as Latte tags (for example, in JavaScript), you need to type a space after {: <div> { Hello } </div> Or you can use the Latte macros {l} and {r}: <div> {l} Hello {r} </div> Or you can turn off the latte syntax for particular block: <div n:syntax="off"> {Hello} </div>
01/07/2015

Component separate rendering of js and html content

@layout.latte {control test:HTML} {control test:JS} TestComponents.php namespace FrontModule\Components; use Nette\Application\UI\Control; use Nette\ComponentModel\IContainer; /** * Description of TestComponents */ class TestComponents extends Control { /** * @param IContainer $parent * @param string $name */ final function __construct(IContainer $parent = NULL, $name = NULL) { parent::__construct($parent, $name); } /** * @see Nette\Application\Control#render() * @return void */ public function render() { $this->renderJS(); $this->renderHTML(); } /** * @return void */ public function renderJS() { $this->template->setFile(dirname(__FILE__) .
30/06/2015

Add data atributes to options elements in select element

Sometimes you need more data in options in select elements like : <select id="country-select" name="country"> <option data-lat="53.412910" data-lon="-8.000000" data-zoom="6" value="102">Czech Republic</option> <option data-lat="53.094024" data-lon="-1.768799" data-zoom="5" value="77">Slovakia</option> </select> So, there is simple solution, use \Nette\Utils\Html for items like: use Nette\Utils\Html; $contries = array( 102 => Html::el()->setText('Czech republic')->data('lon', '-8.000000')->data('lat', '53.412910')->data('zoom', '6'), 77 => Html::el()->setText('Slovakia')->data('lon', '-1.768799')->data('lat', '53.094024')->data('zoom', '5'), ) $form->addSelect('country', 'Country:', $countries); from: http://forum.nette.org/cs/22874-select-a-options-data-attributy
30/06/2015

Latte cache behaviour in production environment

This article was taken and translated from (paveljanda.com)[https://paveljanda.com]. Behaviour in dev mode In development mode, Latte is checking filemtime of each template file in each request. This is the reason why you don’t have to delete all files in the /temp/cache directory when modifying templates. Behaviour in production mode In the production mode, it would slow the application down, hence the templates are once cached and when you make some changes to the template, the change is not applied to the result.
30/04/2015

Different parts of nette have different caching configurations

This article was taken and translated from (paveljanda.com)[https://paveljanda.com]. Nette Framework enables to disable cache to many different parts of the framework. The disbled cache is sometimes represented by Nette\Caching\Storages\DevNullStorage, sometimes by disabling temporary directory. I will describe in following articles what can be disabled and where you don’t have any other choice but to use the cache. Latte Let’s tell Latte not to cache things: services: nette.latteFactory: setup: - setTempDirectory(NULL) We may also add some custom macros:
30/04/2015

Single page microframework

<?php if (empty($template)) { require __DIR__ . '/Nette/loader.php'; $configurator = new Nette\Config\Configurator; $configurator->enableDebugger(__DIR__ . '/log'); // Enable Nette Debugger for error visualisation & logging $configurator->setTempDirectory(__DIR__ . '/temp'); $container = $configurator->createContainer(); // Create Dependency Injection container from config.neon file $template = $container->nette->createTemplate()->setFile(__FILE__); $template->db = new Nette\Database\Connection('sqlite:data/ps.sdb'); $template->render(); exit; } ?> <!DOCTYPE html> <html> <head> <title>Welcome</title> </head> <body> <div n:foreach="$db->table(events)->order(date) as $event"> <h4>{$event->name}</h4> <p>{$event->date|date:"j. n. Y"}</p> </div> ... </body> </html>
24/04/2015

Simple ajax example

This tutorial is created using Nette 2.0.10 Sandbox. Setup First, we download Nette and use content of sandbox folder as base for our tutorial. Than we need to link Nette Ajax addon. Lets download current version of nette.ajax.js. Place it to www/js folder. Templates/@layout.latte {block scripts} <script src="{$basePath}/js/jquery.js"></script> <script src="{$basePath}/js/netteForms.js"></script> <script src="{$basePath}/js/nette.ajax.js"></script> {* Nette Ajax depens on jQuery *} <script src="{$basePath}/js/main.js"></script> {/block} Than we initiate Nette Ajax in main.
24/04/2015

Redirect to same page after form submit

We’re on a protected page and we need to force the user to sign in. We have this place all over the application we want the user to get back to where he started after signing in. Exactly that is solved by storeRequest and restoreRequest, which allows us to “restore request”. In these cases you won’t have to think about how to redirect back with $this->redirect(...). You can have a look at working example in CD-collection in Nette Framework distribution.
24/04/2015

Record editing and passing id to form

If you want to edit a record, you have to pass its id to save function. You can use $form->addHidden() though, but often it’s better to use an action parameter. Most important is fact, that permissions to edit the record have to be check both in the action and the signal. Record submit is signal as well (accessible by url). use Nette\Application\UI\Form; use Nette\Application\BadRequestException; use Nette\Application\ForbiddenRequestException; class RecordPresenter extends BasePresenter { /** @var object */ private $record; /** * Edit record */ public function actionEdit($id = 0) { // fetch record from database $this->record = $this->model->records->fetch($id); if (!
24/04/2015

Overwriting persistent parameter

Problem Let’s have persistent parameter in presenter with some default value. The value here is NULL. /** @persistent int */ public $page; This parameter is now always transferred between redirects. But we want to disable that for some links. Here is how. Solution Create link and pass that parameters’ default value as an attribute. <a href="{link someAction, page => NULL}">some text</a> <!-- or --> <a n:href="someAction, page => NULL">some text</a>
24/04/2015

Opening files in IDE by one click from Tracy bluescreen

When error page is displayed, you can click on a file name to open relevant file in your editor, of course having having cursor in appropriate line of code. To make all this work, you need to configure your system little bit. If not configured otherwise by re-setting variable Tracy\Debugger::$editor, Debugger will open files using URL having this format: editor://open/?file=%file&line=%line, i.e. using “editor://” protocol. You need to register a handler in your system, which can be any executable file able to process passed URL.
24/04/2015

Nette with grunt

Why this friendship? Because everyone can have only one javascript and one stylesheet file. In era of mobile internet, when every request is expensive, we don’t want header like this (in better example) What we want is that one minified javascript file and one minified file with stylesheets. I have got the best experience with Grunt and his great garden of plugins. I will show you simple example how to automate this task.
24/04/2015

Multiple use of single form

Do you have form in separated class which you want to use on several places? Class form class LoginForm extends \Nette\Application\UI\Form { /** @var \Nette\Security\User */ private $user; /** * @param \Nette\Security\User */ public function __construct(\Nette\Security\User $user) { parent::__construct(); $this->user = $user; $this->addText('username', "Username"); $this->addPassword('password', "Password"); $this->addSubmit('sub', "Login"); $this->onSuccess[] = callback($this, 'process'); } public function process() { $values = $this->getValues(); try { $this->user->login($values->username, $values->password); } catch (\Nette\Security\AuthenticationException $e) { $this->addError($e->getMessage()); } } } Register callback processing the form during initialization.
24/04/2015

Linking between modules

Linking between modules needs to be done using absolute routes. That means with colon at the beginning of the route. Relative link to a module <a href="{link Foo:Default:default}">link</a> <!-- or --> <a n:href="Foo:Default:default">link</a> This will try to search for that route in current module. Absolute link to a module <a href="{link :Foo:Default:default}">link</a> <!-- or --> <a n:href=":Foo:Default:default">link</a> This will search for the Foo module in root.
24/04/2015

How to configure TinyMCE in nette

Usefull links - Official site TinyMCE - Configuration Attach TinyMCE to textarea By class (recommended) $form->addTextArea('text', 'Text') ->setAttribute('class', 'mceEditor'); tinyMCE.init({ mode: "specific_textareas", editor_selector: "mceEditor", ... }); By ID $form->addTextArea('text', 'Text') ->setHtmlId('mceEditor'); tinyMCE.init({ mode: "exact", elements: "mceEditor", ... }); Configuring validation To enable validation in textarea with TinyMCe it is important to save written text to textarea before Nette validation. If a form contains only one button (or if all buttons runs validation) you can attach text saving on onSubmit of the form.
24/04/2015

Different layout in administration

One-presenter administration, AdminPresenter.php Handful for simple and small administration. Directory structure <b>app/ models/ presenters/ AdminPresenter.php BasePresenter.php HomepagePresenter.php templates/ Admin/ default.latte Homepage/ default.latte @layout.latte</b> ← basic layout <b>@layoutAdmin.latte</b> ← admin layout <b>bootstrap.php</b> templates/Admin/default.latte <!-- use adminLayout.latte --> {layout "../@layoutAdmin.latte"} or we can modify structure like this: <b>templates/ Admin/ default.latte @layout.latte Homepage/</b> ... <b>@layout.latte</b> and template Admin/@layout.latte will load for AdminPresenter.php automatically. See automatic layout loader or AdminBasePresenter
24/04/2015

How to paginate

Ready to use extensions: pvy/nette-visual-paginator zeleznypa/jao-nette-visual-paginator iPublikuj/visual-paginator But if you want to just a simple paginator, then use \Nette\Utils\Paginator directly.
07/04/2015

How to use Texy in Latte template as a filter

Install Texy via composer and register it as a service in config.neon. services - Texy Register Texy as a Latte filter in presenter /** * @var \Texy @inject */ public $texy; protected function createTemplate($class = NULL) { $template = parent::createTemplate($class); // $template->addFilter('texy', callback($this->texy, 'process')); // old php $template->addFilter('texy', array($this->texy, 'process')); return $template; } and use it in Latte file {$article->content|noescape|texy}
01/02/2015

How to AJAX response (with JSON)

$this->sendJson($dataObject); or $this->sendResponse(new JsonResponse($myObject)); or use native nette channel $this->payload->data = $data; $this->sendPayload(); or just redraw component if ($this->isAjax()){ $this->redrawControl('datalist'); }
19/11/2014

Error presenter from module

Here is how you change the default error presenter to one you have in module Front. The key is using a colon, just as you would when defining routes. nette: application: errorPresenter: "Front:Error"
08/11/2014

Conditional button in form

Form definition if (!empty($this->article)) { $form->addSubmit('send', 'Edit article'); $form->addSubmit('sendAndView', 'Edit article and view'); } else { $form->addSubmit('send', 'Add article'); } Form processing if ($form['send']->isSubmittedBy()) { // ... } if (isset($form['sendAndView']) && $form['sendAndView']->isSubmittedBy()) { // ... } Form rendering {ifset $form[sendAndView]} {input sendAndView class => "btn btn-secondary"} {/ifset}
05/11/2014

How to setup SimpleAuthenticator

With DI extension in config.neon nette: security: users: john: p4ss admin: P4S5w0rD@! is the same as long version without using DI extension in config.neon services: - Nette\Security\SimpleAuthenticator({john:p4ss, admin: 'P4S5w0rD@!'})
02/11/2014

Static function as a factory

Did you know, you could register a public static function as a factory in Nette configuration? Well, you do now. ;) Just write your factory method: class PdoFactory { public static function create(string $dsn) : \PDO { $name = 'xxx'; $password = 'asd'; return new \PDO($dsn, $name, $password); } } And register it in your config.neon: services: - PdoFactory::create('mysql:host=localhost;dbname=test')
28/10/2014

Service registration in NEON

Passing named service: services: testdb: PDO('mysql:host=localhost;dbname=test', 'test','') # named services - Article(@testdb) # testdb is defined as service with name Verbose style of service registration with assignment to public property $db: services: - factory: App\Presenter\HomepagePresenter setup: - $db( stdClass() ) - factory: App\Presenter\HomepagePresenter setup: - $db(@testdb) # used named service Verbose style of factory registration: services: - implement: ArticleFactory # factory: Article # is defined in @return anotation or as a return type # parameters: [a, b] You can skip defining parameters, which can be acquired by DI compiler via autowire mechanism:
28/10/2014

Overwrite whole section in neon config

If you use exclamation mark after section name in config.local.neon, it will override the whole structure it may have inherited from config.neon. # config.local.neon nette: session!: # <-- there is the exclamation mark autoStart: yes # config.neon nette: session: expiration: 14 days # this will not be in the result
28/10/2014

How to test exception call

Assert::exception(function () { $this->userManager->addUser('user','P455w0rd'); }, 'DuplicateEntryException'); --/
28/10/2014

How to create lazy factory

Register it in config.neon - SeoFactory Create interface with one public method create with annotation (or return type) for our class. interface SeoFactory { /** @return SEO */ public function create(); } class SEO { // ... } The DI container will now create its instance when needed.
28/10/2014

How to create custom validation rulles

Constants (DOMAIN, USER_IN_DOMAIN) contain name of static function, that call for validation. Example “user in domain”, show us, how work with parameters. class DomainsRules { const DOMAIN = 'DomainsRules::validateDomain'; const USER_IN_DOMAIN = 'DomainsRules::validateUserInDomain'; public static function validateDomain(\Nette\Forms\IControl $control) { // dump($control->getValue()); // return TRUE or FALSE } public static function validateUserInDomain(\Nette\Forms\IControl $control, $domain) { // dump($control->getValue()); // return TRUE or FALSE } } $form->addText('domain', 'Domain:') ->addRule(DomainsRules::DOMAIN, 'fill domain in format domain.
28/10/2014

How to change latte syntax for JavaScript

<script n:syntax="double"> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-12345678-12', 'domain.com'); {{if $user->isLoggedIn()}}ga('set', 'dimension1', {{$user->identity->username}});{{/if}} ga('send', 'pageview'); </script>
28/10/2014

Form image and rules

$image = $form->addUpload('image', 'Image:'); $image->addCondition(Form::FILLED) ->addRule(Form::IMAGE, 'Please select image file'); ->addRule(Form::MIME_TYPE, 'Please select jpeg image file', 'image/jpeg') ->addRule(Form::MAX_FILE_SIZE, 'Max allowed file size is 2 MB.', '20000'); // requre on condition option if (!$this->article) { // we need image for main article $image->addConditionOn($form['mainArticle'], Form::EQUAL, TRUE) ->setRequired('Please select image.'); }
28/10/2014

Examples of macro "n:class" usage

use , as a class separator <header n:class="position-default, homepage, $query ? position-top"> <h1>Nette Code Snippets</h1> </header> {* is same as: *} <header class="position-default homepage{if $query!=''} position-top{/if}"> <h1>Nette Code Snippets</h1> </header>
28/10/2014

Dibi fluent insert

don’t forget for execute $dibi->insert('table', $values) ->execute(\Dibi::IDENTIFIER);
28/10/2014

Delete directory recursively

$dirContent = Nette\Utils\Finder::find('*')->from($directory)->childFirst(); foreach ($dirContent as $file) { if ($file->isDir()) @rmdir($file->getPathname()); else @unlink($file->getPathname()); } @unlink($directory);
28/10/2014

Connect Dibi as a service with tracy bar

config.neon parameters: database: host: localhost username: root password: *** database: database_name lazy: TRUE profiler : TRUE services: connection: class: DibiConnection(%database%) or with extension # This will create service named 'dibi.connection'. # Requires Nette Framework 2.2 extensions: dibi: Dibi\Bridges\Nette\DibiExtension22 dibi: host: localhost username: root password: *** database: database_name lazy: TRUE
28/10/2014

Macro loader

This short tutorial will show you, how to create and register custom macros. Built on Nette 2.0.8. Create macro First, we create macroSet and add all our macros. How to register macros to template will be shown later. Note: in Nette 2.1 and older, you have to use Nette\Latte namespace instead of Latte. $set = Latte\Macros\MacroSet::install($latte->compiler); $set->addMacro('id', NULL, NULL, 'if ($_l->tmp = array_filter(%node.array)) echo \' id="\' . %escape(implode(" ", array_unique($_l->tmp))) .
30/01/2013

Dependent form select with AJAX

This is case you want to create form select, which values depend on another select value. Simple use case: select country, than select city from the country, than select street from the city. Requires jQuery 1.8.2 and nette.ajax.js addon, built on Nette 2.0.10 sandbox. Single dependent select Logic is very simle: when first select is changed a handle* method is called by AJAX in background (in this case handleFirstSelect($value)). This method specifies values for second select, loads select input with them and invalidates snippet.
30/01/2013

Setting up defaults to edit form

Setting up defaults in factory component / form is wrong. What’s the right way to do so? class FooPresenter extends BasePresenter { protected function loadItem($id) { $item = $this->getContext()->itemService->find($id); if (!$item) { $this->flashMessage("Item with id $id does not exist", 'error'); $this->redirect('default'); // aka items list } return $item; } protected function createComponentRecordForm() { $form = new Form; $form->addText(...); // ... } public function actionEdit($id) { $item = $this->loadItem($id); $defaults = $this->someMagicHere($item); $this['recordForm']->setDefaults($defaults); } }
30/01/2012

Loading models with NotORM and Dependency Injection

Since Nette 2.0.5 we can load models via Dependency Injection container (in config files ) and in presenter we can use autowire method inject. Pros of DI container approach is transparency. This tutorial uses Nette 2.0.5 a NotORM. Are you looking for dibi or Nette Database (in Czech)? Configuring environment config.neon First we set up required parameters and register database service. Then we can add particular models as services.
30/01/2012

How to pass values to helper / Helper Loader

If you need to pass a helper already defined values or database results, you can do it through anonymous function. Static list $genderTypes = array( 'ma' => 'Male', 'f' => 'Female', ); $this->template->registerHelper('gender', function($gender) use($genderTypes) { if (isset($genderTypes[$gender])) { return $genderTypes[$gender]; } return 'gender unknown'; }); Items list from database $categoryList = $this->models->categories->fetchAll(); $this->template->registerHelper('category', function($categoryId) use($categoryList) { if (isset($categoryList[$categoryId])) { return $categoryList[$categoryId]; } return 'unknown'; }); Helper Loader Another and easily extensible solution is to use your own helper loader.
30/01/2012