Jak zachytit 2 různé UniqueConstraintViolationException

popcorn
Člen | 28
+
0
-

Ahoj, chtěl bych zachytit dva různé duplicitní klíče v Db pomocí

catch (Nette\Database\UniqueConstraintViolationException $e)

Ale nevím, kde použít jméno toho klíče,
"":http://prntscr.com/ov7lzx protože bych chtěl pokaždé vyhodit jiný error.
Něco jako:

catch (Nette\Database\UniqueConstraintViolationException['name'] $e) {
			throw new Exceptions\DuplicateNameException;
		} catch (Nette\Database\UniqueConstraintViolationException['email'] $e) {
			throw new Exceptions\DuplicateEmailException;
		}

^^ Což je nesmysl.

Díky za všechny rady. ;)

Hezký večer.

David Matějka
Moderator | 6445
+
0
-

chytej to jen jednou a dle obsahu te vyjimky (asi budes muset nejak parsovat error message) se rozhodnes, co udelas

filsedla
Člen | 101
+
0
-

Ahoj. Ano, ta exception opravdu název sloupce samostatně nikde neobsahuje, tudíž budeš parsovat error message. Příklad:

        try {
			...
        } catch (UniqueConstraintViolationException $e) {
            $matches = Strings::match($e->errorInfo[2], "#Duplicate entry '.*' for key '(.*)'#");
            $columnName = Arrays::get($matches, 1);
            switch ($columnName) {
                case 'name':
                    ...
                    break;
   				case 'email':
                    ...
                    break;
                default:
                    throw new InvalidStateException("Unimplemented case: '{$columnName}'");
            };
        }

Nicméně, používat takový kód se nedá doporučit.

V realitě je lepší se exception vyhnout a předem kontrolovat, jestli náhodou už kolidující záznam neexistuje, a když ano, tak insert nedělat.

Co víc, reálně často ani nemůže být v databázi např. na sloupci email nebo name UNIQUE constraint, protože aspoň na databázové úrovni data v těchto sloupcích nemohou být unikátní.

Editoval filsedla (20. 8. 2019 20:33)

popcorn
Člen | 28
+
0
-

filsedla napsal(a):

Nicméně, používat takový kód se nedá doporučit.

V realitě je lepší se exception vyhnout a předem kontrolovat, jestli náhodou už kolidující záznam neexistuje, a když ano, tak insert nedělat.

No já myslel, že to kontroluji právě těmi exceptions a pak vypíšu, že ten záznam již existuje.

Co víc, reálně často ani nemůže být v databázi např. na sloupci email nebo name UNIQUE constraint, protože aspoň na databázové úrovni data v těchto sloupcích nemohou být unikátní.

Proč by nemohly být unikátní? Já nechci mít dva uživatele např. „test“ a také chci mít pouze jeden e-mail na účet.

filsedla
Člen | 101
+
0
-

popcorn napsal(a):

No já myslel, že to kontroluji právě těmi exceptions a pak vypíšu, že ten záznam již existuje.

Proč by nemohly být unikátní? Já nechci mít dva uživatele např. „test“ a také chci mít pouze jeden e-mail na účet.

Hm, no tak jo. Asi k tomu ty exceptions jsou.

Zajímalo by mě, co si o tom myslí ostatní.

Psal jsem ze svojí zkušenosti kvůli tomu, že já většinou UNIQUE constraint nemůžu mít vůbec. Je to kvůli smazaným záznamům, například v aplikaci sice může být jenom jeden účet s určitým emailem, ale v databázi mohou být také smazané účty (deleted=1) se stejným emailem.

Závisí na tom, co stavíš.

Editoval filsedla (20. 8. 2019 21:06)

popcorn
Člen | 28
+
0
-

Nojo, to máš pravdu, já zase používám unique klíče docela často, protože záznamy, který chci vymazat mažu rovnou. Třeba časem dospěju k tomu, že je budu potřebovat, tak to začnu řešit jinak :)

Jak kontroluješ ty kolidující záznamy jak jsi psal?

David Matějka
Moderator | 6445
+
+1
-

@filsedla to se necha vyresit treba partial unique indexama (to asi nepodporuje mysql), slozenyma unique constrainama, kde jeden sloupec bude nullable (ty se nepocitaji do unikatnosti) nebo treba unique indexama s expression (mysql 8+)

filsedla
Člen | 101
+
-1
-

popcorn napsal(a):
Jak kontroluješ ty kolidující záznamy jak jsi psal?

Jenom takhle (příklad, když chci vložit nový záznam s $email):

$row = $this->database->where('email', $email)->fetch();
if ($row){
   // Error, return / throw / ...
}
// else
$this->database->insert(['email' => $email, ...]);
popcorn
Člen | 28
+
0
-

^ To jsem myslel, že by zbytečně zpomalovalo systém, jelikož musí proběhnout 2 SQL dotazy namísto jednoho (Jasně, zanedbatelný čísla nejspíš, ale stejně)

filsedla
Člen | 101
+
+1
-

Potom teda kód se selectem navíc má ještě jednu nevýhodu (oproti přístupu s exceptions): je tam hazard, že jiný proces může vložit znovu stejný email zrovna v okamžiku, kdy už první proces otestoval, že tam žádný takový email není, ale ještě ho nestihl vložit. To se pak musí řešit.

Editoval filsedla (20. 8. 2019 22:00)

filsedla
Člen | 101
+
0
-

@DavidMatějka Takže ty by sis radši udělal třeba ten složený UNIQUE constraint a chytal UniqueConstraintViolationException?

CZechBoY
Člen | 3608
+
0
-

unique constraint ti zaruci db konzistenci jak chces
pri pouziti select + insert si myslim ze ti vznikne race condition, ale to te mize a nemusi trapit