Lze injectnout databázi do externích libs?
- hotline
- Člen | 41
____________________________________________________________
Funkcni reseni v tomto tematu:
____________________________________________________________
Dobry vecer,
pro praci a kompletni spravu uzivatelu bych potreboval pouzivat svou specifickou tridu. Abych to mel pekne oddelene, nastavil jsem si RobotLoadera, aby skenoval slozku „libs“, do ktere jsem ji umistil. Je to super vec, vse funguje jak ma. Narazil jsem ale na problem v pripade, kdy potrebuji pouzivat databazi. Jde tam klasicky nejak injectnout?
Zkousel jsem to takhle:
public function manage_user($uid){
/** @var Nette\Database\Context @inject */
public $database;
}
Coz mi dava chybu
syntax error, unexpected 'public' (T_PUBLIC)
Diky za rady. Klidne jestli vas napada jine reseni nez slozka libs, ktera by asi mela byt plne externi, tedy nezavisla na zbytku aplikace (?).
Editoval hotline (8. 6. 2015 16:51)
- Azathoth
- Člen | 495
já osobně bych asi zvolil následující postup: mít u té externí třídy interface, který bude sloužit pro komunikaci s databází a za třída z libs bude pracovat pouze s tím interfacem…
dále tu třídu uděláš jako extension (nebo si ji zqaregistruješ jako service, ale přijde mi, že co je v libs/vendor, by mělo být jen jako extension), kde zaregistruješ tuhle třídu jako sloužbu a ona bude v konstruktoru očekávat implementaci výše zmíněného interface.
A pak si jen implementuješ ten interface, zaregistruješ do configu tu implementaci a je to.
A nebo budeš rovnou vyžadovat natvrdo Nette\Database\Context a uděláš tu tvou dřídu v libs závislou na Nette\Database.
- hotline
- Člen | 41
Ja byl stastny, ze to nepise chybu a ted mi doslo, ze je to pripojeni lazy a chyba se vypise az pri dotazu, na ktery ted prisla rada. Takze moje reseni neni funkcni.
Dotaz v nejake metode:
$result = $this->database->table('xxx')...
->where('key=? AND is_active=?', $key, '1');
$row = $result->fetch();
vraci: Call to a member function table() on null
(hned na prvni
radce v dotazu)
Slo by to nejak vyresit nebo na to jdu spatne? Diky za rady.
Editoval hotline (1. 6. 2015 16:25)
- hotline
- Člen | 41
Dve pripojeni asi nejsou uplne idealni, ale do budoucna ty databaze nejspis jeste oddelim, takze by mi i vyhovovalo, kdybych mel pro tuto tridu vlastni pripojeni do db.
Pripojeni bych mel mit v konstruktoru. Ve zkratce to vypada takto:
<?php
use Nette\Database\Connection;
class userManage{
public $database;
public function __construct(){
$this->database = new Connection(...);
}
public function keyVer($key){
$result = $this->database->table('xxx')
->where('key=? AND is_active=?', $key, '1');
$row = $result->fetch();
/...
}
- Unlink
- Člen | 298
No ale to by nemal byť problém, ja by som spravil
<?php
use Nette\Database\Connection;
class userManage{
private $database;
public function __construct(Connection $database){
$this->database = $database;
}
public function keyVer($key){
$result = $this->database->table('xxx')
->where('key=? AND is_active=?', $key, '1');
$row = $result->fetch();
/...
}
a túto triedu by som si zaregistroval klasicky do DI (do budúcnosti môžeš spraviť extension ktorá to spraví sama)
A použitie vlastnej connection nieje problém cez DI nadefinovať.
Išlo mi o to, že či potrebuješ nutne vytvárať inštancie tej triedy sám, alebo to môžeš nechať na DI.
- hotline
- Člen | 41
Diky za rady, ale ja potrebuji tu svou tridu volat primo, bez dalsich zavislosti, apod. Pouzivam ji v kazdem presenteru i v komponentach a resit tam nejake predavani zavislosti by bylo dost komplikovane.
Kdyz se nad tim zamyslim, proc by vlastne nebylo mozne vytvorit tam normalne nove pripojeni a dal s nim pracovat? Aniz bych musel resit neon, predavani pripojeni v presenterech, apod.
Mam to na mysli nejak takhle:
<?php
use Nette\Database\Connection,
Nette\Database\Context;
class userManage{
public $database; public $connection;
public function __construct(){
$connection = new Nette\Database\Connection("mysql:host=localhost;dbname=nette3", "db_nette", "xxx");
$database = new Nette\Database\Context($connection);
}
}
To by melo byt teoreticky funkcni pokud se nepletu. Akorat mi to dava nejakou
chybu:
Argument 2 passed to Nette\Database\Context::__construct() must implement interface Nette\Database\IStructure, none given, called in /libs/usermanage.php on line 11 and defined
,
coz je zvlasti, metody jsou volany v poradku.
Editoval hotline (1. 6. 2015 20:41)
- David Matějka
- Moderator | 6445
Precti si, prosim, dokumentaci. Nejdrive co to je DI a proc to pouzivat a pak jak to pouzivat v nette
- hotline
- Člen | 41
Diky za odkazy, ja uz na to koukal a zkousel to injectovat dvema zpusoby, coz neslo, tak jsem chtel vytvorit nove pripojeni, abych to nemusel dale resit a ani to nevyslo.
1. zpusob:
class neco extends Nette\Application\UI\Presenter{
/** @var Nette\Database\Context @inject */
public $database;
}
Vyusti pri dotazu na databazi takto:
Call to a member function table() on null
2. zpusob:
class neco extends App\Presenters\BasePresenter{
public function __construct(Nette\Database\Context $database)
{
$this->database = $database;
}
}
Vyusti v chybu:
Argument 1 passed to slevhouse_function::__construct() must be an instance of Nette\Database\Context, none given, called in app/presenters/XPresenter.php on line 42 and defined
na radce, kde se vola konstruktor v jednom z presenteru, ve kterem se
k databazi pristupuje stejnym zpusobem.
Zitra se na to poradne podivam a proctu si to, asi jsem neco nekde prehledl. :) Zatim diky.
Editoval hotline (1. 6. 2015 22:56)
- hotline
- Člen | 41
Bohuzel ani smazani cache nepomohlo. Projel jsem nekolik threadu na foru a mel bych to snad mit spravne, akorat se jeste vsude pise, ze bych mel tridu zaregistrovat jako service v neonu, ale nevim, jestli se to nevztahovalo jen k modelum a komponentam. Protoze me to pise, ze trida nebyla nalezena, kdyz ji tam zaregistruju.
<?php
use Nette;
class Test extends Nette\Application\UI\Presenter{
/** @var Nette\Database\Context @inject */
public $database;
public function __construct(Nette\Database\Connection $database)
{
parent::__construct();
$this->database = $database;
}
Argument 1 passed to Test::__construct() must be an instance of Nette\Database\Connection, none given, called in ClanekPresenter.php on line 42 and defined – na radce 42 v clanekpresenteru volam v metode renderDefault tuto tridu, tak se to asi nejak bije.
____________________________________________________________________________________
Nebude spis jednodussi resit to jako model? Mam tady ukazkovou tridua
UserManager.php, ktera s databazi pracuje, tak by to snad nemel byt problem. :D
Takze jsem smazal kompletne slozku libs, vytvoril soubor Test.php ve slozce
model s obsahem
namespace App\Model;
use Nette;
class Test extends \Nette\Object{
/** @var Nette\Database\Context */
private $database;
public function __construct(\Nette\Database\Context $database)
{
$this->database = $database;
}
//...
}
Do neonu jsem pridal service: - App\Model\Test
K databazi pristupuji stejne jako ukazkovy model UserManager.php a stejne
dostavam tuto stejnou, zapeklitou chybu:
`
Argument 1 passed to App\Model\Test::__construct() must be an instance of
Nette\Database\Context, none given, called in app/presenters/BasePresenter.php
on line 40 and defined`
BasePresenter.php:40 je $test = new \App\Model\Test();
Predpokladam, ze problem bude v config.neon, protoze tam nikde nepredavam databazi. Jenze ja databazi nemam jako service, ale takto samostatne:
services:
router: App\RouterFactory::createRouter
- App\Model\Test
database:
dsn: 'mysql:host=xxx;dbname=xxx'
user: xxx
password: xxx
Tak jsem to predelal podle navodu na foru:
services:
router: App\RouterFactory::createRouter
- App\Model\Test
database:
class: Nette\Database\Connection
arguments: ["mysql:host=xxx;dbname=xxx", "xxx", "xxx"]
test:
class: App\Model\Test
arguments: ["@database"]
A z Test.php odebral radky: `/** @var Nette\Database\Context
@inject */
public $database;`
Coz mi vraci chybu:
Service of type Nette\Database\Context used in @var annotation at Property [ <default> public $database ] not found. Did you register it in configuration file?
- David Matějka
- Moderator | 6445
tu tridu Test
nemuzes v base presenteru vytvaret, ale injectnes
si ji. (a tu konfigurace databaze vrat, jak jsi mel pred tim)
- hotline
- Člen | 41
Jj, diky moc, uz jsem to pochopil. Radsi si to sem napisu, kdybych to zapomnel.
Injekce modelu do (base)presenteru:
<?php
namespace App\Presenters;
use Nette,
App\Model;
/**
* Base presenter for all application presenters.
*/
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
/**
* @inject
* @var \App\Model\Test */
public $test;
protected function createTemplate($class = NULL)
{
//$var = $this->test->metoda();
}
}
Editoval hotline (4. 6. 2015 22:50)
- hotline
- Člen | 41
Prosim vas, mam trochu problem s injekci modelu do komponenty. Zkousim to konstruktorem (neni setter lepsi?) a porad nemuzu najit funkcni reseni. Jdu na to nasledovne (zakomentovany kod jsem pridal pro pridani modelu):
<?php
use Nette\Application\UI\Control;
class LoginControl extends Control
{
//private $test;
private $database;
public function __construct(Nette\Database\Context $database/*, Service $service*/)
{
parent::__construct();
$this->database = $database;
//$this->test = $service;
}
}
A presenter:
<?php
namespace App\Presenters;
use Nette,
App\Model;
/**
* Base presenter for all application presenters.
*/
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
/** @var Nette\Database\Context @inject */
public $database;
/**
* @inject
* @var \App\Model\Test */
public $test;
protected function createComponentLoginControl()
{
//$control = new MyControl($this->service); /*to je asi spatne*/
$control = $this->loginControl->create();
return $control;
}
}
Koukal jsem do dokumentace a popravde z toho nejsem moc moudry. Jdu na to takhle dobre nebo spis prosim vas nevite kde mam chybu? Dekuji moc, tyhle injekce mi porad delaji problemy. :(
Editoval hotline (4. 6. 2015 22:52)
- hotline
- Člen | 41
Diky, tovarnu bych uz mel mit, ty delam radsi automaticky, jen jsem to neuvedl, aby to bylo kratsi, omlouvam se.
Takhle vypada konec komponenty:
/** rozhraní pro generovanou továrničku */
interface ILoginControl
{
/** @return \LoginControl */
function create();
}
Je v ni i vlastni latte:
public function render()
{
$template = $this->template;
$template->setFile(__DIR__ . '/Login.latte');
$this->template->render();
}
A v basepresenteru ji pak injectuju:
/** @var \ILoginControl @inject */
public $loginControl;
Koukal jsem na toto tema – https://forum.nette.org/…o-komponenty kde se resi to co potrebuji, pise se tam:
Marek Šneberger napsal(a):
V komponentách jedině konstruktor. Pokud si nechceš v tovrničce předávat závislosti, doporučuju si vytvořit interface a Nette ti závislosti automaticky předá. Viz příklad v plaNette
Interface mam, takze by to melo byt automaticke? Ja jsem komponentu i vytvarel pomoci toho prikladu na planette, ale nikde nemuzu najit, jak tam tedy ten model dostat.
Kdyz mam komponentu s tovarnickou, postupuji spatne nebo zbytecne slozite, kdyz se snazim pouzivat constror nebo setter? Nejak z toho prikladu nemuzu pochopit, jak Nette automaticky prida zavislost na modelu – v prikladu zadny model nevidim, nebo se pletu?
Prijde mi, ze jsem tu teoretickou cast dokumentace spatne pochopil a nikde neni zadny konkretni priklad pro injekci modelu do tovarnicky komponenty. Muzu vas poprosit o nejake nasmerovani nebo nakopnuti? Delal jsem prvni projekt v nette, ktery mam prakticky hotovy, je to parada, ale potrebuji jeste propojit ty komponenty s modelem, aby to fugnovalo.
Diky za odpovedi.
Editoval hotline (8. 6. 2015 16:21)
- David Matějka
- Moderator | 6445
na pla.nette si predava do komponenty Nette\Database\Connection
,
uplne stejnym zpusobem si muzes predat i jakoukoliv jinou sluzbu.
K generovanym tovarnam jeste odkazu na http://www.zeminem.cz/…nitive-guide
a mimochodem, ten interface pojmenuj radeji treba ILoginControlFactory, at je jasne, ze jde o tovarnu
- hotline
- Člen | 41
Proboha, ono je to opravdu tak jednoduche? Za ty 3 dny co to resim jsem vyzkousel tolik slozitosti, ze se ted fakt musim smat. Diky moc, Davide, jsem tvym velkym dluznikem.
Kdyby to nekdo potreboval, tak:
<?php
use Nette\Application\UI\Control;
class LoginControl extends Control
{
private $testModel;
private $database;
public function __construct(Nette\Database\Context $database, App\Model\testModel $testModel)
{
parent::__construct();
$this->database = $database;
$this->testModel = $testModel; //a mame ho :)
}
}
Editoval hotline (8. 6. 2015 16:48)