Existuje alternativa setRowClass (z DIBI) v Nette\Database?

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

Ahoj,
v novém projektu jsem původně chtěl opustit od dibi a zkusit nette\database, ale narazil jsem na věc, která by mi opravdu chyběla…

Jde o setRowClass, která umožňuje ze získaných dat a jeho řádků automaticky vytvořit instance dané třídy. To umožňuje na získaná data volat funkce dané třídy.

Dibi tedy standartně vrací objekt DibiRow, ale můžu to změnit např. na „Producer“.

U Nette\Database vrací TableSelection, nebo TableRow (to si teď přesně nepamatuju), ale nenašel jsem možnost to „přetypovat“, abych mohl nad daty volat fce objektu „Producer“.

Existuje taková možnost, nebo v tomhle případě musím zůstat u Dibi?

Díky, za odpovědi

Filip Procházka
Moderator | 4668
+
0
-

V současnosti ne.

jtousek
Člen | 951
+
0
-

Přímo v Nette to zatím nejde, ale existují forky, které to umožňují.

Např. zde:
https://github.com/…-refactoring
https://github.com/…tte/branches

baz
Člen | 42
+
0
-

Díky za reakce zkusím některé z forků…

případně se pak podělím s výsledkem – jestli jsem nakonec zůstal u Dibi, nebo zběhl…

jtousek
Člen | 951
+
0
-

Já zběhl s tím, že jsem si udělal vlastní fork. Ten je záměrně neveřejný protože jsem to udělal narychlo pomocí hacku a čekám než se do Nette dostane pořádná oprava. Nette\Database mi připadá v mnoha ohledech lepší než dibi i když má stále své drobné mouchy.

baz
Člen | 42
+
0
-

Zatím jsem u Dibi, ale narazil jsem na jiný problém.

Tím, že vytvářím objekt, přes setRowClass, nemám pod kontrolou konstruktor a tím pádem mu nemůžu vnutit objekt s databází, tak jak by se to mělo podle pravidel DI řešit.
Např. u handleru mám:

public function __construct(DibiConnection $db, $tableName, $rowClass = NULL) {
...
}

v configu mám předání databáze řešené takhle:

managerHandler: ManagerHandler( @dibi.connection, 'user', 'Manager' )

ale jak dostat databázi do objektu Manager, který vzniká takhle:

return $this->db->select('*')->from($this->tableName)->where("[id] = %i", $id)
	->execute()
	->setRowClass('Manager')
	->fetch();

Editoval baz (10. 6. 2012 1:12)

baz
Člen | 42
+
0
-

Opravdu to nemá řešení?

Pokud tohle nejde, tak mi nezbyde nic jiného, než to nějak nehezky obejít:

  1. v konstruktoru třídy Manager si vytáhnout nějakou globální proměnnou, kde bude DB (třeba z aktuálního presenteru)
  2. vyřešit to tak, že třídu, kterou já bych chtěl volat nad objektem manager ($manager->getPdfLink()) bych musel používat takhle: $managerHandler->getPdfLink($manager->id)

Obojí se mi ale moc nelíbí.
Další varianta je, nějak si poupravit Dibi, tak aby do konstruktoru DibiRow (případně nějakého rozšíření tahal i další objekt s databází) a já ho pak mohl použít i na ty vytvářené přes setRowClass, ale to už je asi nad moje síly :-(

Neřešil jste někdo tohle už?

Filip Procházka
Moderator | 4668
+
0
-

Nette\Database na tohle není stavěné!

baz
Člen | 42
+
0
-

Píšu, že jsem zvolil Dibi a ten poslední problém už se týká práce s ním, tak proč hned bold a vykřičník?

Filip Procházka
Moderator | 4668
+
0
-

Aby to pochopili i ti, kteří to budou číst po tobě ;)

baz
Člen | 42
+
0
-

Tak jsem ještě prohledával fórum Dibi a našel jsem: https://forum.dibiphp.com/…biconnection

kde by někdo jiný rád přesně to, co já potřebuju :-)

Ale tam jsi to také zavrhl (HosipLan).

Já to nechci používat v takové podobě jako je zmíněno v příspěvku, ale potřebuji nad objektem provádět složitější operace, které nejsou jen o dotazu do DB, ale nějakém zpracování získaných hodnot a vrácení výsledku.

v tomhle případě mi NotORM nepomůže (nebo možná jen nevím jak by pomoct mohlo)…

Je implementačně hodně náročné dát funkci setRowClass druhý nepovinný parametr, který by mohl obsahovat připojení k databázi a v případě, že by existoval, tak by ji nastavil i v konstruktoru nově vznikajícího elementu?

Editoval baz (11. 6. 2012 16:32)

Filip Procházka
Moderator | 4668
+
0
-

Prostě si na to napiš model

class Articles extends Nette\Object
{
	private $connection;

	public function __construct(DibiConnection $connection)
	{
		$this->connection = $connection;
	}

	public function findSomething()
	{
		$query = $this->connection
			->select('*')->from('articles')->where('...');

		return $this->map($query->execute());
	}

	private function map(DibiResult $result)
	{
		$result->setRowClass('Article');
		$conn = $this->connection
		return array_map(function ($article) use ($conn) {
			$article->setConnection($conn);
		}, $result->fetchAll());
	}

}

Do dibi to nepatří.

Editoval HosipLan (11. 6. 2012 18:55)

baz
Člen | 42
+
0
-

Podobný model mám:

class BasicHandler extends QObject {

	const PER_PAGE = 10;
	const DEFAULT_ORDER_BY = 'id ASC';

	protected $tableName;
	protected $rowClass;

	public function __construct(DibiConnection $db, $tableName, $rowClass = NULL) {
		parent::__construct($db);

		$this->tableName = $tableName;
		$this->rowClass = $rowClass;
	}


	public function getList($orderBy = self::DEFAULT_ORDER_BY) {
     		return $this->db->select('*')->from($this->tableName)->orderBy($orderBy);
	}

	public function find($id) {
		return $this->db->select('*')->from($this->tableName)->where("[id] = %i", $id)
		->execute()
		->setRowClass($this->rowClass)
		->fetch();
	}

	public function create($element) {
	      $result = $this->db->insert($this->tableName, (array) $element)->execute();
	      return $this->db->insertId($result);
	}
}

Předpokládám, že bych ho měl tedy upravit tak, že ve funkci find nebude setRowClass, ale execute pošlu do fce map (tak jako v tvém kódu).

Té právě ale úplně nerozumím :(

$conn = $this->connection	/* nechybi tu středník? */
return array_map(function ($article) use ($conn) {
	$article->setConnection($conn);
}, $result->fetchAll());

není zápis array_map(function …) pouze pro PHP 5.3. ?
Potřeboval bych variantu pro 5.2.

Bude to tedy vypadat nějak takhle?

function array_map_prepare($article, $conn) {
	$article->setConnection($conn);
}
return array_map(callback('array_map_prepare'), $result->fetchAll());

Nevím jak naložit s tím „use“ …

Díky

Editoval baz (11. 6. 2012 20:16)

Filip Procházka
Moderator | 4668
+
0
-
$conn = $this->connection       /* nechybi tu středník? */

Jasně chybí. A správně jsem předpokládal, že budeš dostatečně obezřetný, nebudeš hloupě kopírovat kód, ale přečteš si ho a chybičky si všimneš ;)

není zápis array_map(function …) pouze pro PHP 5.3. ?

ano

Bude to tedy vypadat nějak takhle?

	private function map(DibiResult $result)
{
        $result->setRowClass('Article');
        return array_map(callback($this, 'mapConnection'), $result->fetchAll());
}

	/**
	 * Mělo by fungovat i private, ale kdyby náhodou ne, tak public bude určitě ;)
	 */
	private function mapConnection($article)
	{
		$article->setConnection($this->connection);
		return $article;
	}

Editoval HosipLan (11. 6. 2012 23:27)

baz
Člen | 42
+
0
-

Paráda už to funguje :-)
Mockrát děkuju…

Ještě jsem tam musel upravit fci mapConnection:
(přidal jsem return $item bez toho totiž výsledkem array_map bylo pole plné NULL)

public function mapConnection($item) {		/* musí být public ... */
	$item->setConnection($this->db);
	return $item;
}

a vzhledem k tomu, že výsledkem array_map je vždy pole (i při jednom prvku) musel jsem fci find (která hledá podle unikátního klíče a tudíž vrací pouze jeden výsledek) upravit takto:

public function find($id) {
	$arr = $this->map($this->db->select('*')->from($this->tableName)->where("[id] = %i", $id)
		->execute());
	return $arr[0];
}

Každopádně ještě jednou děkuju a doufám, že to třeba pomůže i někomu dalšímu..

Editoval baz (11. 6. 2012 22:38)

baz
Člen | 42
+
0
-

Ahoj,
po delší době vývoje na localu (kde mám PHP 5.3.) jsem nahrál výsledek na cílový server, kde je PHP 5.2. a objevil se problém s fcí array_map (nebo callback)…

To co u mě funguje na serveru vrací následující chybu:

array_map(): The first argument, ‚ManagerHandler::mapConnection‘, should be either NULL or a valid callback

kód mám takto:

public function mapConnection($item) {
	$item->setConnection($this->db);
	return $item;
}

protected function map(DibiResult $result) {
	$result->setRowClass($this->rowClass);
	return array_map(callback($this, 'mapConnection'), $result->fetchAll());
}

public function getList($where = '', $orderBy = self::DEFAULT_ORDER_BY, $limit = '') {
	return $this->map($this->db->select($this->tableName . '.*')->from($this->tableName)->orderBy($orderBy));
}

Je to opravdu problém kompatibility, nebo tam může být i něco jiného?
Případně dá se to nějak jednoduše poupravit, nebo na to musím jít úplně jinak?

Díky,
Otto

Jan Endel
Člen | 1016
+
0
-

Založ si vlastní téma.

//my mistake => pondelni rano

Editoval pilec (2. 7. 2012 11:56)

baz
Člen | 42
+
0
-

proč vlastní téma?
tohle jsem zakládal já a pořád je to o tom samém…

baz
Člen | 42
+
0
-

Je to popsané výše – pošlu dotaz z které potřebuju získat array of Article pomocí setRowClass().
Jenže problém je v tom, že v takto vytvořených objektech nemám jak definovat $this->db abych tam přenesl aktuální spojení.

Proto tam je fce map a mapConnection, která tohle vyřeší.
Jenže teď jsem zjistil, že na PHP 5.2. to asi nefunguje (na PHP 5.3. to běží správně).

Takže pokud lze poupravit část okolo fce array_map tak to mi stačí.
Pokud ovšem tohle v 5.2. vůbec neběží, potřeboval bych poradit jak do nově vzniklého objektu předat connection (konstuktor upravit nemůžu, protože ho nevolám já, ale DIBI a to nečeká víc, než jeden parametr)

Milo
Nette Core | 1283
+
0
-

Zkus

array_map(array($this, 'mapConnection'), ...);
baz
Člen | 42
+
0
-

Geniální :-) – funguje, díky moc.

Musím se ale přiznat, že když jsem viděl, náhradu callback za array, tak jsem nevěřil, že to zabere.
Už kvůli chybové hlášce „should be NULL or valid callback“

Ale běží to…