basepresenter nejde mi načíst proměnná z modelu

Allconius
Člen | 313
+
0
-

Ahoj vytvořil jsem si Basepresenter:

declare(strict_types=1);


namespace App\Presenters;

use Nette;
use App\Model\DbManager;
use Tracy\Debugger;



/**
 * Base presenter for all application presenters.
 */
abstract class BasePresenter extends Nette\Application\UI\Presenter
{

    /** @var DbManager */
    private $dbManager;


    public function __construct(DbManager $dbManager)
    {
        $this->dbManager = $dbManager;

    }

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

        echo "apl=".$this->dbManager->aplikace;
        exit;

    }

}

ale ta proměnná „aplikace“ se mi z modelu App\Model\DbManager nenačte. Píše to pořád „Trying to get property ‚aplikace‘ of non-object“. Přitom v ostatních presenterech to normálně funguje.. co tam mám špatně ? Není problém v tom, že ten model mám znova v konstruktoru Homepage presenteru?:

declare(strict_types=1);

namespace App\Presenters;

use Nette;
use App\Model\DbManager;
use Nette\Utils\Callback;
use IPub\VisualPaginator\Components as VisualPaginator;
use Nette\Application\UI\Form;
use Nette\Utils\Html;
use Tracy\Debugger;


class HomepagePresenter extends BasePresenter
{

    /** @var DbManager */
    private $dbManager;


    private $oscislo;
    private $prijmeni;
    private $utvar;
    private $szobraz;
    private $list;


    public function __construct(DbManager $dbManager)
    {
        $this->dbManager = $dbManager;
        $this->oscislo = ''; //nastav vychozi
        if (isset($_SESSION['oscislo'])) $this->oscislo = $_SESSION['oscislo'];
        $this->prijmeni = ''; //nastav vychozi
        if (isset($_SESSION['prijmeni'])) $this->prijmeni = $_SESSION['prijmeni'];
        $this->utvar = 0; //nastav vychozi
        if (isset($_SESSION['utvar'])) $this->utvar = $_SESSION['utvar'];
        $this->szobraz = 20;
        $this->list = 0;

    }

Marek Bartoš
Nette Blogger | 1155
+
0
-

Definuj property jen v BasePresenter, změň ji na protected a volej v HomepagePresenter parent::__construct(). Teď máš dvě privátní property stejného názvu a do property BasePresenteru nepředáváš nic.

Milo
Nette Core | 1283
+
0
-

V konstruktoru HomepagePresenteru ti chybí volání parent::_construct().

Allconius
Člen | 313
+
0
-

Marek Bartoš napsal(a):

Definuj property jen v BasePresenter, změň ji na protected a volej v HomepagePresenter parent::__construct(). Teď máš dvě privátní property stejného názvu a do property BasePresenteru nepředáváš nic.

to protected myslíš takto ?

abstract class BasePresenter extends Nette\Application\UI\Presenter
{

    /** @var DbManager */
    protected $dbManager;


    public function __construct(DbManager $dbManager)
    {
        $this->dbManager = $dbManager;




    }

a homepagepresenter:


class HomepagePresenter extends BasePresenter
{

    private $oscislo;
    private $prijmeni;
    private $utvar;
    private $szobraz;
    private $list;


    public function __construct()
    {



        $this->oscislo = ''; //nastav vychozi
        if (isset($_SESSION['oscislo'])) $this->oscislo = $_SESSION['oscislo'];
        $this->prijmeni = ''; //nastav vychozi
        if (isset($_SESSION['prijmeni'])) $this->prijmeni = $_SESSION['prijmeni'];
        $this->utvar = 0; //nastav vychozi
        if (isset($_SESSION['utvar'])) $this->utvar = $_SESSION['utvar'];
        $this->szobraz = 20;
        $this->list = 0;
        parent::__construct();


    }

tohle mi teď hlásí že mám málo argumentů v konstruktoru basepresenteru: „Too few arguments to function App\Presenters\BasePresenter::__construct(), 0 passed in /data/docs/www/html/po/foto/app/Presenters/HomepagePresenter.php on line 39 and exactly 1 expected search►“

Allconius
Člen | 313
+
0
-

tak nakonec mi to funguje takto:
BASEPRESENTER

abstract class BasePresenter extends Nette\Application\UI\Presenter
{

    /** @var DbManager */
    protected $dbManager;


    public function __construct(DbManager $dbManager)
    {
        $this->dbManager = $dbManager;
        parent::__construct($dbManager);



    }

HOMEPAGEPRESENTER

class HomepagePresenter extends BasePresenter
{

    /** @var DbManager */
    protected $dbManager;

    private $oscislo;
    private $prijmeni;
    private $utvar;
    private $szobraz;
    private $list;


    public function __construct(DbManager $dbManager)
    {


        $this->dbManager = $dbManager;
        $this->oscislo = ''; //nastav vychozi
        if (isset($_SESSION['oscislo'])) $this->oscislo = $_SESSION['oscislo'];
        $this->prijmeni = ''; //nastav vychozi
        if (isset($_SESSION['prijmeni'])) $this->prijmeni = $_SESSION['prijmeni'];
        $this->utvar = 0; //nastav vychozi
        if (isset($_SESSION['utvar'])) $this->utvar = $_SESSION['utvar'];
        $this->szobraz = 20;
        $this->list = 0;



    }
Marek Bartoš
Nette Blogger | 1155
+
0
-

Nevím co jsi tam prováděl, ale jsem si jistý, že parent::__construct($dbManager); bys měl volat v HomepagePresenter :D Voláš tím konstruktor BasePresenter

Editoval Marek Bartoš (4. 10. 2021 18:53)

SankaCoffee
Člen | 8
+
0
-

Díky tomu, že to v HomepagePresenter plníš ručně znovu, čímž vlastně BasePresenter postrádá smysl.

Udělej to následovně:

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    /** @var DbManager */
    protected $dbManager;

    public function __construct(DbManager $dbManager)
    {
        $this->dbManager = $dbManager;
    }
class HomepagePresenter extends BasePresenter
{

    // definice properties bez $dbManager; - ten už je v BasePresenter


    public function __construct(DbManager $dbManager)
    {
		parent::__construct($dbManager);

		// Definice unikatni pouze pro tento presenter.
    }

nebo druhá možnost za použití inject. Tím se vyhneš volání parent::__construct($dbManager);

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    /**
	 * @inject
	 * @var DbManager
	 */
    public $dbManager;

    public function __construct(DbManager $dbManager)
    {
        $this->dbManager = $dbManager;
    }

$dbManager musí být v tomto případě public.

class HomepagePresenter extends BasePresenter
{

    // definice properties bez $dbManager; - ten už je v BasePresenter


    public function __construct(DbManager $dbManager)
    {
		// parent::__construct($dbManager); - neni zde potreba

		// Tvůj kód unikatni pouze pro tento presenter.
    }

Alternativně jde injectovat pomocí metody.

public function injectBase(DbManager $dbManager)
{
	$this->dbManager = $dbManager;
}

Metodu můžeš pojmenovat pouze inject, ale osobně v base presenteru používám injectBase, abych si jí při vícenásobném dědění pak někde omylem nepřepsal.

m.brecher
Generous Backer | 724
+
0
-

Volání parent::__construct(); by se mělo použít v poděděném presenteru jako první řádek v konstruktoru. Property definovat v BasePresenteru jako protected, aby ji mohly poděděné presentery použít (private nemohou). Navíc je doporučováno v nadřízeném presenteru jako je BasePresenter nepoužívat předávání závislostí konstruktorem, ale jinou metodou, ideální je metoda inject():

class BasePresenter extends .....
{
   protected $property;

   public function inject(Class $property): void
   {
      $this->property = $property;
   }
}

V poděděných presenterech lze pak $property použít jako $this->property a v konstruktoru není potřeba psát parent::__construct(). Takto to doporučuje @DavidGrudl v dokumentaci o závislostech ;)

Co v dokumentaci není, ale otestoval jsem že funguje také je, že metoda pro předání závislostí ->inject() nemusí vůbec obsahovat v názvu ještě název závislosti (injectSomeClass() – takto to je všude v dokumentaci), ale stačí v pohodě inject(). Pokud by Jsi někdy chtěl nacpat v BasePresenteru větší množství závislostí, nepiš pro každou závislost samostatnou metodu inject(), ale jednou metodou inject() předej všechny:


protected $articleRepository;
protected $sortingManager;

public function inject(
   App\Model\ArticleRepository $articleRepository,
   App\Model\SortingManager $sortingManager,
        .....
)
{
   $this->articleRepository = $articleRepository;
   $this->sortingManager = $sortingManager;
}

Editoval m.brecher (5. 10. 2021 0:49)

Polki
Člen | 553
+
0
-

@mbrecher

m.brecher napsal(a):

Volání parent::__construct(); by se mělo použít v poděděném presenteru jako první řádek v konstruktoru. Property definovat v BasePresenteru jako protected, aby ji mohly poděděné presentery použít (private nemohou). Navíc je doporučováno v nadřízeném presenteru jako je BasePresenter nepoužívat předávání závislostí konstruktorem, ale jinou metodou, ideální je metoda inject():

Není třeba to volat jako první řádek. Volání parent konstruktoru můžeš volat kdekoliv uvnitř.
Stejné je to u dědění jakékoliv jiné metody třídy.

Příklad:

class Block extends Rectangle
{

    public function __construct(
        int $width,
        int $height,
        protected int $depth,
    ) {
        if ($depth < 0) {
            throw new InvalidArgumentException('Message', code);
        }
        parent::__construct($width, $height); // Očekávám, že v parent konstruktoru může být nějaká složitá logika na delší výpočet například, proto volám parent konstruktor až po případném vyhozením své výjimky. Pokud bych to dělal naopak, tak bych zbytečně prováděl kód v parentovi, který by se pak zahodil. Takto šetřím zdroje.
    }

    public function getContents(): int
    {
        return $this->depth * parent::getContents(); // Dokonce můžu použít volání parenta i ve výrazu. Chová se to stejně, jako volání jakékoliv jiné funkce...
    }
}

Co v dokumentaci není, ale otestoval jsem že funguje také je, že metoda pro předání závislostí ->inject() nemusí vůbec obsahovat v názvu ještě název závislosti (injectSomeClass() – takto to je všude v dokumentaci), ale stačí v pohodě inject(). Pokud by Jsi někdy chtěl nacpat v BasePresenteru větší množství závislostí, nepiš pro každou závislost samostatnou metodu inject(), ale jednou metodou inject() předej všechny:

Problém ti nastane, když budeš potřebovat více úrovní dědičnosti, nebo zahrneš kvůli čistotě kódu do projektu traity. Například budeš chtít jednu komponentu vykreslit na vícero stránkách (ve více presenterech), ale ne na všech. V takovém případě nepřipadá v úvahu cpát to do BasePredenteru, protože by sis to načítal vždy a tedy by jsi porušil princip dědičnosti.

Když si na to vytvoříš mezi abstract presenter, ve kterém ta komponenta bude přístupná a z něj budou dědit všechny presentery, které tuto komponentu mají používat, tak se stane to, že už nemůžeš použít jen čistě název ‚inject‘, protože parametry se budou bít s parent injectem a dosáhneš úplně stejného problému jako je ConstructorHell akorát s inject metodou…

Tak samo když použiješ místo mezipresenteru traitu (čistější), tak stejně budeš mít problém, že pokud budou tyto všechny vytvořené traity mít jednoduše metodu nazvanou ‚inject‘, tak v případě, že některý presenter bude potřebovat více než 1 takovou traitu, tak jsi zase v loji kvůli stejnému názvu metody, ale jiným parametrům…

Proto i když se toto jeví jako dobrý nápad, tak nejlepší je mít v každém presenteru/traitě metodu inject s unikátním názvem, který reprezentuje, co daný inject má dělat…

Příklady:
‚injectUserRepository‘
‚injectUserLogInFormFactory‘
‚injectBasePresenterDependencies‘
‚injectContactFormTraitDependencies‘

Myslím, že každý z těchto 4 příkladů jasně říká, co daná metoda inject dělá a například metody 3 a 4 injectují všechny závislosti naráz a názvy jsou unikátní víceméně.

Btw. Dokonce metodu inject můžeš použít i bez parametrů a tedy tou nic neinjectovat, ale třeba něco nastavovat nebo tak. Hodí se, pokud potřebuješ dělat nějaké nastavení proměnných uvnitř trait, kde nemůžeš zase použít například startup metody, jelikož by se nevědělo, kterou startup ze které traity využít…

Editoval Polki (5. 10. 2021 2:02)

m.brecher
Generous Backer | 724
+
-1
-

@Polki

Není třeba to volat jako první řádek. Volání parent konstruktoru můžeš volat kdekoliv uvnitř.
Stejné je to u dědění jakékoliv jiné metody třídy.

Ano, sice to jde, ale konkrétně v presenterech Nette by se toto nemělo dělat. V Nette presenterech metody __construct(), startup() a beforeRender() jsou volané v životním cyklu presenteru a mají speciální význam. Ideální je použít je tak, že se ve final presenterech tyto metody v nepřepisují, ale vždy se nejprve zavolá parent::__construct(), apod.. a až pak kód final presenteru. Pak je v hierarchii presenterů pořádek – asi jen vyjímečně to dělat jinak, raději vůbec ne.

parent::__construct($width, $height); // Očekávám, že v parent konstruktoru může být nějaká složitá logika na delší výpočet například, proto volám parent konstruktor až po případném vyhozením své výjimky. Pokud bych to dělal naopak, tak bych zbytečně prováděl kód v parentovi, který by se pak zahodil. Takto šetřím zdroje.

Příklad s class Block extends Rectangle je příklad OOP obecně, ale presenter v MVC Nette je výrazně jednoúčelová třída navržená pro řízení ostatních objektů. Ideální je, když sám nedělá nic a vše na někoho deleguje. V moderním frameworku Nette je zabudován koncept DI, a je to skoro POVINNOST použít konstruktor presenterů pro předávání závislostí. Vše ostatní řešit ve startupu. Parent presentery by neměly použít pro předání závislostí konstruktor, ale inject() – důvodem je ponechat konstruktor potomků přehledný. Proto tvrdím – nikdy nepoužívej v abstract presenterech __construct(), ale inject(), vyhneš se parent::__construct() peklu.

Když si na to vytvoříš mezi abstract presenter, ve kterém ta komponenta bude přístupná a z něj budou dědit všechny presentery, které tuto komponentu mají používat, tak se stane to, že už nemůžeš použít jen čistě název ‚inject‘, protože parametry se budou bít s parent injectem a dosáhneš úplně stejného problému jako je ConstructorHell akorát s inject metodou…

Ano, toto je správná poznámka, vedle ConstructHell existuje i InjectHell

Proto i když se toto jeví jako dobrý nápad, tak nejlepší je mít v každém presenteru/traitě metodu inject s unikátním názvem, který reprezentuje, co daný inject má dělat…

Příklady:
‚injectUserRepository‘
‚injectUserLogInFormFactory‘
‚injectBasePresenterDependencies‘
‚injectContactFormTraitDependencies‘

Myslím, že každý z těchto 4 příkladů jasně říká, co daná metoda inject dělá a například metody 3 a 4 injectují všechny závislosti naráz a názvy jsou unikátní víceméně.

Jistě, je to určitě jasné a přehledné mít to takhle udělané, ale porovnejme tyto dva kódy:

ukecanější:

public function injectUserRepository(App\Model\UserRepository $userRepository)
{
   $this->userRepository = $userRepository;
}
public function injectUserLogInFormFactory(App\Form\LogInFormFactory $logInFormFactory)
{
   $this->logInFormFactory = $logInFormFactory;
}

nebo stručnější:

public function inject(
   App\Model\UserRepository $userRepository,
   App\Form\LogInFormFactory $logInFormFactory,
)
{
   $this->userRepository = $userRepository;
   $this->logInFormFactory = $logInFormFactory;
}

Jasně, oba dva jsou celkem přehledné, ale ten druhý je hodně podobný tomu jak se to předává konstruktorem, je stručnější a když je závislostí hodně tak i podstatně přehlednější.

Třeba se dočkáme i toho, že někoho z PHP tvůrců osvítí „duch svatý“ a zavedou obdobu construktor property promotion i pro nějakou jednu další speciální metodu, třeba mě napadá:

public function __inject(
   protected App\Model\UserRepository $userRepository,
   protected App\Form\LogInFormFactory $logInFormFactory,
)
{}

Editoval m.brecher (5. 10. 2021 10:41)

Polki
Člen | 553
+
+1
-

@mbrecher
Nejsem si po tvé odpovědi úplně jistý, jestli rozumíš Nette, případně programování obecně. Z tvých příspěvků je vidět, že sis přečetl dokumentaci a jsi schopen ji odcitovat když je třeba, ale nejsem si jistý, jestli tomu opravdu rozumíš a ani nevím, jestli jsi správně pochopil co jsem psal.

Nette sice má životní cykly presenterů dokonce celá aplikace má nějaký životní cyklus. Všechno ho má :D To ale neznamená, že je předepsáno, v jakém pořadí máš volat něco uvnitř funkcí.
V dnešním světě je programování zatím stále závislé na sekvenčnosti. Tomu se podřizuje i životní cyklus presenteru. Ten ale jen říká, v jakém pořadí se volají dané metody. Ne v jakém pořadí se mají volat věci uvnitř.

Podívej se například na následující kód:

class BasePresenter extends Presenter
{
	#[Inject] public MailManager $mailManager;

    public function startup()
    {
		if (!$this->getUser()->isAllowed('Frontend', 'view')) {
			Debugger::log(/* data uživatele jako IP adresa apod */);
			$this->mailManager->tryToHackNotification(); // Metoda na ukázku, že se tu může dít něco zdlouhavého, co nechceme volat vždy
			$this->flashMessage('Pro přístup nemáte dostatečná oprávnění. Prosím přihlašte se.', 'warning');
			$this->redirect('Sign:in', $this->storeRequest());
        }
		// jakákoliv další logika
        parent::startup(); // tady je úplně jedno, kde parent::startup je, jelikož startup Presenteru jen nastavuje proměnnou
    }
}
class HomepagePresenter extends BasePresenter
{
    public function startup()
    {
		$httpRequest = $this->getHttpRequest();
		if (!$httpRequest->isMethod($httpRequest::GET)) {
			$httpResponse =  $this->getHttpResponse();
			$this->error('This method is not allowed here. Please use ' . $httpRequest::GET, $httpResponse::S405_METHOD_NOT_ALLOWED);
  		}
        parent::startup(); // tady MUSÍME volat startup od rodiče až na konci, protože když bychom jej volali před ifem, tak se bude zbytečně vykonávat ifování v parentovi a případně další složitý kód, nebo odesílání E-mailů, které se vůbec odesílat nemusí, když je špatná metoda.
    }
}

Z výše uvedeného vyplývá, že životní cyklus Presenteru nemá absolutně žádný vliv na to, kde voláme parent metody. Důležité je mít obsah těch metod napsán tak, aby on byl v rámci sekvenčnosti v pořádku. No a s pořádkem v presenterech to podle mě nemá co dělat, ale to už je věc vkusu.

V moderním frameworku Nette je zabudován koncept DI, a je to skoro POVINNOST použít konstruktor presenterů pro předávání závislostí. Vše ostatní řešit ve startupu.

Pokud vím, tak DI je v Nette snad už od začátku a ne v moderním Nette. Ohledně řešení všeho ostatní ve startupu nesouhlasím. Jsou věci, které je ok řešit až v renderu, nebo v jiných metodách jako například handle apod. Tady si protiřečíš s tvým výrokem, že existuje životní cyklus presenteru.

Proto tvrdím – nikdy nepoužívej v abstract presenterech __construct(), ale inject(), vyhneš se parent::__construct() peklu.

To taky není tak úplně pravda. Konstruktor hell se tím sice vyřeší, ale není nutné používat inject metody či anotace. Existují i jiné způsoby, jak se vyhnout konstruktor hell. (Například nepoužívat více úrovní Presenterů, ale jen 1 a v každém presenteru jen 1 akci, takže budeš mít konstruktor sám pro sebe, nebo použít třídu obalující všechny závislosti pro daný konstruktor do sebe, takže bude závislost jen 1 pro každý konstruktor atp…)

Ano, toto je správná poznámka, vedle ConstructHell existuje i InjectHell

Opět mimo. Nic jako InjectHell neexistuje, jelikož už z podstaty existovat nemůže. Konstruktor má každá třída jen jeden a je použitý na předání povinných závislostí (bez parametrů postnutých do konstruktoru prostě instanci nevytvoříš.)
Narozdíl od toho inject metod, anotací atd. můžeš mít neomezené množství a nemáš nikdy nikde zaručeno, že se budou volat. To znamená, že by se měl používat podobně jako setter jen pro nepovinné závislosti. To, že Nette DI ví, že tam má předat ty závislosti a ty se tedy můžeš spolehnout na to, že inject metody/anotace fungují není zásluha jazyka ani návrhového vzoru DI, ale jen dobré vůle Grudla, který byl tak hodný a tuto fičuru do NetteDI doimplementoval aby v Presenterech nenastávalo peklo. Pokud ti totiž začne metoda inject v parentovi bobtnat, tak v potomkovi ji prostě nebudeš přetězovat, ale vytvoříš si jinou, jinak pojmenovanou. Tzn. žádné InjectHell neexistuje.

Jistě, je to určitě jasné a přehledné mít to takhle udělané, ale porovnejme tyto dva kódy:

Opět jsme se asi nepochopili. Já měl na mysli toto:
Před:

class BasePresenter extend Presenter
{
	public function injectUserRepository(App\Model\UserRepository $userRepository)
	{
 	  	$this->userRepository = $userRepository;
	}
	public function injectUserLogInFormFactory(App\Form\LogInFormFactory $logInFormFactory)
	{
   		$this->logInFormFactory = $logInFormFactory;
	}
}

Předělat na toto:

class BasePresenter extend Presenter
{
	public function injectBasePresenterDependencies(
   		App\Model\UserRepository $userRepository,
   		App\Form\LogInFormFactory $logInFormFactory,
	) {
   		$this->userRepository = $userRepository;
   		$this->logInFormFactory = $logInFormFactory;
	}
}

Nebo od PHP8 na toto:

class BasePresenter extend Presenter
{
	#[Inject] public App\Model\UserRepository $userRepository;
   	#[Inject] public App\Form\LogInFormFactory $logInFormFactory;
}

No a než napíšeš, že public properties jsou zlo, tak nejsou, jen je třeba umět je použít. Kdyby byly zlo, tak by neexistovaly. Zrovna v tomhle případě je to celkem jedno a ušetříš si práci.
Jediný důvod proč se upřednostňovaly inject metody před inject anotacemi bylo, že v PHP nebylo možno u properties vynutit datové typy (takže ti do public property mohl kdokoliv podvrhnout či poslat cokoliv), což šlo v metodě nejdříve ověřit ifem a následně vyhodit výjimku, případně později to šlo ověřit vynucením typu v argumentu metody. Dnes, když se dá nadefinovat typ přímo v property, tak odpadá nutnost používat inject metody s výjimkou toho, kdy v nich potřebujeme něco dodatečně nastavit.

Třeba se dočkáme i toho, že někoho z PHP tvůrců osvítí „duch svatý“ a zavedou obdobu construktor property promotion i pro nějakou jednu další speciální metodu, třeba mě napadá:

Nic takového nejspíše existovat nebude a to už ze samé podstaty, jelikož jak jsem psal výše inject je výmysl FW Nette. Není to žádný pattern, žádný výchozí způsob používání atp.
Taktéž jak jsem psal inject metod můžeš mít neomezeně mnoho, takže by nešlo nijak rozumně určit, kde to možné je a kde není. Taktéž kdyby se ujal jen jeden možný název, například inject jen jak píšeš, tak počkej, to je zvláštní. Něco mi to připomíná. Počkej… Jo vlastně. Konstruktor má úplně stejný předpis. :) Proč bychom měli mít 2 metody, které mají dělat totéž a fungovat stejně? To smrdí porušováním DRY principu. :)
No a důvodů, proč to nedělat mě napadá celá další řada.

m.brecher
Generous Backer | 724
+
0
-

@Polki

Ze zaslaných kódů je jasné, že Jsi v Nette a programování obecně mnohem dál než já. Asi jsem měl nad tím, co Jsi poslal víc popřemýšlet, než jsem něco napsal.

1)
Ano, příklad s BasePresenterem, který řeší nějakou autorizaci a jeden z final presenterů řeší něco ještě důležitějšího a proto se volá parent::startup() až na konci – s tím nelze než souhlasit. Ze všech pravidel existují výjimky a dají se udělat, když člověk ví přesně co dělá a proč.

2)
Vše ostatní řešit ve startupu – to jsem napsal blbě – správně mělo být vše ostatní v startup/action/render/beforeRender

3)
„nikdy nepoužívej“ – to je taková básnická nadsázka, nikdy jsem použil ve smyslu „snaž se vyhnout“

4)
Myšlenka nepoužívat abstract presentery – šlo by to taky, ale asi stejně všichni alespoň jeden BasePresenter používáme

5)
InjectHell neexistuje z podstaty – ano to Jsi vyargumentoval správně takže nezbývá než souhlasit

6)
přehlednost kódu jak navrhuješ:

class BasePresenter extend Presenter
{
 	public function injectBasePresenterDependencies(
    		App\Model\UserRepository $userRepository,
    		App\Form\LogInFormFactory $logInFormFactory,
 	) {
    		$this->userRepository = $userRepository;
    		$this->logInFormFactory = $logInFormFactory;
 	}
 }

Ano, to jsme si vlastně jenom nerozuměli – tohle je přehledné a navíc zde není konflikt s podobnou inject metodou v případném nadřazeném presenteru, tohle se mě líbí :)

7)
Nebo od PHP8 na toto:

class BasePresenter extend Presenter
{
 	#[Inject] public App\Model\UserRepository $userRepository;
   	#[Inject] public App\Form\LogInFormFactory $logInFormFactory;
}

Ano, ano – tohle je asi nejestetičtější zápis předání závislostí, který jsem vůbec neznal, protože atributy v OOP jsem ještě nikdy nepoužíval ale plánuji do nich také proniknout ;)

8)
„public properties jsou zlo“ – ve všech publikacích a článcích, co jsem o PHP četl se všude dokola opakovalo, „public properties nejsou nejlepší nápad, …“, ale samozřejmě, ve speciálních případech se použít dají – opět člověk musí přesně vědět co dělá a proč.

9)
„construktor property promotion pro další metody“ – ano, to jsem jenom fantazíroval a je potřeba se vrátit do reality :)

Dík za široký a kvalitní výklad problematiky předání závislostí, který mne určitě posunul o kus dál. A určitě se naše názory na inject() příliš neliší.

Polki
Člen | 553
+
0
-

@mbrecher
Mě se líbí, jak se snažíš. Pozoruju tvoje dění tady na fóru a myslím si, že jsi udělal dobrý pokrok za krátký čas.
To se o moc lidech říct nedá. Například mám jednu žačku, která je stále schopná po 3 letech učení napsat něco jako:

interface UserLoginControlFactory
{
	public function create(new UserRepository()): UserLoginControl;
}

Což už z podstaty je špatně.
Když pomineme to, že podle MensyČR má IQ 153, tak si myslím, že za 3 roky se generovanou továrničku a to jak funguje DI a tvorba komponent dokáže naučit každý a ne jen ten talentovaný nebo chytrý.

Ještě k tvým bodům:

  1. Máš pravdu, že jsem ani na jednom projektu neviděl, že by někdo neměl nějaký abstraktní Presenter. Nicméně poslední dobou je doporučována cesta bez abstraktního presenteru a s Traitami, tedy jakýkoliv duplicitní kód je v traitě a duplicita je jen v use daných trait
  2. Existuje to i v PHP nižším, než 8:
class BasePresenter extend Presenter
{
	/** @inject */ public App\Model\UserRepository $userRepository; // PHP 7.4
   	/** @var App\Form\LogInFormFactory @inject */ public $logInFormFactory; // PHP < 7.4
}
  1. Je to tak proto, že lidi to neumí používat. Stejně jako třeba GOTO.

Proto se celá komunita i školy řídí heslem než to použít špatně, tak radši vůbec.
Jednoduše jde říci, že pokud máš proměnnou, do které je ti úplně jedno, kdo ti co dá a kdo si co z toho bude číst, tak ji dej public.
Příklad správně použité public property:

public int $libovolneCislo;

v tento moment máme ověřeno, že v té proměnné bude INT to nám stačí a je nám jedno kdo nám to odkud přepíše, nebo kdo to bude číst, vždy se zachováme správně a berem to tak, že tam může být libovolný INT
Příklad špatně použité public property:

public int $sudeCislo;

Zde chceme, aby v proměnné bylo uloženo sudé číslo, ale kvůli tomu, že je public nám tam může kdokoliv dát cokoliv, takže klidně i liché číslo. Proto vzniklo skrytí této property a zpřístupnit ji pouze přes Getter/Setter
Příklad správně použité public property:

private int $sudeCislo;
public function setSudeCislo(int $cislo): void
{
	$this->sudeCislo = $cislo * 2;
}
public function getSudeCislo(): int
{
	return $this->sudeCislo;
}

Což nám zajistí správnou integritu dat.
Jenže málokdo opravdu pozná rozdíl a proto se doporučuje dělat PURE gettery a settery i když nemají žádný význam, jen předají plain hodnotu. (Před PHP 7.4 to byla absolutní nutnost, protože samotná property mohla být jakéhokoliv datového typu, takže pro vynucení datového typu se VŽDY musel použít setter.)

Editoval Polki (6. 10. 2021 14:12)

m.brecher
Generous Backer | 724
+
0
-

@Polki – do Nette se zpočátku vniká velmi rychle, ale jakmile chce člověk aplikaci strukturovat do komponent a složitějších šablon tak křivka obtížnosti vyletí strmě vzhůru. Ale proniknout se do toho dá.

Polki
Člen | 553
+
0
-

@mbrecher
To je pravda. Nicméně taky záleží, co člověk dělal předtím, než se Nette začal učit a jak je sžitý s přemýšlením programátora a jestli umí návrhové vzory apod.

Mě třeba trvalo se do celého Nette dostat přesně 7 dní, jelikož jsem předtím vyzkoušel snad všechny dostupné jazyky a tedy to, jak funguje Nette mi šlo krásně do ruky, protože například se mi líbila syntaxe zápisu v Javě, ale nelíbila se mi v PHP. No když jsem objevil Nette, které celou PHP logiku předělalo do nějakých ucelených tříd a snad nad každou php metodou si udělalo vlastní obal a sjednotilo to do krásného celku, tak jsem byl tak nadšený, že jsem dokumentaci hltal po litrech.

Videí tehdy moc nebylo a jediné kurzy byly přímo od Davida a to až v Praze/Brně, takže to nepřipadalo v úvahu, tak jsem během prvních 2 dní projel celou dokumentaci a vyzkoušel si napsat 3 menší projekty, abych se s tím sžil a pak začal projíždět projekty jiných lidí, abych viděl, jak to mají napsáni oni. To mi zabralo 5 dní. Takže celkem 7 dní v prosinci roku 2013 no a od 1.1.2014 Ofiko v Nette makám.

Samozřejmě jsou věci, které jsem neznal a naučil se je až posléze, ale to jsou drobnosti a většinou na běh aplikace jako takové nemají vliv.

Nikdy mi vlastně žádný jazyk nepřišel těžký. Jen nepohodlný, nebo pro dané konkrétní věci zbytečný. Nette je takové, které bych zatím neměnil. Dostalo mě tak moc, že jsem z všestranného programátora, který uměl udělat jakoukoliv aplikaci v jakémkoliv jazyce nativně pro dané zařízení přešel čistě na Nette.
Jsem moc rád, že přichází noví lidé, kteří se Nette chtějí věnovat.

David Grudl
Nette Core | 8099
+
+8
-

@mbrecher @Polki Nechtěli byste jít na pivo?

Polki
Člen | 553
+
+3
-

@DavidGrudl
Myslíš, abychom to neřešili na fóru, tak si k tomu máme spolu sednout na pivo?

Jen škoda, že to nevyjde, protože pivo nepiju. :D

Editoval Polki (7. 10. 2021 0:17)

Allconius
Člen | 313
+
0
-

Ahoj, díky moc za všechny odpovědi nakonec jsem to vyřešil tak že přidávám do všech presenterů:

    protected function startup()
    {
        parent::startup();
        $this->template->aplikace = $this->dbManager->aplikace;

    }

vpodstatě jsem to řešil protože potřebuji přidat proměnnou z konfigu do základní šablony.
common.neon:

parameters:
  config:
    aplikacenazev: OZ_kurzy
    aplikace: 149

@layout.latte

{if (($user->loggedIn)and(($user->getIdentity()->aplikace)==$aplikace))}

potřebuji jen zkontrolovat jestli přihlášený uživatel má nastavenou i správnou aplikaci :-) MOžná to jde nějak jednodušeji ale nic lepšího mě nenapadlo než to cpát do šablony přes presenter …
ten dbManager dělá jen:

    /** @var Nette\Database\Context */
    private $database;

    /**
     * @var array
     */
    private $config;
    public $aplikace;


    public function __construct(array $config, Nette\Database\Context $database)
    {
        $this->database = $database;
        $this->config = $config;
        $this->aplikace = $this->config['aplikace'];

    }
Allconius
Člen | 313
+
+1
-

Polki napsal(a):

@DavidGrudl
Myslíš, abychom to neřešili na fóru, tak si k tomu máme spolu sednout na pivo?

Jen škoda, že to nevyjde, protože pivo nepiju. :D

šel bych do čajovny, líbí se mi tam ta atmosféra a taky pivo nepiju :-D

Polki
Člen | 553
+
0
-

@Allconius

  1. Pokud neděláš nějakou logiku s šablonama v action metodách, dal bych to do startupu.
  2. Pokud si nejsi jistý, tak to, co děláš dělej takto:

Vytvoř si ACL třídu:

<?php

declare(strict_types = 1);

namespace App\Model;

use Nette\Security\Permission;

class AuthorizatorFactory
{
	public static function create(int $configAplikace): Permission
	{
		$acl = new Permission();

		$acl->addRole('guest');									// nastav si svou roli

		$acl->addResource('application');						// tady nech natvrdo application

		$acl->allow('guest' ,'application', (string) $configAplikace);	// Tady nastavíš, že ten, kdo má roli guest, má přístup pouze do aplikace, kterou máš zadanou v configu.

		return $acl;
	}
}

config.neon:

parameters:
	config:
		aplikacenazev: OZ_kurzy
		aplikace: 149

srvices:
	- App\Model\AuthorizatorFactory::create(configAplikace: %aplikace%)

A pak už jen šablona @layout.latte:

{if $user->isAllowed('application', (string)($user->getIdentity()->aplikace ?? 'no_application'))}

V Presenterech nemusíš kvůli toho dělat vůbec nic a stejně tak nemusíš předávat nic do šablon a ani tvořit dbManagera. Tyto 3 fragmenty kódu, co jsem napsal úplně dostačují.
Akorát, pokud aplikacenazev je unikátní, tak bych možná používal ten, aby jsi nemusel do rolí přetypovávat na string.

šel bych do čajovny, líbí se mi tam ta atmosféra a taky pivo nepiju :-D

Přesně :D V baráku jsem si na to vyhradil jednu místnost a udělal si z ní čajovnu.

Editoval Polki (7. 10. 2021 18:50)