Jak zavolat v prezenteru komponentu ?

Allconius
Člen | 313
+
0
-

Ahoj, koukal jsem na dokumentaci https://doc.nette.org/…n/components a vytvořil jsem si komponentu (SignComponent.php):

<?php

declare(strict_types=1);

namespace App\Presenters;

use Nette\Application\UI\Control;

class SignControl extends Control
{

    public function render(): void
    {
        // vložíme do šablony nějaké parametry
        $this->template->param = 7;
        // a vykreslíme ji
        $this->template->render(__DIR__ . '/SignComponent.latte');
    }

}

První dotaz je jestli u komponenty je dobře „namespace App\Presenters;“? Bez toho mi to nějak vůbec prezenter neviděl. Pak mám BasePresenter.php kde chci tu komponentu načíst:

<?php

declare(strict_types=1);

namespace App\Presenters;

use Nette;
use App\Components\Sign\SignComponent;
use Tracy\Debugger;

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

    protected function createComponentSign(): SignControl
    {
        $sign = new SignControl;
        $sign->items = $this->item;
        return $sign;
    }

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

        $sign = $this->getComponent('sign');


    }

}


V HomePresenter.php nemám nic:

<?php

declare(strict_types=1);

namespace App\Presenters;

use Nette;

class HomePresenter extends BasePresenter
{


}

a vždycky se mi to zasekne na tom vytváření items:

Cannot read an undeclared property App\Presenters\HomePresenter::$item.

Editoval Allconius (24. 4. 2023 11:47)

dakur
Člen | 493
+
+3
-

Cannot read an undeclared property App\Presenters\HomePresenter::$item.

Říká ti to, že nezná property $item v home presenteru. V tom sice nic není, ale dědí od base presenteru, takže musíš hledat i o úroveň výš.

V metodě createComponentSign() máš totiž $sign->items = $this->item no a to $this->item nemáš nikde definované, takže proto ti to píše.

Allconius
Člen | 313
+
0
-

dakur napsal(a):

Cannot read an undeclared property App\Presenters\HomePresenter::$item.

Říká ti to, že nezná property $item v home presenteru. V tom sice nic není, ale dědí od base presenteru, takže musíš hledat i o úroveň výš.

V metodě createComponentSign() máš totiž $sign->items = $this->item no a to $this->item nemáš nikde definované, takže proto ti to píše.

No to je právě to co nechápu proč v té inicializaci komponety je v dokumentaci nějaký items:

class DefaultPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentPoll(): PollControl
	{
		$poll = new PollControl;
		$poll->items = $this->item;
		return $poll;
	}
}

nebo to má být jen jako příklad použití, že si do komponenty můžeš načíst nějaké prvky z presenteru ?

dakur
Člen | 493
+
0
-

Ano, je to jen příklad, jak do ní předat data. I když uznávám, že ne úplně šťastný, protože jednak je v něm překlep a druhak se to v celém článku už nikde nepoužívá, takže není ani jasné, proč to tam, co to má představovat, kde se to tam bere etc. :-) Prostě jestli to nepotřebuješ, tak ten řádek smaž.

dakur
Člen | 493
+
0
-

P.S.: na předání dat do komponenty pak viz níže: https://doc.nette.org/…n/components#…

Asi to $this->item je nějaký zapomenutý kód..

Allconius
Člen | 313
+
0
-

dakur napsal(a):

P.S.: na předání dat do komponenty pak viz níže: https://doc.nette.org/…n/components#…

Asi to $this->item je nějaký zapomenutý kód..

Ahoj, díky a toto tam musí být?:

$sign = $this->getComponent('sign');

mě se ta komponenta načte i bez toho jen tím:

createComponentSign()
m.brecher
Generous Backer | 765
+
0
-

@Allconius

Ahoj, díky a toto tam musí být?:

$sign = $this->getComponent('sign');

Záleží na tom, jestli proměnnou $sign někde dál použiješ, pokud ne tak je to zbytečné, komponenta se zavolá automaticky při vykreslování/signálu.

První dotaz je jestli u komponenty je dobře „namespace App\Presenters;“?

Dal bych to do jiného namespace než presenter, ale potom když někde třídu komponenty použiješ, musíš buďto a) napsat jméno třídy včetně namespace, nebo b) importovat třídu pomocí use – třeba use App\Components\Sign – asi proto ti to nefungovalo v presenterech.

dakur
Člen | 493
+
0
-

Allconius napsal(a):

Ahoj, díky a toto tam musí být?:

$sign = $this->getComponent('sign');

mě se ta komponenta načte i bez toho jen tím:

createComponentSign()

Nene, komponenty se instanciují automaticky ve chvíli, kdy je použiješ v šabloně, takže pokud nepotřebuješ komponentu použít i v presenteru, není to třeba.

Allconius
Člen | 313
+
0
-

m.brecher napsal(a):

@Allconius

Ahoj, díky a toto tam musí být?:

$sign = $this->getComponent('sign');

Záleží na tom, jestli proměnnou $sign někde dál použiješ, pokud ne tak je to zbytečné, komponenta se zavolá automaticky při vykreslování/signálu.

První dotaz je jestli u komponenty je dobře „namespace App\Presenters;“?

Dal bych to do jiného namespace než presenter, ale potom když někde třídu komponenty použiješ, musíš buďto a) napsat jméno třídy včetně namespace, nebo b) importovat třídu pomocí use – třeba use App\Components\Sign – asi proto ti to nefungovalo v presenterech.

Ahoj,
to právě mám v presenteru přes use (BasePresenter.php):

<?php

declare(strict_types=1);

namespace App\Presenters;

use Nette;
use App\Components\Sign\SignComponent;

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

    protected function createComponentSign(): SignControl
    {
        $sign = new SignControl;
        return $sign;
    }


akorát je divný že když mám v té komponentě namespace App\Presenters; tak to funguje (SignComponent.php):

<?php

declare(strict_types=1);

namespace App\Presenters;
//namespace App\Components;

use Nette\Application\UI\Control;

class SignControl extends Control
{

    public function render(): void
    {
        // vložíme do šablony nějaké parametry
        $this->template->param = 7;
        // a vykreslíme ji
        $this->template->render(__DIR__ . '/SignComponent.latte');
    }

}

ale když tam dám namespace App\Components; tak to hodí:

Error

Class "App\Presenters\SignControl" not found

na tom řádku:

$sign = new SignControl;

nightfish
Člen | 474
+
+2
-

Allconius napsal(a):
akorát je divný že když mám v té komponentě namespace App\Presenters; tak to funguje (SignComponent.php):

ale když tam dám namespace App\Components; tak to hodí:
Class „App\Presenters\SignControl“ not found

@Allconius
To je očekávané chování.
V BasePresenteru máš use App\Components\Sign\SignComponent; (všimni si, že na konci je SignComponent). V metodě createComponentSign() pak voláš new SignControl (všimni si, že je to SignControl, nikoliv SignComponent). A protože SignControl nikde neimportuješ pomocí use, tak se očekává, že je ve stejném namespace jako presenter.

Udělej si pořádek v pojmenování komponenty (doporučuji spíš používat SignControl než SignComponent), dej SignControl do „správného“ namespace (App\Components\Sign), pak ji v BasePresenteru naimportuj pomocí use App\Components\Sign\SignControl a vše bude fungovat dle očekávání.

Allconius
Člen | 313
+
0
-

nightfish napsal(a):

Allconius napsal(a):
akorát je divný že když mám v té komponentě namespace App\Presenters; tak to funguje (SignComponent.php):

ale když tam dám namespace App\Components; tak to hodí:
Class „App\Presenters\SignControl“ not found

@Allconius
To je očekávané chování.
V BasePresenteru máš use App\Components\Sign\SignComponent; (všimni si, že na konci je SignComponent). V metodě createComponentSign() pak voláš new SignControl (všimni si, že je to SignControl, nikoliv SignComponent). A protože SignControl nikde neimportuješ pomocí use, tak se očekává, že je ve stejném namespace jako presenter.

Udělej si pořádek v pojmenování komponenty (doporučuji spíš používat SignControl než SignComponent), dej SignControl do „správného“ namespace (App\Components\Sign), pak ji v BasePresenteru naimportuj pomocí use App\Components\Sign\SignControl a vše bude fungovat dle očekávání.

Ahoj, upravil jsem názvy a mám teď 2 soubory App\Components\Contacts\DetailControl.php a App\Components\Contacts\DetailControl.latte
DetailControl.php:

<?php

declare(strict_types=1);

namespace App\Components;

use Nette\Application\UI\Control;

class DetailControl extends Control{

    public function render(): void
    {

        $this->template->render(__DIR__.'/DetailControl.latte');

    }
}

a HomePresenter.php:

<?php

declare(strict_types=1);

namespace App\Presenters;

use Nette;
use App\Components\Contacts\DetailControl;

class HomePresenter extends BasePresenter
{
    protected function createComponentDetail(): DetailControl
    {
        $detail = new DetailControl;
        return $detail;
    }

}

Ale stejně mi to píše:

Error
Class "App\Components\Contacts\DetailControl" not found

Co tam mám špatně ?

nightfish
Člen | 474
+
+1
-

Allconius napsal(a):
Ahoj, upravil jsem názvy a mám teď 2 soubory App\Components\Contacts\DetailControl.php
DetailControl.php:
namespace App\Components;
class DetailControl extends Control

Co tam mám špatně ?

Špatně máš namespace komponenty DetailControl. Není až tak důležité, v jakém adresáři je soubor uložen, nýbrž jaký má namespace. A ty máš namespace App\Components, tedy plný název třídy je App\Components\DetailControl a nikoliv App\Components\Contacts\DetailControl, jak uvádíš v presenteru.

Doporučuji se při organizaci tříd držet PSR-4, které říká, že namespace odpovídá adresáři. Ve tvém případě tedy změníš namespace App\Components; na namespace App\Components\Contacts\DetailControl; a bude to fungovat.

Allconius
Člen | 313
+
-1
-

nightfish napsal(a):

Allconius napsal(a):
Ahoj, upravil jsem názvy a mám teď 2 soubory App\Components\Contacts\DetailControl.php
DetailControl.php:
namespace App\Components;
class DetailControl extends Control

Co tam mám špatně ?

Špatně máš namespace komponenty DetailControl. Není až tak důležité, v jakém adresáři je soubor uložen, nýbrž jaký má namespace. A ty máš namespace App\Components, tedy plný název třídy je App\Components\DetailControl a nikoliv App\Components\Contacts\DetailControl, jak uvádíš v presenteru.

Doporučuji se při organizaci tříd držet PSR-4, které říká, že namespace odpovídá adresáři. Ve tvém případě tedy změníš namespace App\Components; na namespace App\Components\Contacts\DetailControl; a bude to fungovat.

Ahoj, díky moc konečně jsem to pochopil že NAMESPACE je adresář a USE je soubor :-)

namespace App\Components\Contacts;
use App\Components\Contacts\DetailControl;
Kcko
Člen | 465
+
+1
-

Allconius napsal(a):

nightfish napsal(a):

Allconius napsal(a):
Ahoj, upravil jsem názvy a mám teď 2 soubory App\Components\Contacts\DetailControl.php
DetailControl.php:
namespace App\Components;
class DetailControl extends Control

Co tam mám špatně ?

Špatně máš namespace komponenty DetailControl. Není až tak důležité, v jakém adresáři je soubor uložen, nýbrž jaký má namespace. A ty máš namespace App\Components, tedy plný název třídy je App\Components\DetailControl a nikoliv App\Components\Contacts\DetailControl, jak uvádíš v presenteru.

Doporučuji se při organizaci tříd držet PSR-4, které říká, že namespace odpovídá adresáři. Ve tvém případě tedy změníš namespace App\Components; na namespace App\Components\Contacts\DetailControl; a bude to fungovat.

Ahoj, díky moc konečně jsem to pochopil že NAMESPACE je adresář a USE je soubor :-)

namespace App\Components\Contacts;
use App\Components\Contacts\DetailControl;

Namespace žádný adresář není.

Namespace (jmenný prostor) je množinou symbolů, které slouží k uspořádání objektů různého druhu, takže tyto objekty mohou být označeny jménem. Například: – programovací jazyky organizují své proměnné, funkce a podprogramy v oborech jmen – u počítačových sítí a distribuovaných systémů se přiřazují názvy zdrojům, jako jsou počítače, tiskárny, webové stránky apod. – v rámci Internetu jsou webové zdroje rozděleny do hierarchických struktur za pomoci DNS – souborové systémy jsou jmenné prostory, které přiřazují názvy souborům a adresářům

Zdroj: https://it-slovnik.cz/…m/namespace/?…

Allconius
Člen | 313
+
0
-

Ještě dotaz ohledně továrny, můžu si načítat spojení s DB třeba z modelu a předávat přes továrnu do komponenty ? (DetailControlFactory.php):

<?php

declare(strict_types=1);

namespace App\Components\Contacts;

use Nette;
use Nette\Mail\Message;
use Nette\Mail\SendmailMailer;
use Nette\Utils\Image;
use App\Model\DbManager;

class DetailControlFactory
{
    public function __construct(
        private DbManager $dbManager,
    ) {
    }

    public function create(int $id): DetailControl
    {
        return new DetailControl($id, $this->dbManager);
    }
}

Nebo jak nejlépe řešit spojení s DB v komponentě ?

Allconius
Člen | 313
+
0
-

Takto mi to funguje jen nevím jestli to není zbytečně složitý (DetailControl.php) :

<?php

declare(strict_types=1);

namespace App\Components\Contacts;

use Nette\Application\UI\Control;
use Nette\Mail\Message;
use Nette\Mail\SendmailMailer;
use Nette\Utils\Image;
use App\Model\DbManager;

class DetailControl extends Control {

    /**
     * @param $id
     */
    private $id;
    private $dbManager;


    function __construct($id, DbManager $dbManager)
    {

        $this->id = $id;
        $this->dbManager = $dbManager;
    }

    public function render(): void
    {

        $oo = $this->dbManager->najdiSouhlas(77777);
        $this->template->oscis = $this->id;
        $this->template->render(__DIR__.'/DetailControl.latte');

    }
}

nightfish
Člen | 474
+
0
-

@Allconius
Můžeš použít generovanou továrnu, tzn. kód továrny nahraď za

<?php

declare(strict_types=1);

namespace App\Components\Contacts;

interface DetailControlFactory
{
    public function create(int $id): DetailControl;
}

Pokud při registraci továrny v DI kontejneru používáš type: nebo class:, tak jej nahraď za implement (protože nahrazuješ třídu rozhraním). Nette se pak postará o vytvoření továrny ± ve stejném rozsahu, jako byla tvá původní továrna. Tato automaticky generovaná továrna pak zajistí předání (autowiring) všech argumentů konstruktoru DetailControl, které nemáš v továrně uvedeny jako argumenty metody create(). Čtení k tématu: https://doc.nette.org/…tion/factory#…

Allconius
Člen | 313
+
0
-

nightfish napsal(a):

@Allconius
Můžeš použít generovanou továrnu, tzn. kód továrny nahraď za

<?php

declare(strict_types=1);

namespace App\Components\Contacts;

interface DetailControlFactory
{
    public function create(int $id): DetailControl;
}

Pokud při registraci továrny v DI kontejneru používáš type: nebo class:, tak jej nahraď za implement (protože nahrazuješ třídu rozhraním). Nette se pak postará o vytvoření továrny ± ve stejném rozsahu, jako byla tvá původní továrna. Tato automaticky generovaná továrna pak zajistí předání (autowiring) všech argumentů konstruktoru DetailControl, které nemáš v továrně uvedeny jako argumenty metody create(). Čtení k tématu: https://doc.nette.org/…tion/factory#…

A co znamená hláška, že vlastnost nesmí být použita před inicializací?:

Error
Typed property App\Presenters\BasePresenter::$signControlFactory must not be accessed before initialization
mystik
Člen | 292
+
0
-

To znamena ze se z property pokousis cist hodnotu aniz bys tam pred tim nejakou nastavil. Kde ji nastavit zalezi na kontextu. Muzes ji nastavit jako default v definici, v konstruktoru nebo si pohlidat poradi v jakem se ti volaji metody, aby se ta co hodnotu nastavuje vzdy volala pred tou, kde se cte.