Jak se z presenteru dostat k hodnote Configuratoru

markoska
Člen | 12
+
0
-

Predem se omlouvam za zacatecnicky dotaz, ale verte, ze se na nej uz hodinu snazim najit odpoved…

V souboru app/Bootstrap.php mam nastaveno:

$configurator->setDebugMode(true);

Jak se k teto hodnote dostat v presenteru? Potreboval bych jednu akci podminit tim, jestli aplikace bezi ve vyvojovem nebo produkcnim rezimu. Kdyz zkousim dat:

public function renderDefault(): void
{
    $conf = new Nette\Bootstrap\Configurator();
    if ($conf->isDebugMode() === true) {
        $this->template->debug = "jo";
    } else {
        $this->template->debug = "ne";
    }
}

Tak mi to vraci, ze ve vyvojovem rezimu nejsem.

Diky za vasi trpelivost ;)

galab
Backer | 74
+
0
-

Není to teda z configu, ale můžeš si to ověřit u tracy

if(\Tracy\Debugger::$productionMode === true) {
    ...
} else {
    ...
};

Editoval galab (8. 2. 2022 23:00)

Marek Bartoš
Nette Blogger | 1146
+
+2
-

Informace o debug módu je v parametru %debugMode%

V presenteru se k hodnotě dostaneš tak, že si presenter manuálně zaregistruješ do služeb a službě parametr předáš.

services:
	- App\Presenters\ExamplePresenter(%debugMode%)
namespace App\Presenters;

class ExamplePresenter extends \Nette\Application\UI\Presenter
{
	private bool $debugMode;

	public function __construct(bool $debugMode)
	{
		$this->debugMode = $debugMode;
	}

	public function renderDefault(): void
	{
    	if ($this->debugMode) {
        	$this->template->debug = "jo";
    	} else {
        	$this->template->debug = "ne";
    	}
	}
}
David Grudl
Nette Core | 8082
+
+8
-

Úplně nejjednodušší způsob jak si cokoliv předávat z konfigu. Vytvoř si takovouto třídu:

<?php
namespace App;

class Config
{
	public function __construct(...$args)
	{
		\Nette\Utils\Arrays::toObject($args, $this);
	}
}

A teď si do konfigu (např. soubor common.neon) předej jakékoliv data následujícím způsobem:


services:
	- App\Config(
		nazevProjektu: abc
		nejakaHodnota: 123
		debugMode: %debugMode%
	)

V proměnné debugMode bude aktuální stav debug režimu.

No a nakonec v presenteru, kde budu chtít mít přístup k údajům, přidám tímto způsobem proměnnou $config.

final class HomepagePresenter extends Nette\Application\UI\Presenter
{
	/** @inject */
	public \App\Config $config;

Lze ji zapsat i do společného předka, ze kterého ostatní presentery dědí, a pak data budou k dispozici všude.

A data lze číst pomocí $this->config->debugMode.

Pavel980
Člen | 9
+
0
-

@DavidGrudl postupoval jsem presne podle tve rady a ejhle chyba.
Apache/2.4.54 (Win64) OpenSSL/1.1.1p PHP/8.1.10
Nette 3.1
Cannot write to an undeclared property App\Model\Config::$debugMode.

David Grudl napsal(a):

Úplně nejjednodušší způsob jak si cokoliv předávat z konfigu. Vytvoř si takovouto třídu:

<?php
namespace App;

class Config
{
	public function __construct(...$args)
	{
		\Nette\Utils\Arrays::toObject($args, $this);
	}
}

A teď si do konfigu (např. soubor common.neon) předej jakékoliv data následujícím způsobem:


services:
	- App\Config(
		nazevProjektu: abc
		nejakaHodnota: 123
		debugMode: %debugMode%
	)

V proměnné debugMode bude aktuální stav debug režimu.

No a nakonec v presenteru, kde budu chtít mít přístup k údajům, přidám tímto způsobem proměnnou $config.

final class HomepagePresenter extends Nette\Application\UI\Presenter
{
	/** @inject */
	public \App\Config $config;

Lze ji zapsat i do společného předka, ze kterého ostatní presentery dědí, a pak data budou k dispozici všude.

A data lze číst pomocí $this->config->debugMode.

Editoval Pavel980 (21. 10. 2022 0:11)

Šaman
Člen | 2632
+
+3
-

Imho potřebuješ v té třídě Config mít deklarované public property $nazevProjektu, $nejakaHodnota a $debugMode.
Když koukám, co dělá ta fce toObject, tak opravdu jen zcela nemagicky projde pole a každou dvojici klíč a hodnota se předá do toho objektu (v tomto případě $this, tedy naše třída Config).

Obecně tohle dělá:

  • vytvoříš si nějakou třídu (Config), ta ale musí mít nějakou možnost nastavení hodnot (v konstruktoru, nebo settery)
  • nastavíš jí hodnoty v neonu (v této ukázce se předávají do konstruktoru)
  • a vyžádáš si ji pak v presenteru, nebo v jiné třídě pomocí DI
  • a pak s ní pracuješ – ať už čteš její public property (jako v ukázce), nebo lépe třeba přes gettery

Editoval Šaman (22. 10. 2022 0:34)

Pavel980
Člen | 9
+
0
-

@Šaman dekuji za radu doufam, ze jsem te pochopil spravne:
tak pro kazdy klic musim vytvorit setter/getter.

Class Config jsem upravil takto.

<?php
class Config
{
    use Nette\SmartObject;

    /** @var */
    public $debugMode;

    public function __construct(...$args)
    {
        Nette\Utils\Arrays::toObject($args, $this);
    }

    public function getDebugMode(): bool
    {
        return $this->debugMode;
    }

    public function setDebugMode(bool $debugMode): void
    {
        $this->debugMode = $debugMode;
    }
}
?>

Jeste bych se chtel zeptat jak jednoduse si vytahnout parametry z bootstrap.php?
v soucasne dobe mam v BasePresenteru.

<?php
	use Nette\DI\Container;

	class BasePresenters extends Presenter
	{
		/** @var Context */
    	private $context;

		/** @var string*/
		public $nejakyParametr_1;

		public function injectContext(Container $context)
    	{
        	$this->context = $context;
    	}

    	public function getContext(): Container
    	{
        	return $this->context;
    	}

		public function startup()
    	{
        	parent::startup();

			$this->nejakyParametr_1 = $this->getContext()->parameters['parametr_1'];

		}
	}
?>

a v bootstrapu mam

<?php
		$configurator->addDynamicParameters([
            'parametr_1' => $_SERVER['XXXXX'],
            'parametr_2' => $_SERVER['bbbb'],
			'parametr_3' => true, /* true/flase */
        ]);


?>

apod. Predem moc dekuji za rady ;)

Šaman napsal(a):

Imho potřebuješ v té třídě Config mít deklarované public property $nazevProjektu, $nejakaHodnota a $debugMode.
Když koukám, co dělá ta fce toObject, tak opravdu jen zcela nemagicky projde pole a každou dvojici klíč a hodnota se předá do toho objektu (v tomto případě $this, tedy naše třída Config).

Obecně tohle dělá:

  • vytvoříš si nějakou třídu (Config), ta ale musí mít nějakou možnost nastavení hodnot (v konstruktoru, nebo settery)
  • nastavíš jí hodnoty v neonu (v této ukázce se předávají do konstruktoru)
  • a vyžádáš si ji pak v presenteru, nebo v jiné třídě pomocí DI
  • a pak s ní pracuješ – ať už čteš její public property (jako v ukázce), nebo lépe třeba přes gettery
m.brecher
Generous Backer | 717
+
0
-

@markoska

Já také používám $debugMode a do všech presenterů ho dostanu takto, lze použít když presentery dědí z BasePresenter-u:

# common.neon

decorator:
	App\Presenters\BasePresenter:
		setup:
			- $debugMode = %debugMode%

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    public bool $debugMode;

	.....
}

@Pavel980 tento způsob nevyžaduje settry a je asi nejjednodušší

Editoval m.brecher (22. 10. 2022 17:06)

Šaman
Člen | 2632
+
+2
-

Pavel980 napsal(a):

Jeste bych se chtel zeptat jak jednoduse si vytahnout parametry z bootstrap.php?

Tím, že to v bootstrapu předáváš do configu, tak by to mělo jít úplně stejně, jako ten debugMode.

Jinak ten getter/setter: to je jedna z možností. Teoreticky čistější, prakticky asi dostačuje ta public property. Btw. ty getter a setter nepoužíváš, protože jestli používáš ten kód od Davida, tak ten to zapisuje přímo do oné property a ty ji máš public. Getter a setter se většinou vytvářejí k private a protected proměnným. To je ale obecná teorie OOP (zapouzdření).


Rozepíšu tu dvě možnosti, jedna maličko pracnější, ale průhlednější:

Pro každou proměnnou samostatnou třídu

třída:

<?php
declare(strict_types=1);

namespace App\Model\Configuration;


class TempDirProvider
{

	protected string $tempDir;


	public function __construct(string $tempDir)
	{
		$this->tempDir = $tempDir;
	}


	public function get(): string
	{
		return $this->tempDir;
	}

}

config:

services:
	- App\Model\Configuration\TempDirProvider(%tempDir%)

presenter:

use App\Model\Configuration\TempDirProvider;

final class FooPresenter
{
	/** @inject */
	public TempDirProvider $tempDirProvider;

# a pak někde v kódu
$tempDir = $this->tempDirProvider->get();

Výhoda tohoto řešení je, že už od pohledu víš, co vlastně načítáš. Presenter si řekl o třídu, která mu má říct, kde je odkládací adresář. Tu třídu jsi v configu vytvořil a předal jí i onu proměnnou. Presenter o žadném configu, ani contextu nic neví, prostě mu někdo předal třídu. Přesně podle zásad DI. Je to čisté a přehledné.


druhá možnost je mít jednu třídu pro všechny věci ze sekce configu

třída:

<?php

namespace App\Utils;

use Nette\SmartObject;


class Configuration
{
	use SmartObject;

	/** @var array */
	protected $configuration;


	/**
	 * @param array
	 */
	public function __construct($configuration)
	{
		$this->configuration = $configuration;
	}


	/**
	 * @param string
	 * @return mixed
	 */
	public function getParameter($name)
	{
		return $this->configuration[$name];
	}

}

config:

parameters:
	presenterConfiguration:
		tempDir: %tempDir%
		secretOfUniverse: 42

services:
	- App\Utils\Configuration(%presenterConfiguration%)

presenter:

use App\Utils\Configuration;
class FooPresenter
{
	/** @var Configuration @inject */
	public $configuration;

# a pak někde v kódu
$tempDir = $this->configuration->getParameter('tempDir');
$bar = $this->configuration->getParameter('bar'); # vyhodí chybu pro neexistující prvek pole

Výhoda je, že můžeš přidávat do configu nové položky a pak je rovnou v kódu načítat beze změny třidy, která se o to stará. Nevýhoda je, že nikde není uvedené, co všechno ta třída Config, kterou presenter vyžaduje, má obsahovat. Je to méně průhledné a namísto „potřebuji adresu odkládacího adresáře“ si presenter říká „potřebuji všechny parametry které mi náleži v configu a jestli tam nebude vše potřebné, tak možná nebudu fungovat“. Asi chápeš, že pro ladění je to ta horší možnost.


P.S. Obě třídy jsem vykopíroval z funkční aplikace, ale ta druhá je pro nějakou starší verzi PHP a Nette. Uprav si to podle sebe, šlo mi spíš o pochopení že to není žádná magie. Vždy je to stejné – máš třídu pro přenos, v configu ji nakrmíš daty a presenter si ji vyžádá.

Editoval Šaman (23. 10. 2022 1:25)

Pavel980
Člen | 9
+
0
-

Vsem dekuji za pripadne rady…

Šaman napsal(a):

Pavel980 napsal(a):

Jeste bych se chtel zeptat jak jednoduse si vytahnout parametry z bootstrap.php?

Tím, že to v bootstrapu předáváš do configu, tak by to mělo jít úplně stejně, jako ten debugMode.

Jinak ten getter/setter: to je jedna z možností. Teoreticky čistější, prakticky asi dostačuje ta public property. Btw. ty getter a setter nepoužíváš, protože jestli používáš ten kód od Davida, tak ten to zapisuje přímo do oné property a ty ji máš public. Getter a setter se většinou vytvářejí k private a protected proměnným. To je ale obecná teorie OOP (zapouzdření).


Rozepíšu tu dvě možnosti, jedna maličko pracnější, ale průhlednější:

Pro každou proměnnou samostatnou třídu

třída:

<?php
declare(strict_types=1);

namespace App\Model\Configuration;


class TempDirProvider
{

	protected string $tempDir;


	public function __construct(string $tempDir)
	{
		$this->tempDir = $tempDir;
	}


	public function get(): string
	{
		return $this->tempDir;
	}

}

config:

services:
	- App\Model\Configuration\TempDirProvider(%tempDir%)

presenter:

use App\Model\Configuration\TempDirProvider;

final class FooPresenter
{
	/** @inject */
	public TempDirProvider $tempDirProvider;

# a pak někde v kódu
$tempDir = $this->tempDirProvider->get();

Výhoda tohoto řešení je, že už od pohledu víš, co vlastně načítáš. Presenter si řekl o třídu, která mu má říct, kde je odkládací adresář. Tu třídu jsi v configu vytvořil a předal jí i onu proměnnou. Presenter o žadném configu, ani contextu nic neví, prostě mu někdo předal třídu. Přesně podle zásad DI. Je to čisté a přehledné.


druhá možnost je mít jednu třídu pro všechny věci ze sekce configu

třída:

<?php

namespace App\Utils;

use Nette\SmartObject;


class Configuration
{
	use SmartObject;

	/** @var array */
	protected $configuration;


	/**
	 * @param array
	 */
	public function __construct($configuration)
	{
		$this->configuration = $configuration;
	}


	/**
	 * @param string
	 * @return mixed
	 */
	public function getParameter($name)
	{
		return $this->configuration[$name];
	}

}

config:

parameters:
	presenterConfiguration:
		tempDir: %tempDir%
		secretOfUniverse: 42

services:
	- App\Utils\Configuration(%presenterConfiguration%)

presenter:

use App\Utils\Configuration;
class FooPresenter
{
	/** @var Configuration @inject */
	public $configuration;

# a pak někde v kódu
$tempDir = $this->configuration->getParameter('tempDir');
$bar = $this->configuration->getParameter('bar'); # vyhodí chybu pro neexistující prvek pole

Výhoda je, že můžeš přidávat do configu nové položky a pak je rovnou v kódu načítat beze změny třidy, která se o to stará. Nevýhoda je, že nikde není uvedené, co všechno ta třída Config, kterou presenter vyžaduje, má obsahovat. Je to méně průhledné a namísto „potřebuji adresu odkládacího adresáře“ si presenter říká „potřebuji všechny parametry které mi náleži v configu a jestli tam nebude vše potřebné, tak možná nebudu fungovat“. Asi chápeš, že pro ladění je to ta horší možnost.


P.S. Obě třídy jsem vykopíroval z funkční aplikace, ale ta druhá je pro nějakou starší verzi PHP a Nette. Uprav si to podle sebe, šlo mi spíš o pochopení že to není žádná magie. Vždy je to stejné – máš třídu pro přenos, v configu ji nakrmíš daty a presenter si ji vyžádá.

whatever122
Člen | 6
+
-11
-

No nebo nejjednodušší bude, když použiješ $GLOBALS[‚configurator‘] a nemusíš nic nastavovat v 10 jiných souborech a fungovat to bude úplně stejně.

Editoval whatever122 (9. 12. 2022 1:13)

Marek Bartoš
Nette Blogger | 1146
+
+5
-

@whatever122 Nejjednodušší do okamžiku, než proměnnou na jednom místě přejmenuješ a nic ti neřekne, že jsi nějaké místo vynechal. Nebo dokud nezačneš psát automatizované testy a nemusíš zjišťovat, které globální proměnné je třeba nastavit, abys kód dostal do správného stavu. Nebo dokud nechceš analyzovat typovou správnost kódu přes phpstan/psalm. Je to velmi neprofesionální a náchylné na chyby. Kód nepíšeme komplikovaný proto že by nás to bavilo, ale proto, že se tak při zdánlivě nesouvisející změně nesesype.

David Grudl
Nette Core | 8082
+
0
-

Odpověď je také v dokumentaci, jak se v presenteru dostat k hodnotě z configu