DiscoveredReflection: join přes dvě tabulky v select

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

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!

vvoody
Člen | 910
+
0
-
foreach ($db->table('Book')->select('author.country.name') as $row) {
 	echo $row->name;
}

(strielam len od pása)

buff
Člen | 63
+
0
-

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

no ano, pak musis napsat author.cntr.name, to .country. je stejna, jako bys volal $row->country->name, respektive $row->cntr->name. Idealni prilezitost nemiz zprasene sloupce v db ;)

buff
Člen | 63
+
0
-

Díky!

Převzatou databázi si nevybereš. ;-) Mě to taky netěší.

Jak to teda funguje? Když ten cizí klíč končí na _id, tak to napíšu bez _id; a když nekončí na _id, tak napíšu jeho celý název? Proč má to _id výjimku?

hrach
Člen | 1834
+
0
-

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
buff
Člen | 63
+
0
-

Perfektní. Díky moc za vysvětlení. Proč mi ale tedy nefunguje to, co mám v původní otázce?

Totiž, teď si s tím hraju a toto funguje:

$row->ref('fk_id_athr')->ref('fk_id_cntr')->name;

Zatímco toto nefunguje:

$row->fk_id_athr->fk_id_cntr->name;

hrach
Člen | 1834
+
0
-

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)

vvoody
Člen | 910
+
0
-

Pre lepšie zorientovanie môžeš sem napísať sql dump? Minimálne všetky FK, PK a názvy tabuliek v ktorých sa nachádzajú.

buff
Člen | 63
+
0
-

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ě?

hrach
Člen | 1834
+
0
-

A co toto?

->select('book.id_athr.id_cntr.name');

Mozna tam bude chyba, vim ze sem neco opravoval a jeste to nebylo mergnuto, pac to bylo ve vetsim rfc.

buff
Člen | 63
+
0
-

Ne, to nezabralo – PDOException: No reference found for $book->book. (to je ale OK, ne?)

vvoody
Člen | 910
+
0
-
->select('athr.cntr.name');

?

edit: :D kto preboha vymýšľal tie názvy FK?

edit2: a ak takto upravíme to od hracha?

->select('id_athr.id_cntr.name');

Editoval vvoody (21. 12. 2012 1:47)

buff
Člen | 63
+
0
-

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

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

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…)

buff
Člen | 63
+
0
-

Aha. Díky moc, pánové. Myslíte, že se to někdy v dohledné době dostane do Nette, nebo si to tam mám přidat ručně?

hrach
Člen | 1834
+
0
-

jdu se na to podivat a pripravit opravu. snad by to tedy mohlo projit.

vvoody
Člen | 910
+
0
-

Upraviť Nette určite neupravuj. Úpravu db zrejme nechceš na rozbehnutom projekte. Posledná možnosť (než bude fix v Nette) je si napísať vlastný reflection ktorý bude presne vyhovovať tvojej db a aj tomu bugu aby sa neprejavil :D

hrach
Člen | 1834
+
0
-

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)

buff
Člen | 63
+
+1
-

Je to tam. Tak děkuju moc ještě jednou.