/ Gists / Nette

Gists - Nette

On gists

Vlastní validační pravidlo na prvek

Nette Nette-Forms

rule.php #

<?php

$form->addEmail('iEmail')
	->addRule(function($control) {
		return $this->user->checkEmail($control->value)
});

On gists

Nette 2.4 - custom filters - class

Nette Nette-Latte

SotioFilters.php #

<?php

namespace App\FrontModule\Filters;

use Nette;
use Andweb;

class SotioFilter
{

	/**
	 * Andweb\Localization\ITranslator
	 */
	private $translator;

	public function __construct(Andweb\Localization\ITranslator $translator)
	{
		$this->translator = $translator;
	}


	public function register($template)
    {
        $template->addFilter(null, [$this, 'loader']);
    }


	public function loader($filter)
	{
		if (method_exists(__CLASS__, $filter)) 
		{
            $args = func_get_args();
            array_shift($args);
            return call_user_func_array(array(__CLASS__, $filter), $args);
		}

		return NULL;
	}


	public function readMore($s)
	{
		$s = preg_replace('~(<div>--START--</div>)(.+)<div>--END--</div>~Usi', 
			'<div class="read-more-wrapper">$2</div><div class="read-more-btn-wrapper"><a href="">'.$this->translator->translate('READ MORE').'</a></div>',
			 $s
		);

		return $s;
	}


}

On gists

Abort exception

Nette Nette-Tricks

abort.php #

<?php


try
		{
		  // ...
			$this->redirect('PresenterWhatEver:default');

		}
		catch (\Exception $e)
		{
			// posle se to dal kdyz premserujeme - redirect vyhazuje abortException ...
			if ($e instanceof Nette\Application\AbortException)
			{
				throw $e;
			}
		}

On gists

Store/Restore Request

Nette Nette-Tricks

Presenter.php #

<?php

	public function actionDefault($back = null) 
	{
		

		if ($back)
			$this->restoreRequest($back);

		if ($this->presenter->navigation->navItem['alias'] == 'allOpenCalls')
		{
			
			$parentId = $this->presenter->navigation->navItem['parent__navigation_id'];
			$nav = $this->navigation->getById($parentId);
			$children = $nav->getChildren();

			$this->config['navigation_id'] = array_keys($children);

		}


		$this->template->back = $this->storeRequest();

	}

On gists

Konfiguracni objekty - neon - Nette

Nette Nette-Neon

article.md #

Konfigurační objekty v Nette

Tomáš Jacík   30. 1. 2017

Jak se poprat s předáním konfigurace službě z config.neon? A jak k tomu využít Nette DI?

Jak se to běžně dělává?

Mějme hypotetickou třídu InvoiceForm. Formulář má vlastní šablonu, nemůžeme tedy napsat pouhou továrnu, potřebujeme komponentu. A k ní továrnu. Navíc ale chceme formuláři předat z config.neon nějaké výchozí hodnoty. Jak by takový kód vypadal?

Formulář

declare(strict_types = 1);

namespace App\Forms;

use Nette\Application\UI\Control;
use Nette\Application\UI\Form;

final class InvoiceForm extends Control
{
    /**
     * @var array
     */
    private $config;

    public function __construct(array $config)
    {
        $this->config = $config;
    }

    protected function createComponentInvoiceForm(): Form
    {
        $form = new Form;

        $form->addText('maturity', 'Splatnost')
            ->setDefaultValue($this->config['defaultMaturity']);

        return $form;
    }

    public function render()
    {
        $this->getTemplate()->render(__DIR__ . '/InvoiceForm.latte');
    }
}

Továrna

declare(strict_types = 1);

namespace App\Forms;

final class InvoiceFormFactory
{
    /**
     * @var array
     */
    private $config;

    public function __construct(array $config)
    {
        $this->config = $config;
    }

    public function create(): InvoiceForm
    {
        return new InvoiceForm($this->config);
    }
}

Šablona

<form n:name="invoiceForm">
    {* naše vlastní vykreslení formuláře *}
</form>

V config.neon pak musíme továrnu formuláře zaregistrovat a předat jí potřebné parametry.

parameters:
    invoicing:
        defaultMaturity: 7
        pdfDirectory: %appDir%/../invoices

services:
    - App\Forms\InvoiceFormFactory( %invoicing% )

Jak to udělat lépe?

Místo běžného pole si na konfiguraci vytvoříme objekt, který bude konfiguraci našemu formuláři zprostředkovávat.

declare(strict_types = 1);

namespace App\Config;

use Nette\Utils\ArrayHash;

abstract class AbstractConfig extends ArrayHash
{
    public function __construct(array $arr)
    {
        foreach ($arr as $key => $value) {
            if (is_array($value)) {
                $this->$key = ArrayHash::from($value, TRUE);
            } else {
                $this->$key = $value;
            }
        }
    }
}

Z tohoto objektu pak podědíme konfiguraci pro náš formulář. Můžeme také do třídy přidat metody, které budou s naší konfigurací pracovat. Např. metodu getPdfPath(), kterou později využijeme v třídě na generování PDF. Konfigurační třída tedy není jen jednorázová, předáme její pomocí fakturační konfiguraci více službám.

declare(strict_types = 1);

namespace App\Config;

/**
 * @property int    $defaultMaturity
 * @property string $pdfDirectory
 */
final class InvoicingConfig extends AbstractConfig
{
    public function getPdfPath(int $invoiceId): string
    {
        return vsprintf('%s/%s.pdf', [$this->pdfDirectory, $invoiceId]);
    }
}

Nyní můžeme třídu InvoiceForm upravit, aby nového konfiguračního objektu využila. Všimněte si také metody setDefaultValue(). Díky ArrayHash nyní můžeme ke konfiguraci přistupovat jako k objektu.

declare(strict_types = 1);

namespace App\Forms;

use App\Config\InvoicingConfig;
use Nette\Application\UI\Control;
use Nette\Application\UI\Form;

final class InvoiceFormNew extends Control
{
    /**
     * @var InvoicingConfig
     */
    private $config;

    public function __construct(InvoicingConfig $config)
    {
        $this->config = $config;
    }

    protected function createComponentInvoiceForm(): Form
    {
        $form = new Form;

        $form->addText('maturity', 'Splatnost')
            ->setDefaultValue($this->config->defaultMaturity);

        return $form;
    }

    public function render()
    {
        $this->getTemplate()->render(__DIR__ . '/InvoiceForm.latte');
    }
}

Můžeme se také zbavit vlastní implementace továrny a nahradit ji interfacem. Nette nám pak továrnu vygeneruje samo.

declare(strict_types = 1);

namespace App\Forms;

interface InvoiceFormFactoryInterface
{
    public function create(): InvoiceForm;
}

Nakonec vše zaregistrujeme v config.neon. Všimněte si také absence sekce parameters.

services:
    - App\Config\InvoicingConfig({
        defaultMaturity: 7
        pdfDirectory: %appDir%/../invoices
    })
    - App\Forms\InvoiceFormFactoryInterface

Co na závěr? Tohle jistě není definitivní řešení. Dalo by se lecjak rozšířit. Např. dodělat validace hodnot vstupujících do konfiguračních objektů. Tím by však byl článek již moc složitý. Jeho pointou je ukázat alternativní přístup předávání konfigurace službám. A jak to ve svých aplikacích děláte vy?


On gists

Přihlášení backend usera do frontu a obráceně

Nette Nette-Tricks

backend-frontend-user.php #

<?php

// https://forum.nette.org/cs/34290-prihlaseni-admina-jako-uzivatel
// bez přepisování $user proměné 
public function handleKlientLogin($hash)
{
	overim hash a nactu data klienta

	$user = $this->getUser();
	$user->getStorage()->setNamespace('frontend');
	$user->login(new Identity($user->id, $user->role, ['username' => $user->username]));
	$this->redirect(....);
}

On gists

ProductDeliveryDateInfo component

Nette Nette-Controls AW

ProductDeliveryDateInfo.php #

<?php

namespace FrontModule\Components;

use Nette,
	Andweb,
	Model;

class ProductDeliveryDateInfo extends Andweb\Application\UI\FrontControl 
{


	const HOUR = 13;



	protected $items = array(
				
			'Praha' => array(
			
				'Osobní odběr Praha'              => 0,
				'Expresní večerní doručení Praha' => 0,
			
			),
			
			'Česká republika' => array(
			
				'Česká pošta'    => 1,
				'Zásilkovna'     => 1,
				'Kurýrní služba' => 1,
			),			
			
			
			'Slovensko' => array(
			
				'Česká pošta'    => 2,
				'Zásilkovna'     => 2,
				'Kurýrní služba' => 2,
			),

	);


	public function __construct() 
	{

	}


	public function renderDefault() 
	{

		$template        = $this->template;
		$template->items = $this->items;

		// Fucking php5.3 on production
		$that = $this;

		$template->registerHelper('formatDate', function($s) use ($that) {
			return $that->formatDate($s);
		});
		
	}	



	public function renderNotAvailable() 
	{
		$template   = $this->template;
		$this->view = 'default';

		$template->notAvailable = TRUE;
		
		$template->items = $this->items;

		// Fucking php5.3 on production
		$that = $this;

		$template->registerHelper('formatDate', function($s) use ($that) {
			return $that->formatDate($s);
		});


		$this->render();
		
	}



	public function formatDate($s)
	{
		$today = new \DateTime();
		$tomorrow = clone $today;
		$tomorrow->modify('+ 1 day');

		if ($today->format('j.n.Y') == $s)
			return $this->presenter->translator->translate('dnes');			

		if ($tomorrow->format('j.n.Y') == $s)
			return $this->presenter->translator->translate( 'zítra');

		return $s;
	}


	// ukaze na velikonocni nedeli, napric vsemi casovymi pasmy, jinak funkce easter_day se chova obcas divne, viz php.net
	public function getEasterDateTime($year) 
	{
		$base = new \DateTime("$year-03-21");
		$days = easter_days($year);
	    return $base->add(new \DateInterval("P{$days}D"));
	}


	public function isNotHoliday(\Datetime $date)
	{
		// statni svatky
		$holidays = array('01-01', '05-01', '05-08', '07-05', '07-06', '09-28', '10-28', '11-17', '12-24', '12-25', '12-26');
		
		// velikonocni pondeli
		$holidays[] = $this->getEasterDateTime(date('Y'))->modify('+1day')->format('m-d');
		
		// velky patek, (pred velikonocnim pondelim)
		$holidays[] = $this->getEasterDateTime(date('Y'))->modify('-2day')->format('m-d');
		
		$day        = $date->format('w');

		if ($day == 0 || $day == 6) 
			return FALSE;

		if (in_array($date->format('m-d'), $holidays)) 
			return FALSE;

		return TRUE;
	}


	function getDeliveryDate($actualDate, $dayDelay = 0)
	{
		$actualDate = new \DateTime($actualDate);
		$actualDate->modify("+$dayDelay day");

		while (!$this->isNotHoliday($actualDate))
		{	
			$actualDate->modify('+1 day');
		}

		return $actualDate;
	}


	public function formatter($dayDelay)
	{
		// 13h a vic + 1 day
		if (date('H') >= self::HOUR)
			$dayDelay += 1;

		return $this->getDeliveryDate(date('Y-m-d'), $dayDelay);
	}





}

On gists

SQL array - NDB - args

Nette MySql

args.php #

<?php
// DIBI
$q = []
array_push('SELECT * FROM %n', $table);
array_push('WHERE id > %d', $id);

$res = dibi::fetchAll($q);


// NDB
$q = $args = [];
$q[] = "SELECT * FROM ?";
$args[] = $table;

$q[] = "WHERE id > ?";
$args[] = $id;

$res = $this->db->fetchAll(implode(' ', $q), ...$args);



On gists

Formulář jako komponenta s vlastní šablonou / Form as a component with its own template

Nette

FooForm.latte #

{form myForm}
	<div class="form-group">
	  {label book class => 'control-label'/}
		{input book class => form-control}
	</div>
	<div class="form-group">
		{input send class => 'btn btn-primary'}
	</div>
{/form}

On gists

Nette toggle - JS

Nette JavaScript

toggle.js #

// @link: https://forum.nette.org/cs/32804-vyvolani-toggle-pri-zavreni-bootstrap-datepicker

$(':text[data-provide="datepicker"]').datepicker({
	language: 'cs'
})
.on('hide', function(e) {
	// `e` here contains the extra attributes
	var defaultValue = e.currentTarget.defaultValue;
	var value = e.currentTarget.value;

	if(defaultValue == value) {
		Nette.toggle('submit-container', false);
	} else {
		Nette.toggle('submit-container', true);
	}
});