Lze injectnout databázi do externích libs?

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

____________________________________________________________

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)

Šaman
Člen | 2668
+
+5
-

Veškeré automatické injectování funguje JEN na třídách, které vytváří DI kontejner. Tedy vesměs jen na těch, které jsou definované v configu.

Ale ta chyba souvisí s tím, že property nepatří do funkce, ale do těla třídy.

Editoval Šaman (30. 5. 2015 0:02)

Azathoth
Člen | 495
+
+1
-

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
+
0
-

Diky moc, vyresil jsem to podle posledniho napadu:

<?php
use Nette\Database\Connection;

class userManage{

public function __construct(){
$database = new Connection(...);
}

Editoval hotline (1. 6. 2015 16:26)

hotline
Člen | 41
+
0
-

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)

Unlink
Člen | 298
+
0
-

v konštruktore by to nemalo byť?

$this->database = new Connection(...);

Ale podľa mňa to nieje dobré, takto vytvárať connection, mal by si i ho vyžiadať v konštruktore.

Aké bude použitie tej triedy UserManager?

Editoval Unlink (1. 6. 2015 16:34)

hotline
Člen | 41
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

Precti si, prosim, dokumentaci. Nejdrive co to je DI a proc to pouzivat a pak jak to pouzivat v nette

hotline
Člen | 41
+
0
-

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)

Azathoth
Člen | 495
+
0
-

@hotline a zkoušel jsi mazat cacheM mně to vždycky pomůže.
Máš use mezi namespace a class tohle?

use Nette;
hotline
Člen | 41
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
+1
-

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
+
0
-

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)