DiscoveredReflection: join přes dvě tabulky v select

- buff
 - Člen | 63
 
Ahojte. Mám následující databázi:
Book (id, name, author_id)
Author (id, name, country_id)
Country (id, name)
Používám DiscoveredReflection (musím; ve skutečnosti se ty
sloupečky nejmenují tak pěkně, to jen zde pro názornost).
Tohle funguje:
foreach ($db->table('Book') as $row) {
	echo $row->ref('author_id')->ref('country_id')->name;
}
Tohle nefunguje
(No reference found for $author_id->country_id.):
foreach ($db->table('Book')->select('author_id.country_id.name') as $row) {
	echo $row->name;
}
Existuje nějaký způsob, jak druhý zápis zfunkčnit? Tedy odkázat se na
druhou cizí tabulku přímo ve volání select? Jde mi
o použití v NiftyGridu.
Děkuji!

- buff
 - Člen | 63
 
Ahoj. Díky za odpověď!
Tak to funguje s ConventionalReflection, ale ne s
DiscoveredReflection. Tedy kupodivu mi to v tomto modelovém
příkladu zafungovalo i s Discovered, ale problém je v tom, že ten sloupec
s cizím klíčem se ve skutečnosti nejmenuje country_id, ale nějak
libovolně, např fk_cntr_id. A pak už to tímto zápisem nejde.

- hrach
 - Člen | 1844
 
ne, funguje to tak, ze napises cely nazev sloupce, nebo nejaky jeho fragment
(substring). takze pokud se sloupec bude jmenovat fk_cntr_id, tak
toto bude fungovat:
$row->cntr->name||select('author.cntr.name')$row->fk_cntr->name||select('author.fk_cntr.name')$row->fk_cntr_id->name||select('author.fk_cntr_id.name')$row->k_cnt->name– nedoporucuji, prasarna, nema logiku

- hrach
 - Člen | 1844
 
No jasny. Jsem to napsal blbe… Pokud napises kompletni nazev sloupce, tak samozrejme to vrati hodnotu danyho klice.
$row->ref('cntr')->name || $row->cntr->name || select('author.cntr.name')$row->ref('fk_cntr')->name || $row->fk_cntr->name || select('author.fk_cntr.name')$row->ref('fk_cntr_id')->name || select('author.fk_cntr_id.name')$row->ref('k_cnt')->name || $row->k_cnt->name– nedoporucuji, prasarna, nema logiku
Editoval hrach (21. 12. 2012 0:22)

- buff
 - Člen | 63
 
Jasně. Už je to zmatek, pardon. Napíšu znovu (pozměněnou) otázku. :-)
Mám databázi:
Book (id, name, fk_id_athr)
Author (id, name, fk_id_cntr)
Country (id, name)
fk_id_athr je cizí klíč do tabulky Author, fk_id_cntr je cizí klíč do tabulky Country.
A mám následující kód:
<?php
$table = $this->context->database->table('book');
foreach($table as $row) {
	echo $row->ref('fk_id_athr')->ref('fk_id_cntr')->name; //vypíše název země
	echo $row->id_athr->id_cntr->name; //vypíše název země
}
$table = $this->context->database->table('book')
	->select('id_athr.id_cntr.name');
foreach($table as $row) {
	echo $row->name; //PDO exception: No reference found for $id_athr->id_cntr.
}
?>
Čili „šipková“ notace už mi funguje perfektně, „tečková“ notace ve volání metody select však ne. Co dělám špatně?

- buff
 - Člen | 63
 
Toto ->select('id_athr.id_cntr.name'); je přesně to, co
jsem tam měl původně.
A toto ->select('athr.cntr.name'); bohužel padá
s analogickou
hláškou No reference found for $athr->cntr.
Takže je tam bug?
Ty názvy sloupců jsem vymyslel já, abych vám aspoň zlehounka nastínil, jaká je situace v té reálné databázi ;-)

- vvoody
 - Člen | 910
 
Myslím že som našiel bug v SqlBuilder->buildJoins().
Testoval som na kode:
$table = $c->table('book')->select('book.*, athr.name AS aname, athr.cntr.name AS cname');
foreach ($table as $row) {
	dump($row);
}
a dátach:
DROP TABLE IF EXISTS `author`;
CREATE TABLE `author` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `fk_id_cntr` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_id_cntr` (`fk_id_cntr`),
  CONSTRAINT `author_ibfk_1` FOREIGN KEY (`fk_id_cntr`) REFERENCES `country` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
INSERT INTO `author` (`id`, `name`, `fk_id_cntr`) VALUES
(1,	'Pišta',	1);
DROP TABLE IF EXISTS `book`;
CREATE TABLE `book` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `fk_id_athr` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_id_athr` (`fk_id_athr`),
  CONSTRAINT `book_ibfk_1` FOREIGN KEY (`fk_id_athr`) REFERENCES `author` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
INSERT INTO `book` (`id`, `name`, `fk_id_athr`) VALUES
(1,	'Islo vajce na vandrovku',	1);
DROP TABLE IF EXISTS `country`;
CREATE TABLE `country` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
INSERT INTO `country` (`id`, `name`) VALUES
(1,	'Slovensko');
Problém bol celkom jasný: No reference found for $athr->cntr
Prečo metóda $reflection->getBelongsToReference($parent, $name) dostala ako
parameter $paren = ‚athr‘ namiesto správneho ‚author‘. Tieto úpravy mi
to fixli, ale netrufam si povedať či to niečo ine nerozoserie.
protected function buildJoins($val, $inner = FALSE)
{
	$reflection = $this->selection->getConnection()->getDatabaseReflection();
	$joins = array();
	preg_match_all('~\\b([a-z][\\w.:]*[.:])([a-z]\\w*|\*)(\\s+IS\\b|\\s*<=>)?~i', $val, $matches);
	foreach ($matches[1] as $names) {
		// v prvej iteracii bude parent a alias v ON podmienke totozny
		$alias = $parent = $this->selection->getName();
		if ($names !== "$parent.") { // case-sensitive
			preg_match_all('~\\b([a-z][\\w]*|\*)([.:])~i', $names, $matches, PREG_SET_ORDER);
			foreach ($matches as $match) {
				list(, $name, $delimiter) = $match;
				if ($delimiter === ':') {
					list($table, $primary) = $reflection->getHasManyReference($parent, $name);
					$column = $reflection->getPrimary($parent);
				} else {
					list($table, $column) = $reflection->getBelongsToReference($parent, $name);
					$primary = $reflection->getPrimary($table);
				}
				$joins[$name] = ' '
					. (!isset($joins[$name]) && $inner && !isset($match[3]) ? 'INNER' : 'LEFT')
					. ' JOIN ' . $this->driver->delimite($table) . ($table !== $name ? ' AS ' . $this->driver->delimite($name) : '')
					// before:
					// . ' ON ' . $this->driver->delimite($parent) . '.' . $this->driver->delimite($column)
					// after
					. ' ON ' . $this->driver->delimite($alias) . '.' . $this->driver->delimite($column)
					. ' = ' . $this->driver->delimite($name) . '.' . $this->driver->delimite($primary);
				// treba rozlisovat realny nazov tabulky potreby pre reflection od aliasu v ON podmienke
				$alias = $name;
				$parent = $table;
			}
		}
	}
	return $joins;
}
				
- hrach
 - Člen | 1844
 
jasny, to je ten bug, kterej sem uz fixnul tady: https://github.com/…5731414c9dc0
budu se na to muset znovu podivat a trochu do dopracovat :) (to rfc, ktery bude obsahovat i toto…)

- hrach
 - Člen | 1844
 
Zjistil jsem, ze jsem to uz u sebe asi opravoval driv,
takze to mam dokonce mimo ten velkej refactoring: tady je ten dulezity
commit:
https://github.com/…f6f167dd5c22
edit: https://github.com/…tte/pull/912
Editoval hrach (22. 12. 2012 14:35)