Nette/Database (NotORM) a editace M:N vazeb

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

Ahoj,
potřeboval bych nějaké typy k M:N vazbám.
Je to sice popisováno v každém článku o NotORM, nicméně všude se řeší jen čtení a nedaří se mi dopracovat k efektivnímu řešení editace, vkládání a mazání.

mám následující tabulky:

user: id, name, ...
property: id, name, ...
user_property: user_id, property_id, value

Tabulka user obsahuje řádově stovky tisíc řádků, a každý user má desítky až stovky properties, proto potřebuji minimalizovat počet pokládaných dotazů.
Změny se provádějí formou „vlastnosti uživatele uveď do stavu:“.

Rád bych se dostal k následujícím SQL dotazům:

// načtu skupinu uživatelů kterou upravuji
SELECT * FROM user WHERE id IN (3, 5, 8, 9, ...)
// ideálně: načtu vazby pro celou skupinu
SELECT * FROM user_property WHERE user_id IN (3, 5, 8, 9, ...)
// případně: pro každého uživatele načtu jeho vazby
SELECT * FROM user_property WHERE user_id = 3
// prováděné změny zaměřit podle kombinace user_id + property_id (které reálně tvoří primární klíč)
// je to vůbec možné, nebo je nutné doplnit (informačně zbytečný) sloupec "id"?
UPDATE user_property SET value = 'hodnota' WHERE user_id = 3 AND property_id = 13
// kvůli cizím klíčům ověřit existenci příslušných properties
SELECT * FROM property WHERE id IN (15, 16, ...)
// vytvořit záznamy pro chybějící property
INSERT INTO property (id) VALUES (15), (16), ...
// v jediném příkazu vložit všechny nové záznamy
INSERT INTO user_property (user_id, property_id, value) VALUES (3, 15, 'hodnota'), (3, 16, 'hodnota'), ...
// ideálně: všechny rušené záznamy odstranit v jediném příkazu
DELETE FROM user_property WHERE (user_id = 3 AND property_id = 11) OR (user_id = 3 AND property_id = 14) ...
// případně: záznamy rušit v blocích podle uživatele
DELETE FROM user_property WHERE user_id = 3 AND property_id IN (11, 14, ...)

Nedělám si iluze, že tohle zvládnu bez potřeby poskládat si dotaz ručně. Nicméně předpokládám, že něco z toho NotORM dokáže samo :-)

vrana
Člen | 131
+
0
-

Ideální use case pro NotORM:

<?php
$delete = array();

// načtu skupinu uživatelů kterou upravuji
foreach ($db->user("id", array(3, 5, 8, 9)) as $user) {

	// ideálně: načtu vazby pro celou skupinu
	$user_properties = $user->property();
	foreach ($user_properties as $property) {
	}

	// prováděné změny zaměřit podle kombinace user_id + property_id (které reálně tvoří primární klíč)
	$user_properties->where("property_id", 13)->update(array(
		"value" => "hodnota",
	));

	// kvůli cizím klíčům ověřit existenci příslušných properties
	$properties = $db->property(array(15, 16));

	// vytvořit záznamy pro chybějící property
	$properties->insert(
		array("id" => 15),
		array("id" => 16)
	);

	// v jediném příkazu vložit všechny nové záznamy
	$user_properties->insert(
		array("property_id" => 15, "value" => "hodnota"),
		array("property_id" => 16, "value" => "hodnota")
	);

	$delete[] = new NotORM_Literal("(3, 11)");
	$delete[] = new NotORM_Literal("(3, 14)");

	// případně: záznamy rušit v blocích podle uživatele
	// $user_properties->where("property_id", array(11, 14))->delete();
}

// ideálně: všechny rušené záznamy odstranit v jediném příkazu
$db->user_property("(user_id, property_id)", $delete)->delete();
?>

Ty properties bych si načetl před začátkem cyklu všechny do pole, které bych vevnitř cyklu udržoval, ať se pokaždé nemusí načítat znovu.

Michal Blaha
Člen | 2
+
0
-

Díky za vyčerpávající řešení.
Jako na potvoru používám Nette\Connection a to zkolabuje na tomhle místě:

vrana napsal(a):

<?php
      $user_properties->where("property_id", 13)->update(array(
              "value" => "hodnota",
      ));
?>

NotORM funguje bez problémů :-).
Sice se vynořilo několik jiných problémů spojených s používáním PostgeSQL, ale týká se to jen formátování dotazů … s tím se přesunu na github.

A něco postřehů z vlastního postupu:

  • Vazební tabulku jsem doplnil o sloupec id a pomocí triggeru ho nastavuji na text: $id1_$id2. Takže jsem schopen si celý výsledek načíst do pole a pak k tomu přistupovat podle klíčů
  • Přičemž hledání podle textového sloupce je zhruba stejně rychlé jako hledání podle 2 sloupců číselných. (pro potřeby editace, nebo mazání)

… čímž se zbavím poměrně magického načítání vazebních tabulek

Editoval Michal Blaha (2. 5. 2011 1:15)

David Grudl
Nette Core | 8157
+
0
-

Michal Blaha napsal(a):

a to zkolabuje na tomhle místě:

Co myslíš tím zkolabuje?

Oggy
Člen | 306
+
0
-

vrana napsal(a):

<?php
> 	$user_properties->where("property_id", 13)->update(array(
> 		"value" => "hodnota",
> 	));
?>

při pokusu updatnout M:N záznam dostávám pořád NOTICE o neexistenci indexu row[‚id‘] ..

např.

<?php
foreach($application->application_tag() as $application_tag) {
	$application_tag->update(array('neco'=>5,'necojineho'=>4));
}
?>

vyžaduje to mít primární klíč id..zde je primarni klic kombinaci application_id,tag_id ..

je chyba někde u mě? nebo je toto požadované chování?

Editoval Oggy (11. 1. 2012 22:52)