Ukládání do více tabulek v jednu chvíli

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

Zdravím, zatím jenom tak s Nette blbnu. Možná se to netýká ani tak přímo Nette, jako samotného MVP.

Zajímalo by mě, jaký je nejlepší způsob ukládání dat z formuláře do více tabulek zároveň. Když to uvedu bodově na příkladu uživatelských profilů:

  • Mám tabulku user, kde jsou základní informace o uživateli důležité k přihlášení a dalšímu fungování v aplikaci
  • Potom je tu tabulka profile, kde jsou doplňující informace – jméno a popis uživatele. S uživatelem je spojená indexem user_id
  • Dále tabulky avatar, profile_contact a contact_kind, ty už jsou pro teď nepotřebné.

No a jde o to, že si najednou vůbec nejsem jistý, jakým způsobem zapisovat hodnoty např. registračního formuláře do těchto tabulek zaráz tak, abych si pokud možno nezprasil kód. Samotný formulář mám napsaný takto:

<?php

public function createComponentProfileForm()
        {
                $form = new AppForm();

                $form->addText('login', 'Username:')
                        ->addRule(Form::FILLED, 'Please provide an username.');
                $form->addText('email', 'E-mail:')
                        ->addRule(Form::FILLED, 'Please provide an e-mail.')
                        ->addRule(Form::EMAIL, 'Please provide valid e-mail.');
                $form->addPassword('password', 'Password:')
                        ->addRule(Form::FILLED, 'Please provide a password.');
                $form->addSelect('role', 'Level:', array('registered' => 'Registered', 'editor' => 'Editor', 'admin' => 'Admin'))
                        ->skipFirst('Select user level')
                        ->addRule(Form::FILLED, 'Please select user level');

                $form->addText('name', 'Name:');
                $form->addTextArea('description', 'Description:');

                $form->addSubmit('save', 'Save');
                $form->onSubmit[] = callback($this, 'profileFormSubmitted');

                return $form;
        }

?>

Můžu si v modelu udělat na vložení jednu metodu a v ní potom konkrétně vybrat, které hodnoty kam uložit, ale to se mi zdá právě blbě, „neuniverzální“. Nebo můžu mít na každou tabulku v modelu zvlášť vkládací metodu, ale to se mi taky nějak nezdá. Takže otázka – můžete se podělit o nějaký svůj ukázkový model řešící takto navrhlé tabulky?

Tabulky:

Btw. Když už píšu, lze při vkládání do tabulky zjistit id právě vloženého záznamu, když je sloupec id auto_increment? Díky.

iguana007
Člen | 970
+
0
-

Já jsem zrovna nedávno řešil podobnou věc a udělal jsem to vše v jedné metodě v modelu.

Měl jsem případ, kdy ukládám nový projekt a k němu vytvářím i novou kategorii v článcích a do ní několik článků a to vše tolikrát, kolik je v systému nastavených jazyků (přes foreach).
Poslední id zjistíš takto:
https://forum.dibiphp.com/…st-insert-id

Foowie
Člen | 269
+
0
-

Já to řeším, že údaje dělím do FormContainerů podle toho, do jaké tabulky výsledná data patří.
V modelu to pak je snadno rozdělitelné.

Muhal
Člen | 4
+
0
-

Díky vám oběma :)

V modelu to pak je snadno rozdělitelné.

Můžeš prosím napsat, jak to v modelu rozdělit? Mám v hlavě nápady, ale znovu, nezdají se mi být tak úplně OK :) V dokumentaci ani na fóru jsem nic moc nenašel.

Foowie
Člen | 269
+
0
-
// tvorba formu
$form = new AppForm($this, $name);

$userContainer = $form->addContainer("user");
$userContainer->addText("login", "Login");
...
$profileContainer = $form->addContainer("profile");
$profileContainer->addText("name", "Name");
...
// po submitu
$values = $button->getForm()->getValues();
$userValues = $values["user"];
$profileValues = $values["profile"];
dibi::query("UPDATE user SET %a", $userValues);
dibi::query("UPDATE profile SET %a", $profileValues);

Tady máš nástřel jak by to mohlo vypadat …
PS: Neříkám že je to nějak propracovaný návrh, ale je to rychlé .)

Editoval Foowie (27. 7. 2010 12:46)

Muhal
Člen | 4
+
0
-

Díky moc!

Bernard Williams
Člen | 207
+
0
-

Nazdárek,

@Foowie: Jak v takto navrženém modelu kontroluješ chyby a ošetřuješ výjimky?

@Muhal: V čem si, prosím tě, generoval to schéma tabulku? Viděl jsem to tu už několikrát, ale nikde jsem nezjistil ten program.

Děkuji
Bernard

Editoval Bernard Williams (27. 7. 2010 13:50)

Muhal
Člen | 4
+
0
-

Je to prográmek MySQL Workbench.

Foowie
Člen | 269
+
0
-
... model

if(validační chyba)
	throw new ModelException("Popis chyby");

try {
	db::beginTransaction();

	try {
		velmi_nebezpečná_instrukce
	} catch(JináVýjimka $x) {
		throw new ModelException("Popis chyby");
	}

	db::commit();
} catch(ModelException $e) {
	db::rollback();
	throw $e;
}

on submit:

try {
	MujModel->upravHodnoty($button->getForm()->getValues());
	$this->flashMessage("Všechno OK");
	invalidateControl / redirect
} catch(ModelException $e) {
	$this->flashMessage($e->getMessage());
	invalidateControl...
}

Ve skutečnosti je to ještě trošku jinak ale pro přehlednost nějak takhle ;)

Foowie
Člen | 269
+
0
-

Pokud to v databázi nastavuješ jako cizí klíč tak není.

koprkuba
Člen | 24
+
0
-

Ahoj, mám takovýhle problém:
Mám dvě tabulky:

Objednavky(`id_objednavky` auto_increment, `id_zakaznika`); PK = `id_objednavky`;
Obsah_objednavky(`id_objednavky`,`id_produktu`,`pocet_kusu`); FK = `id_objednavky`=>`Objednavky.id_objednavky`;

Pokud zákazník vytvoří objednávku tak se vytvoří řádek v tabulce Objednavky kde je id_objednavky automaticky inkrementováno a jeho zákaznické ID.
Chtěl bych pak vložit dalších x (počet produktů v košíku) řádků s tím, že by id_objednavky z tabulky Objednavky bylo stejné jako to id_objednavky v Obsah_objednavky.

Zkoušel jsem to takhle, ale laděnka mi vyhodila error: Cannot retrieve last generated ID. na řádku 392.

Line 385:         * @return int
Line 386:         * @throws DibiException
Line 387:         */
Line 388:        public function getInsertId($sequence = NULL)
Line 389:        {
Line 390:            $this->connected || $this->connect();
Line 391:            $id = $this->driver->getInsertId($sequence);
Line 392:            if ($id < 1) throw new DibiException('Cannot retrieve last generated ID.');
Line 393:            return (int) $id;
Line 394:        }
Line 395:
Line 396:
Line 397:
Line 398:        /**
Line 399:         * Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().

Zde je můj kód:

$objednavky_model = new Objednavky();
$objednavky_model->insert(array('id_zakaznika' => $userID));
$id_objednavky = dibi::insertId();
$kosik = new Kosik();
$items = $kosik->getContent();

$obsahObjednavky_model = new ObsahObjednavky();
foreach ($items as $key => $item) {
	$data = array(
        	'id_objednavky' => $id_objednavky,
                'id_produktu' => $key,
                'pocet_kusu' => $item->count
	);
        $obsahObjednavky_model->insert($data)->execute();
}

díky moc

EDIT: Vloží mi to do Obsah_objednavky pouze jeden řádek (ten první produkt z košíku)

Editoval koprkuba (30. 12. 2010 18:44)

cuga
Člen | 210
+
0
-

@koprkuba: u Obsah_objednavky ti chybi neco jako Polozka_id, ktera by mela mit nastavene autoincrement…

jinak kdyz koukam na atributy tabulek, tak pokud jsi opravdu vypsal vsechny, doporucoval bych ti u polozky objednavky jeste evidovat cenu… protoze cena u produktu se muze v case menit, ale ceny v objednavkach by ti meli zustavat nemenne

koprkuba
Člen | 24
+
0
-

@cuga: super díky za radu, už to funguje. BTW bude to fungovat i v případě, že by 2 uživatelé zadali požadavek ve stejný čas a došlo by ke kolizi takové, že by uživatel A vytvořil řádek v tabulce Objednávky (potom by se z nějakého důvodu zpozdil) pak by uživatel B vytvořil řádek do Objednávky i řádky do Obsah_objednavky a až potom by uživatel A vytvořil řádky do Obsah_objednavky?

P.S. samozřejmě vypsal jsem jen to důležité jinak si v db. držím daleko více údajů včetně data a cen, právě proto, že mohou být produkty ve slevě atp.