Existuje alternativa setRowClass (z DIBI) v Nette\Database?
- baz
- Člen | 42
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
- jtousek
- Člen | 951
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
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
Opravdu to nemá řešení?
Pokud tohle nejde, tak mi nezbyde nic jiného, než to nějak nehezky obejít:
- v konstruktoru třídy Manager si vytáhnout nějakou globální proměnnou, kde bude DB (třeba z aktuálního presenteru)
- 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ž?
- baz
- Člen | 42
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
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
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
$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
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
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
- baz
- Člen | 42
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)