Dvojite vztahy mezi tabulkami

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

Ahoj,
resim pripad, podobny strukture https://doc.nette.org/…ase/explorer
Konkretne:

V tabulce Author je vazba na Adresu autora/maintainera.

CREATE TABLE `address` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT;


CREATE TABLE `author` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) NOT NULL,
  `address_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `address_id` (`address_id`),
  CONSTRAINT `author_ibfk_1` FOREIGN KEY (`address_id`) REFERENCES `address` (`id`)
) ENGINE=InnoDB DEFAULT;

Kdyz chci zobrazit adresy autora a maintainera, tak mi to spatne vygeneruje vazbu do tabulky Address.


table(book)->select(title, author.address.name, maintanier.address.name)

SELECT `title`,`address`.`name`,`address`.`name`
FROM `book`
LEFT JOIN `author` ON `book`.`author_id` = `author`.`id`
LEFT JOIN `address` ON `maintainer`.`address_id` = `address`.`id`
LEFT JOIN `author` AS `maintainer` ON `book`.`maintainer_id` = `maintainer`.`id`

Ocekaval bych tohle:

SELECT `title`,`author_address`.`name`,`maintainer_address`.`name`
FROM `book`
LEFT JOIN `author` ON `book`.`author_id` = `author`.`id`
LEFT JOIN `author` AS `maintainer` ON `book`.`maintainer_id` = `maintainer`.`id`
LEFT JOIN `address` AS `author_address` ON `author`.`address_id` = `author_address`.`id`
LEFT JOIN `address` AS `maintainer_address` ON `maintainer`.`address_id` = `maintainer_address`.`id`

Je to featura nebo bug? Delam neco spatne?

Felix
Nette Core | 1270
+
0
-

Uplne si nejsem jisty, ale co tohle:

table(book)->select(title, author.address.name AS author_address_name, maintanier.address.name AS maintainer_address_name)

Davas tam 2 sloupecky se stejnym jmenem. Ale tezko rict, jestli je to tim.

jednou
Člen | 14
+
0
-

Pridal jsem aliasy. Error hlaska stale stejna:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'maintainer.address_id' in 'on clause'

vygenerovane sql:

SELECT `title`,`address`.`name` AS `author_address`,`address`.`name` AS `maintainer_address`
FROM `book`
LEFT JOIN `author` ON `book`.`author_id` = `author`.`id`
LEFT JOIN `address` ON `maintainer`.`address_id` = `address`.`id`
LEFT JOIN `author` AS `maintainer` ON `book`.`maintainer_id` = `maintainer`.`id`
Felix
Nette Core | 1270
+
0
-

Muzes mi prosim ukazat jak ten dotaz presne skladas? A taky vyexportovat presne DB? Mozna nemas nastaveny cizi klic nebo treba jinak pojmenovane sloupecek. Nebo ne, jenom premyslim nahlas.

jednou
Člen | 14
+
0
-

Ok, pridávám tedy SQL pro tabulku Book:

CREATE TABLE `book` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `author_id` int(11) NOT NULL,
  `maintainer_id` int(11) NOT NULL,
  `title` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `author_id` (`author_id`),
  KEY `maintainer_id` (`maintainer_id`),
  CONSTRAINT `book_ibfk_1` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`),
  CONSTRAINT `book_ibfk_2` FOREIGN KEY (`maintainer_id`) REFERENCES `author` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Volam to v presenteru takhle:

$res = $this->database->table("book")->select("title,author.address.name AS author_address,maintainer.address.name AS maintainer_address");
$a = $res->fetch();

Editoval jednou (27. 6. 2016 13:54)

Felix
Nette Core | 1270
+
0
-

Tak jsem si to pro jistotu zkusil i u sebe a je fakt ze to nejde. Ale nejsem si jisty, ze je to bug. To by musel odpovedet nekdo kdo nette/database zna do hloubky. (@dg, @DavidMatějka, @hrach, ..)


Tohle projde v pohode, ale udela to 5 dotazu.

foreach ($context->table('book') as $book) {
	echo $book->author->address->name;
	echo $book->maintainer->address->name;
}
  • SELECT id, author_id, maintainer_id FROM book
  • SELECT id, address_id FROM author WHERE (id IN (1))
  • SELECT id, name FROM address WHERE (id IN (1))
  • SELECT id, address_id FROM author WHERE (id IN (2))
  • SELECT id, name FROM address WHERE (id IN (2))

To je asi nezadouci.

Unlink
Člen | 298
+
0
-

S najnovšou nette database sa to dá spraviť takto

$res = $this->database->table("book")
->select("title,author.address.name AS author_address,ma.name AS maintainer_address")
->alias('maintainer.address', 'ma');
$a = $res->fetch();

Výsledné SQL

SELECT `title`, `address`.`name` AS `author_address`, `ma`.`name` AS `maintainer_address`
FROM `book`
LEFT JOIN `author` `author` ON `books`.`author_id` = `author`.`id`
LEFT JOIN `address` ON `author`.`address_id` = `address`.`id`
LEFT JOIN `author` `maintanier` ON `books`.`maintanier_id` = `maintanier`.`id`
LEFT JOIN `address` `ma` ON `maintanier`.`address_id` = `ma`.`id`

V staršej by tvoj kód fungovať mal, akú verziu presne používaš?

Editoval Unlink (27. 6. 2016 14:13)

jednou
Člen | 14
+
0
-

Mám Nette Framework 2.2.7 (released on 2015–01–06), ktery vraci:

Call to undefined method Nette\Database\Table\Selection::alias()

Zkusim tedy upgrade, zatím díky.

Editoval jednou (27. 6. 2016 14:28)

jednou
Člen | 14
+
0
-

RC verzi na produkci dát nemůžu. Nevíte jak to rozchodit v Nette-2.3.10?

norbe
Backer | 408
+
0
-

Ono to bude fungovat v jakékoli verzi, ale nesmíš tam mít překlep, aspoň teda předpokládám, nezkoušel jsem :-) maintanier vs maintainer

Edit: Blbost špatně jsem se podíval na to kde je problém. Podle mne to můžeš obejít tak, že místo druhého address dáš jen addres, tzn.

$this->database->table("book")->select("title,author.address.name AS author_address,maintainer.addres.name AS maintainer_address");

Editoval norbe (27. 6. 2016 15:41)

jednou
Člen | 14
+
0
-

@norbe: S pouzitim „addres“ misto „address“ to obejit nejde. Výsledek:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'address.name' in 'field list'

SELECT `title`,`address`.`name` AS `author_address`,`addres`.`name` AS `maintainer_address`
FROM `book`
LEFT JOIN `author` ON `book`.`author_id` = `author`.`id`
LEFT JOIN `address` AS `addres` ON `maintainer`.`address_id` = `addres`.`id`
LEFT JOIN `author` AS `maintainer` ON `book`.`maintainer_id` = `maintainer`.`id`
blaztar
Člen | 93
+
0
-

Z dokumentace:
Hlavní myšlenkou je načítání dat pouze z jedné tabulky a tak, aby se tyto dotazy pokládaly jen jednou.

Což bych převedl na:

function renderBook()
{
	$this->template->book = $this->db->table('book')->get(1); // id záznamu
}
title: {$book->title}

author: {$book->author->name}
address: {$book->author->address->name}

maintainer: {$book->ref('author', 'maintainer_id')->name}
maintainer: {$book->ref('author', 'maintainer_id')->address->name}
Mysteria
Člen | 797
+
0
-

blaztar: To je samozřejmě pěkné, ale jenom do chvíle, kdy je to třeba vypsáno v nějaké tabulce a chceš uživatelům umožnit řazení třeba podle města autora a podobně. Pak už ten JOIN udělat musíš.

blaztar
Člen | 93
+
0
-

Jen píšu na co je Nette Database stavěná, ale třeba to půjde.

Nebo použít query:

$this->db->query("
	SELECT title, address.name AS author_address, ma.name AS maintainer_address
	FROM book
	LEFT JOIN author author ON book.author_id = author.id
	LEFT JOIN address ON author.address_id = address.id
	LEFT JOIN author maintainer ON book.maintainer_id = maintainer.id
	LEFT JOIN address ma ON maintainer.address_id = ma.id
")->fetchAll();

Editoval blaztar (27. 6. 2016 19:14)

Unlink
Člen | 298
+
0
-

Ono to je zrejme bug, keď nájde že má druhý krát joinovať address cez adress_id z tabuľky author tak to prepíše a neberie ten chain v úvahu.

Práve v poslednej verzii (áno, je to ešte RC) pridal @norbe tieto aliasi, pomocou ktorých sa to dá obísť.

jednou
Člen | 14
+
0
-

Je nejaka sance dodelat alias() v Nette-2.3.10? Má cenu to někam reportovat?

CZechBoY
Člen | 3608
+
0
-

@jednou No buď si forkneš Nette/Database a doděláš si tam podporu nebo použiješ 2.4 RC nebo můžeš použít https://github.com/…rts/database

Unlink
Člen | 298
+
+1
-

Keď sa k tomu dostanem tak skúsim pozrieť prečo to nefunguje automaticky.