ako pracovať s modelom, prepojenia s presenterom atd

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
account23
Člen | 36
+
0
-

Ahojte,

nejak blúdim v dokumentácii a návodoch a youtube a všade možne :D ale nemôžem prísť na to ako pracovať s Modelom, ako vlastne funguje, ako ho prepojiť s presenterom a view, čo má byť v model a čo v presenter?

keby bol nejaký príklad nejaký malý web alebo niečo jednoduché vytvorené pomocou modelu, pozeral som QuickStart a tam ani raz model nebol použitý. V dokumentaci blúdim a nemôžem poriadne zistiť ako ho využívať. Na nete je všetko iba na staré verzie, neviem či má význam zisťovať to tam.

Prešiel som z frameworku CodeIgniter a tam model slúžil iba na spájanie sa s databázov, jediné čo som o modele u nette zistil, že to neni tak ako u codeigniter :D

Je vôbec možné naučiť sa pracovať s nette na tejto verzii ?

vvoody
Člen | 910
+
0
-

Model nemá žiadnu striktnú formu v nette. Prejdi si príklady, za model sa dá považovať napríklad AlbumRepository.

romiix.org
Člen | 343
+
0
-

Vysvetlenie čo je Model: https://doc.nette.org/…n/presenters
https://doc.nette.org/…n/presenters

V skratke: Model zabezpečuje dáta pre Presenter. Obsluhuje dátový zdroj a poskytuje potrebné dáta vo vhodnej forme. Štandardne je model obyčajný objekt definovaný ako služba.

account23
Člen | 36
+
0
-

No pozeram to ale moc tomu nechápem, spýtam sa teda takto.

Spravím nejakú funkciu v modeli, ako ju dostanem do presentera a následne zobrazím na webe ?

romiix.org
Člen | 343
+
0
-

account23 napsal(a):

No pozeram to ale moc tomu nechápem, spýtam sa teda takto.

Spravím nejakú funkciu v modeli, ako ju dostanem do presentera a následne zobrazím na webe ?

To je konkrétna otázka na ktorú sa dá odpovedať:)

Vytvor si model – Napr. triedu TestModel.php

Triedu zaregistruj ako službu. Objekt sa vytvorí iba raz.

Službu dodaj do presentru (princíp Dependency injection). Buď cez __construct, metódu inject*, setter alebo anotáciu @inject.

Editoval romiix.org (8. 1. 2014 16:17)

jiri.pudil
Nette Blogger | 1032
+
0
-

Jen pro terminologickou úplnost (protože v dokumentaci je to asi příliš nenápadné): model je označení celé vrstvy aplikace, která má na starosti veškerou business logiku a ani ne sebemenším projektu obvykle není reprezentovaná jen jednou třídou.

account23
Člen | 36
+
0
-

Ďakujem, skúšam to pozerať, dúfam že to aj pochopím :D dúfal som že to bude jednoduchšie že len do presenteru nahodim nejaký ten load alebo insert a potom už len využívam funkcie v modeli ..

iNviNho
Člen | 352
+
0
-

Podľa mňa je to celé zložité … ja som sa v tom stratil hned, preto som pre pracu s DB začal používať DIBI a moj život je sladší …

V modeli si vytvorim iba funkciu, ktorá mi vráti napr. dané fetchnuté riadky cez return a v presenteri ju volam napr. SelectModel::VyberHracov()

a v modeli mam public static function vyberHracov() {
$select = dibi dotaz …
return $select;
}

Ak ťa to zaujalo, napíšem ti konkrétny príklad …

Editoval iNviNho (8. 1. 2014 18:06)

account23
Člen | 36
+
0
-

iNviNho – to znie o dosť jednoduchšie ako to registrovanie do služby :) Ten príkldad by fakt potešíl, takže budem môcť volať model tak jednoducho SelectModel::VyberHracov() bez žiadnych rýpaní sa v config ?

Mysteria
Člen | 797
+
+1
-

@account23: Pro Nette DB taky žádnou službu dělat nemusíš, klidně můžeš do presenteru napsat

$model = new \Model\ProductsModel();
$model->findAll();

Ale když to zaregistruješ jako službu, tak pak je to takto:

/**
 * @inject
 * @var \Model\ProductsModel */
public $model;

$this->model->findAll();

Jednou z výhod služby je, že veškerou její konfiguraci (například dotažení připojení k DB a podobně) neřešíš někde v presenteru ale v config.neon.

Osobně když jsem s Nette začínal, tak jsem taky používal první způsob, protože jsem taky neměl tušení, co to vlastně ta „služba“ je, k čemu to je, jak to vytvořit a podobně, ale časem jsem na to přišel. :)

Jinak co se týká toho MVC problému, tak vlastně to jde úplně zjednodušit, že máš Presenter, který řekne Modelu aby mu dodal data a ty pak dá šabloně, takže úplně jednoduše to není nic víc než:

class TestPresenter {
	/**
	 * @inject
	 * @var \Model\TestModel */
	public $model;

	public function renderDefault() {
		$this->template->data = $this->model->findAll();
	}
}

class TestModel {
	/**
	* @inject
	* @var \Nette\Database\Context */
	public $database;

	public function findAll() {
		return $this->database->table('test')->fetchAll();
	}
}

No a v šabloně máš pak proměnnou {$data}, který obsahuje pole všech údajů co máš v DB.

Editoval Mysteria (8. 1. 2014 19:28)

romiix.org
Člen | 343
+
0
-

Pred pár dňami sa mi dostal do ruky web ktorý používal všade modely typu SelectModel::VyberHracov() – samé statické triedy.

Vyznať sa v takom kóde a dohľadať všetky závisosti je PEKLO!

DI má svoj zmyseľ. Ak sa na to vykašleš len preto, že je to na začiatku trošičku zložitejšie, neskôr to budeš ľutovať. Pravdaže pri malinkom webíku je to jedno, ale ak ti to trochu narastie, buď to potom budeš prepisovať odznovu, alebo budeš trpieť a nadávať si.

account23
Člen | 36
+
0
-

a čo keby som to spravil v presenteri nejak takto:

$model = new \Model\ProductsModel();

$model->funkciaModelu();

Takto jednoducho by to fungovať nemohlo ? s použitím čistého nette ?

V tom tvojom kode to tak nejak vypadá pokiaľ

findAll(); // je funkcia z modelu ?
romiix.org
Člen | 343
+
0
-

To či použiješ

$model = new \Model\ProductsModel();

$model->funkciaModelu();

alebo

\Model\ProductsModel::funkciaModelu()

na veci veľa nemení. Tak či tak používaš model skryte. Na prvý pohľad nie je závislosť na tom modeli zrejmá.

Hlavnou myšlienkou je definovanie závislostí. Tzn. ak ti presenter závisí na \Model\ProductsModel(), tak mu radšej dodaj ten objekt rovno správne nakonfigurovaný tak ako popísal Mysteriapríspevku vyššie :)

Presenter si objekty ktoré potrebuje k behu navytvára a nezískava od niekiaľ, ale ich hotové dostáva.

account23
Člen | 36
+
0
-

ok, dík konečne sa môžem v tom trošku pohnúť :)

Mysteria
Člen | 797
+
+1
-

@account23: Chápu, že se to může zdát složitý, ale jakmile to tak nějak pochopíš, tak si pak vlastně řekneš že to MVC a DI vlastně je úplně logická a dobrá věc i když zezačátku to nejspíš vidíš jako nějakou složitou a nepochopitelnou věc.

Nejlíp bych to asi vysvětlil na příkladu jo, protože tak to bylo pro mě nejlíp pochopitelný, tak by pro tebe mohlo bejt taky. Vezmi si, že to budeš mít napsaný prasácky:

class ProductsPresenter {
    public function renderDefault() {
        $this->template->data = (new \Model\ProductsModel())->findAll();
   }
}

class ProductsModel{
    private $database;

    public function __construct() {
        $this->database = new \Nette\Database\Context(new \Nette\Database\Connection('mysql:host=localhost;dbname=database', 'root', 'password' ));
    }

    public function findAll() {
        return $this->database->table('test')->fetchAll();
    }
}

Jo, ono to takhle fungovat bude. Ale má to několik háčků. První je, co když se rozhodneš změnit připojení k databázi? Všechny modely budeš ručně upravovat? Právě proto by si všechny součásti do toho Modelu měl dávat zvenčí (buď přes ty magické anotace @inject, nebo klidně v konstruktoru), aby prostě bylo jasné co ten Model potřebuje (to jsou ty závislosti). Další věcí je, když se rozhodneš upravit třeba konstruktor toho modelu, tak ve všech prezenterech budeš měnit to vytváření modelu, kdežto když použiješ zase DI, tak v config.neon si tam ten parametr předáš a hotovo. V prezenteru nebudeš muset měnit vůbec nic.

Ono fakt nejlíp to pochopíš v případě, když jedeš prasácky a pak až budeš po páté editovat 10 souborů kvůli jedné změně, tak už si pak možná řekneš, proč jsem jenom nedal tomu DI šanci a neušetřil jsem kupu času. ;)

account23
Člen | 36
+
0
-

No pozeral som aj ten článok o DI a tiež tam vkuse točia ohľadom pripojenia k databáze, načo teda slúži config.local.neon ? Nerieši sa to pripojenie na databázu rovno tam a už to viac nikde nemusim riešiť ? Načo by som v každom modely požadoval nové spojenie ?

A čo ak chcem databázu mať zapnutú globálne? Že ju budem využívať v každom scripte a nemusel ju nijak volať, šlo by to ? Alebo na to slúži ten druhý config.neon ?

Ako som už písal, prešiel som z frameworku codeigniter a tam to fungovalo typom – autoload – automaticky mi načítalo model, alebo librari, či helper… na celý script čiže som ho už nemusel nikde viac volať a mohol využívať jeho funkcie. Čiže mohol som naloadovať databázu pre celý script a už som ju nemusel ani raz v celom scripte spomenúť

Editoval account23 (9. 1. 2014 11:23)

jiri.pudil
Nette Blogger | 1032
+
0
-

Načo by som v každom modely požadoval nové spojenie ?

Správná otázka. To je nesmysl. Podstatou služby je právě to, že jde v rámci jednoho requestu stále o tutéž instanci. Takže pokud budeš třeba v jednom requestu potřebovat zvýšit hodnocení článku a zároveň přepočítat celkové hodnocení jeho autora a budeš to řešit ve dvou různých třídách, obě ty třídy pracují se stejným připojením.

A čo ak chcem databázu mať zapnutú globálne?
Nerieši sa to pripojenie na databázu rovno tam a už to viac nikde nemusim riešiť ?

Ano, tohle si Nette řeší samo. V neonu uvedeš konfiguraci pro připojení k databázi a Nette z ní vygeneruje patřičnou službu. Není ale pravda, že už to dál nikde nemusíš řešit. Ta služba není globálně dostupná. Proč by to bylo špatně, ses mohl dočíst třeba v dokumentaci nebo výše v příspěvku od @Mysteria.

Takže musíš vyřešit, jak si tu službu zpřístupnit ve vlastní modelové třídě. To se dá jednoduše udělat tak, že si o ni řekneš v konstruktoru. Díky tomu, že pak i tu svoji třídu zaregistruješ v configu jako službu, se Nette samo postará o předání té závislosti.

Potom ti zbývá už jen vyřešit, jak tu svoji službu dostat tam, kde ji potřebuješ. Takže třeba do presenteru. A to Nette opět zajistí samo, když si o tu službu řekneš pomocí @inject anotace nebo inject* metody. (Čistější by opět bylo použít konstruktor, ale vzhledem k tomu, že se z presenterů obvykle vytváří nějaká struktura pomocí dědičnosti, by to mohlo způsobit víc problémů, než usnadnění.)

(edit: o ní → o ni; jejda!)

Editoval jiri.pudil (9. 1. 2014 12:02)

iNviNho
Člen | 352
+
0
-

V SelectModel:

<?php
public static function vypisInfoOInzerate($id) {
       return dibi::fetch("select * from inzeraty where inzeraty.id_inz = %i", $id);
   }
?>

V presenteri

<?php
	$info = SelectModel::vypisInfoOInzerate($id);
	$this->template->info = $info;
?>

Ja naozaj nevidím v tomto nikde chybu a v modeli nevidím žiadne pripojovanie do DB … Jeden celý rok sme sa na VŠ učili databázy a rád by som si queries rád písal sám, aj keď s použitim NetteDatabase by to šlo jednoduchšie, ale queries sa už robia inak …

Jan Suchánek
Člen | 404
+
0
-

iNviNho: Model si předávej do Presenteru přez inject a připojení(dibi) do Modelu v autowiringem v neonu přez konstruktor a nepoužívej singleton.

Mysteria
Člen | 797
+
0
-

@account23: Tím, že zaregistruješ v Nette službu neznamená, že ji máš automaticky ve všech modelech a presenterech. Když tu službu potřebuješ, tak ji tam prostě injectneš a tím pádem máš pouze ty služby, které potřebuješ (je nesmysl dávat do všech presenterů úplně všechny služby, když je nepotřebuješ).

@iNviNho: Jak v tvém případě dostáváš dibi do toho modelu? Navíc to používání statických metod, když to není nezbytné, tak podle mě taky není dobrej nápad.

iNviNho
Člen | 352
+
0
-

V index.php mám

<?php
dibi::connect(\Nette\Environment::getConfig('database'));
?>

samozrejme v configu údaje pre connect …

Všetci píšu nepoužívaj, ale nikto nenapíše prečo :-\

Mysteria
Člen | 797
+
0
-

@iNviNho: Co třeba minimálně proto, že Environment je od verze 2.0 deprecated, když pomineme, že tímhle způsobem nejde identifikovat závislost modelu na databázi?

Editoval Mysteria (9. 1. 2014 16:20)

mkoubik
Člen | 728
+
0
-

To už si rovnou můžeš nadefinavoat globální funkce database_connect() a vypis_info($id) a psát si spaghetti-code do vypis.php, na to nepotřebuješ framework :-)

David Matějka
Moderator | 6445
+
0
-

@iNviNho: registruj si jeden z extensionu dle tve verze nette https://github.com/…ridges/Nette

iNviNho
Člen | 352
+
0
-

Stále nikto nenapísal, čo sa môže stať a kde je problém.

„identifikovat závislost modelu na databázi?“ je to potrebné? Ak hej, tak už konečne prečo

Mysteria
Člen | 797
+
0
-

@iNviNho: Myslím, že to nemá cenu, je o tom tady celá stránka. Každopádně osobně mě je jedno, že tomu nedáš šanci, nechceš se zlepšit a bráníš špatný přístup. Problém nastává až v případě, kdy je možnost, že se tvýho špatnýho přístupu chytne nováček, kterej nemá možnost poznat, že je to špatně a místo toho, aby se už odzačátku učil správnej postup, tak se naučí něco, co stejně časem vyhodnotí jako „špatnou etapu v programování“ (tam se dostaneš taky, časem).

A nepoužívání zastaralých tříd by snad měla být samozřejmost, tam není co vysvětlovat.

Editoval Mysteria (9. 1. 2014 17:28)

Pavel Macháň
Člen | 282
+
0
-

iNviNho napsal(a):

Stále nikto nenapísal, čo sa môže stať a kde je problém.

„identifikovat závislost modelu na databázi?“ je to potrebné? Ak hej, tak už konečne prečo

Píšu to po paměti ± takže tam může být někde chyba.

config.neon – nejsem si teď jistej jestli se do 2.1 dostalo možnost registrovat extension v configu (a jestli jsem to napsal dobře)… kdyžtak zaregistrovat v bootstrapu před vytvořením kontejneru

common:
	extensions:
    		dibi: \DibiNetteExtension
	dibi:
		driver: mysql
		host: localhost
		username: test
		password: test
		database: test_db
		lazy: TRUE

index.php

$container = require __DIR__ . '/../app/bootstrap.php';
$container->application->run();

presenter

class HomepagePresenter extends Nette\Application\UI\Presenter {

    /**
     * @var \DibiConnection
     * @injection
     */
    public $db;

    public function rednerDefault() {
        $this->db->query('Select * .....');
    }

}

Editoval EIFEL (9. 1. 2014 17:30)

mystik
Člen | 312
+
0
-

iNviNho napsal(a):

Stále nikto nenapísal, čo sa môže stať a kde je problém.

„identifikovat závislost modelu na databázi?“ je to potrebné? Ak hej, tak už konečne prečo

Protože skryté závislosti způsobují, že z kódu se ti postupně stane noční můra ;-) Když nevíš jaké závislosti tvoje komponenty mají tak nevíš jaké důsledky může mít změna jedné komponenty na další.

account23
Člen | 36
+
0
-

heh docela dobrá diskusia sa tu rozbviedla :) Viete čo by mi naozaj pomohlo ? :) Keby bol niekto tak dobrý a ochotný napísať sem krátky príklad napríklad ako je QuickStart, ale bola by v ňom použitá databáza a nie user z config.neon a aby tento script používal model a bolo to vytvorené bez dibi a úplne korektne ako by sa to malo robiť

Myslím že by to pomohlo veľa ľuďom, čo som pozeral tak tu je taký návodík:
"":https://pla.nette.org/…tvarime-blog

ale niesom si istý či je to dobré aj na Nette 2.1 a ako by to vypadalo bez dibi – absolutne som nepochopil ako ho kde dať do akej zložky /libs
či ho obsahuje staršia verzia nette(v 2.1 som zložku libs nenašiel) alebo myslia libs serveru čo je dosť nanič ak chcem využívať normálny webhosting

Editoval account23 (9. 1. 2014 18:31)

Mysteria
Člen | 797
+
0
-

@account23: Tohle je přesně to, co hledáš s jedinou drobností, že tam je pro DI použito konstruktorů místo @inject anotací. Ale nic ti nebrání jednoduše to upravit na anotace.

/** @var AlbumRepository */
private $albums;

public function __construct(AlbumRepository $albums) {
	$this->albums = $albums;
}
/**
 * @inject
 * @var AlbumRepository */
public $albums;

Úplně stejně u modelu. Obě věci dělají naprosto stejné, správné, druhá je pro „línější“ programátory.

Co se týká libs složky tak to byla výchozí složka pro jakýkoliv knihovny projektu (tzn. Nette, Kdyby, Doctrine2,…). V Nette 2.1 se přejmenovala na vendor.

Editoval Mysteria (9. 1. 2014 18:46)

account23
Člen | 36
+
0
-

počkaj počkaj :D chceš mi povedať že zakomentovaný @inject niečo vykonáva ? :D Nuž idem to pozrieť dúfam že z toho budem múdrejší :)

David Matějka
Moderator | 6445
+
0
-

@account23: ano, anotace jsou obcas dulezite pro beh programu, viz https://forum.nette.org/…a-komponentu#…

Mysteria
Člen | 797
+
+1
-

@account23: Pozor, je rozdíl mezi /* */ a /** */.

/**
 * @inject
 * @var AlbumRepository */
public $albums;

Tohle je vlastně „příkaz“ pro Nette znějící zhruba takto: „Najdi službu (třídu) AlbumRepository (Repository = Model) a vlož ji do proměnné album.“ Výhodou je, že se všechno udělaná samo, takže vůbec neřešíš, jak se to do tý proměnný uloží, všechno se udělá „samo“.

account23
Člen | 36
+
0
-

Ahá takže

/**
 * @inject - hľadá model
 * @var AlbumRepository - class v modeli a priradí to do premmenéj pod sebou ? :D */

Inač ten priklad na github má takú chybu, chýba tam nejaký ten popis. Skúsil som si to nahodiť no nechápem odkiaľ ten script berie dáta ? :D pridávam tam nejaké dáta a je tam user. Podľa config sa pripájam na localhost test databázi ale tá je prázdna :D Tu je nejaká mágia či čo ? :D

Editoval account23 (9. 1. 2014 19:47)

David Matějka
Moderator | 6445
+
0
-

kdyz kouknes do configu, zjistis, ze se pouziva sqlite z adresare app/model

Mysteria
Člen | 797
+
+3
-

@account23: Ta databáze samozřejmě magická není je to normální SQLite3 databáze (soubor app/model/demo.db3). Ale samozřejmě můžeš si ji exportovat do MySQL, upravit připojení k DB a bude to taky fungovat.

@inject říká, že se má Nette pokusit najít službu (služeb může být víc druhů, ne jenom modely) definovanou ve @var. A ve @var není nic jinýho než název třídy.

Takže když si vytvoříš class MujPrvniModel { }, tak tam bude @var MujPrvniModel a podobně.

Ale vlastně teď jsem si všiml, že tu řešíme @inject služby, ale neřekli jsme ti, jak tu službu vlastně nadefinovat. To se dělá v config.neon:

services:
	- Authenticator
	- AlbumRepository

Praktické shrnutí:
1. Vytvořit model (a rovnou třeba injectnout databázi, z 99% ji budeš potřebovat):

class MujModel {
	/** @var \Nette\Database\Context */
	public $database;

	public __construct(\Nette\Database\Context $database) {
		$this->database = $database;
	}

	public function mojeMetoda() {
		return null;
	}
}

2. Vytvořit službu v config.neon:

services:
	- MujModel

3. Vytvořit presenter a injectnout model:

class MujPresenter {
	/**
 	* @inject
 	* @var MujModel */
	public $model;

	public function renderDefault() {
		$this->template->data = $this->model->mojeMetoda();
	}
}

Možná si říkáš, jak to, že u injectu té databáze má ve @var \Nette\Database\Context, když v config.neon není nikde služba - \Nette\Database\Context, ale to je tím, že u databáze to dělá Nette za tebe když ji nakonfiguruješ:

nette:
    database:
        default:
            dsn: 'mysql:host=host;dbname=dbname'
            user: user
            password: password
            reflection: conventional

Samozřejmě tohle je nejzákladnější varianta, služby mohou mít parametry a podobně.

Když se budeš tímhle řídit, tak využíváš jak MVC, tak i DI a tomu kódu není co vytknout. :)

Editoval Mysteria (22. 3. 2014 11:22)

account23
Člen | 36
+
0
-

@Mysteria Nice, super vysvetlenie :) konečne tomu poriadne chápem

David Matějka
Moderator | 6445
+
0
-

@Mysteria: @inject anotace ve sluzbach nepouzivej. krom toho, ze jsou defaultne vypnuty, tak je to osklivy. preferuj konstruktor injection

class MujModel {
    /** @var \Nette\Database\Context */
    public $database;

    public function __construct(\Nette\Database\Context $database)
    {
        $this->database = $database;
    }

    public function mojeMetoda() {
        return null;
    }
}
Mysteria
Člen | 797
+
0
-

@account23: V pohodě, rád jsem pomohl. Já to měl horší, jelikož jsem na tohle musel přijít sám.

@matej21: Může se zeptat z jakýho důvodu je pro služby lepší konstruktor před anotacema? Osobně když nepředávám žádné jiné parametry modelu tak preferuju inject, abych tam ten konstruktor nemusel dávat, ale nebránit změnit preference. :)

Ohledně vypnutí, nevím jak v 2.1, ale ve 2.2 jsem nic nezapínal (nebo jsem to zapomněl) a fungují.

David Matějka
Moderator | 6445
+
0
-

@Mysteria: v konstruktoru jsou ty zavislosti jasne viditelny a nezpochybnitelny. Kdyz mas zavislosti v properties s @inject anotaci, tak je mozno onen objekt vytvorit bez zavislosti, coz zpusobi, ze bude v nekonzistentnim stavu a nepude s nim pracovat. a kdyz budes mit zavislosti v konstruktoru, neprimo te to nuti psat hezci kod

vice treba https://forum.nette.org/…a-steroidech#…

Editoval matej21 (9. 1. 2014 21:16)

account23
Člen | 36
+
0
-

@matej21: Kde všade by som @inject používať nemal a kde mohol ? netuším čo myslíš pod pomenovaním v službách :D čo patrí do tých služieb a čo nie ? :D

David Matějka
Moderator | 6445
+
0
-

@account23: sluzby je vsechno, co mas registrovane v configu pod sekci services (tedy ruzne modelove tridy atd). @inject anotace pouzivej pouze v presenterech. v modelech a jinych sluzbach pouzivej konstruktor injection

Editoval matej21 (9. 1. 2014 22:22)

Mysteria
Člen | 797
+
0
-

@matej21: Díky za vysvětlení. Nakonec jsem se tu naučil i já něco užitečnýho. :)

Filip Procházka
Moderator | 4668
+
0
-

Zrada: do configu si můžeš registrovat i presentery a Nette to pozná a bude používat DI Container na jejich vytvoření.

Služba je cokoliv co je registrované v DI Containeru (to se dělá pomocí services v configu).

Ohledně Dependency Injection tady myslím chybí ještě odkaz na tento článek v dokumentaci.


Konstruktor injection je nejčistější možný způsob předání závislostí, protože sám o sobě je součást jazyka. Definuje závislosti nejtvrdším možným způsobem, když si napíšeš třídu

class ArticlesModel extends Nette\Object
{
	/** @var Nette\Database\Context @inject */
	public $db;

	public function getData() {
		return $this->db->query('SELECT ...');
	}
}

A potom si vytvoříš instanci a zavoláš metodu

$model = new ArticlesModel();
$model->getData();

Tak se model v pohodě vytvoří ale když zavoláš metodu vyskočí na tebe FATAL ERROR, protože v $db nic není!

Problém je to v okamžiku kdy mezi vytvořením a použitím máš 100 řádků kódu a ještě je volání metody v podmínce, takže se nemusí provést vždy. Tady se dá snadno přehlédnout chyba a kód může havarovat až na hostingu, kde tvého klienta (nebo tebe) může taková chyba stát peníze.

Když ale naopak definuješ závislost v konstruktoru

class ArticlesModel extends Nette\Object
{
	/** @var Nette\Database\Context */
	private $db;

	public function __construct(Nette\Database\Context $db) {
		$this->db = $db;
	}

	public function getData() {
		return $this->db->query('SELECT ...');
	}
}

Tak už ani nevytvoříš instanci takového modelu. Víš proč? Protože jazyk jasně říká, nehledě na framework, že třída ke své existenci potřebuje databázové spojení a bez něj nemá smysl aby vznikla její instance.

Tohle to hodí prostě fatal error.

$model = new ArticlesModel();

Proto je konstruktor nejčistší možný způsob předání závislostí. Ohledně těch ostatních více čtení viz už jednou linkovaný post.

KEnik
Člen | 10
+
0
-

Zdravím, když jsem se pokusil nastavit dle prispěvku #36
tak mi nette vyhodí chybu:

Nette\DI\ServiceCreationException
Class JidloModel used in service ‚24_JidloModel‘ has not been found or is not instantiable.

Co mám špatně?

config.neon:
`parameters:

php:
date.timezone: Europe/Prague
# zlib.output_compression: yes

nette:
application:
errorPresenter: Error
mapping:
*: App*Module\Presenters*Presenter

session:
expiration: 14 days

services
JidloModel
App\UserManager
App\RouterFactory

router: @App\RouterFactory::createRouter `

app/model/JidloModel.php

<?php
namespace App\Model;

class JidloModel extends BaseModel
{


	public function get($id)
	{
		return $this->database->table('jidla')->find($id)->fetch();
	}

}

app/model/BaseModel.php

<?php
namespace App\Model;

use Nette\Object;

class BaseModel extends Object
{
	public $database;
	public function __construct($database)
	{
		$this->database = $database;
	}


}

app/model/presenters/JidloPresenter.php

<?php
namespace App\Presenters;

use Nette,
	App\Model;
use JidloGrid;


class JidloPresenter extends DefaultPresenter
{

	private $JidloModel;



	public function startup()
	{
		parent::startup();
		$this->JidloModel = $this->getModel('JidloModel');

	}
Šaman
Člen | 2666
+
0
-

Chybi ti tam namespace:

services:
    App\Model\JidloModel
KEnik
Člen | 10
+
0
-

Update: díky Šamene
config.neon doplnil jsem namespace na:

services
App\Model\JidloModel
Nyní mi to hlásí

Nette\DI\ServiceCreationException
Service ‚22_App_Model_JidloModel‘: Parameter $database in App\Model\BaseModel::__construct() has no type hint, so its value must be specified

Editoval KEnik (24. 2. 2014 12:40)

Šaman
Člen | 2666
+
0
-

Neví to, co se má předat do konstruktoru jako parametr $database. Musíš specifikovat jakou třídu to má v systémovém kontejneru najít a předat (nejspíš Nette\Database\Connection). Nastuduj si Dependency Injection v dokumentaci, resp. co je to autowiring

<?php
namespace App\Model;

use Nette\Object;

class BaseModel extends Object
{
    public $database;
    public function __construct(Nette\Database\Connection $database)
    {
        $this->database = $database;
    }


}
?>

Editoval Šaman (24. 2. 2014 12:59)

KEnik
Člen | 10
+
0
-

DIky to už jsme odladil. Měl jsme tam více chyb ze starých pokusu o načtení jinými zpusoby.

ted mám problém použít model v presenteru příklad mi nedává moc smysl

3. Vytvořit presenter a injectnout model:

class MujPresenter {
    /**
    * @inject
    * @var MujModel */
    public $model;

    public function renderDefault() {
        $this->template->data = $this->model->mojeMetoda();
    }
}

Jak pozná že má vzít metodu z class MujModel?
presentery maji namespace App\Presenters;
modely namespace App\Model;

toto mi nefunguje:

<?php
namespace App\Presenters;

use Nette,
	App\Model;
use JidloGrid;
use App\Model\JidloModel;


class JidloPresenter extends BasePresenter
{

	private $jidloModel;




      public function renderDefault($id)
    {
		$this->template->data = $this->jidloModel->mojeMetoda();
	}

Výsledek:
Fatal Error
Call to a member function mojeMetoda() on a non-object search►

Šaman
Člen | 2666
+
0
-
  1. To, co se snažíš využívat je autowiring. Podívej se na něj. Využívá typehint a anotace, bez nich to skutečně neví, co použít. Takže je doplň podle té ukázky nahoře.
  2. Jestli se v posledních commitech nic nezměnilo, tak u anotací se musí vypsat celý namespace, protože neumí pracovat se sekcí use. U typehintů stačí napsat namespace do use a jako typehint pak použit jen samotný název třídy.

Pokud bys chtěl ukázku odladěného kódu nad aktuálním nette 2.1, tak na GitHubu mám jednu miniaplikaci. Jen zrovna to injectování a továrničky nejsou řešené přímo v presenterech, ale pomocí traity přímo u komponenty/služby, která se má předat nebo vytvořit (ale je to přesně ten kód, který by se vložil do presenteru místo řádku use TInjectNeco, nebo use TCreateComponentNeco.)

Radši tě odkážu na jinou ukázku, sice ještě nad vývojovou verzí Nette, ale pro presentery se nic nezměnilo. V tomhle případě si injectuji třídu, kterou později využívám k vytvoření komponenty. Jak anotace @var, tak anotace @inject jsou nutné!

Editoval Šaman (24. 2. 2014 13:20)