Patrik Votoček

weBlog



Ověření unikátnosti ukládaných dat


Jak řešíte ukládání dat s ověřením unikátnosti záznamu?

Řešení první

Prvním řešením je zeptat se

$values = array('title' => "Test", 'slug' => "test", 'text' => "Lorem ipsum...");
if ($model->where("slug = ?", $values['slug'])->count("*") < 1) {
        $model->insert($values);
} else {
        /* $form['slug']->addError("..."); */
}

Problém

  • mezi ověřením a uložením mohlo dojít k uložení článku se slugem „test“ někým jiným

Řešení druhé

Druhým možným řešením je zachycení vyjímky při selhání

try {
        $model->insert(array('title' => "Test", 'slug' => "test", 'text' => "Lorem ipsum..."));
} catch (\PDOException $e) {
        $info = $e->errorInfo;
        if ($info[0] == 23000 && $info[1] == 19) {
                /* $form['slug']->addError("..."); */
        } else {
                throw $e;
        }
}

Problémy

  • kód 19 vrací SQLiteMySQL je to 1062, u jiné DB to zase bude jinak
    • napsat ověření pro všechny DB kód rapidně prodlouží
    • nestačí ověřovat chybový kód, ale musí se ověřovat i typ DB (v databázi A může chybový kód 19 znamenat něco jíného, než v databázi B)
  • v Doctrine 2 se vám tímto uzavře EntityManager

12 komentářů


Andrew

nový

Řešení třetí – zamknout tabulku a pak řešení jedna. Což má ale taky svoje mouchy.

Osobně mi ale nejlepší přijde řešení 2. V konkrétním projektu nad konkrétní databází se to dá ošetřit docela snadno, u něčeho multiplatformního už to je problém, to uznávám.

Andrew avatar

HosipLan

nový

Vyhození výjimky v prePersist myslím Doctrine EntityManager nezamyká, není ještě v transakci afaik. Nebyl bych tak paranoidní a zkontroloval těsně před vložením a dyštak zabil v prePersist pomocí nějakého listeneru (validator) :)

HosipLan avatar

mm-marek

nový

ve svém best of best cms (keep smiling) to řeší myšlenka ze mezi napsanim slugu a odeslanim je nějaký cas kde ptave může dojít ke kolizim. takze si slug „rezervuji“ ajaxove po napsani.

mm-marek avatar

Andrew

nový

[3] To ale přece vůbec nic neřeší. Stejně tak můžou přijít dva konfliktní AJAX požadavky. Jenom se ten potenciální konflikt přesunuje z odeslání všech dat na odeslání identifikátoru na pozadí.

Andrew avatar

Jan Tvrdík

nový

Používám řešení 2 s tím, že nezachytávám PDOException ale vlastní DuplicateEntryException (extends RuntimeException), na kterou mi PDOException přeloží Mapper, který ví, jaká DB se používá.

Jan Tvrdík avatar

pave.kucera

nový

Řešení 2) se dá použít i tak, aby Doctrine entity manager nezavřela, stačí otevřít svoji transakci, tam zachytit výjimku a nechat em otevřený. Je fakt, že nevím jaký vliv to má na „vnitřní stav“ entity manageru / unit of work, ale chci to blíž prozkoumat, protože se mi 2) líbí mnohem víc než 1)

pave.kucera avatar

Patrik Votoček

nový

[1] Na zamikání tabulky jsem zapoměl (nejspíš proto protože ho moc nemusím).

[2] To problém neřeší jen snižuje (pokud vůbec) riziko že se něco takového stane.

[3] jak říká [4] vůbec to neřeší problém.

[5] řeším to vpostatě podobně. Jen místo mapperu v service vrstvě (jsem líný dodělávat tuhle funkcionalitu do doctrine).

Patrik Votoček avatar

Lopo

nový

a co tak vyuzit ON DUPLICATE KEY v SQL ? teda aspon MySQL to ma, neviem ako je na tom napr MSSQL, Postgre atd.

Lopo avatar

Pawell

nový

IMHO nejčistší řešení je mít na to v DB trigger/funkci (a vůbec to na úrovni PHP neřešit) a určený unikátní slug pak vracet do PHP pomocí RETURNING.

Pawell avatar

Patrik Votoček

nový

[8] a co by ten ON DUPLICATE KEY měl dělat?

[9] bavíme se tu obecně o unikátním záznamu (nejde jenom o slug). Jak mě pomůže trigger/funkce v DB pokud je slug uživatelsky definovatelný?

Patrik Votoček avatar

Jakub Vrána

nový

Já se kloním k druhému řešení.

První řešení se dá zkombinovat s příznakem FOR UPDATE (v NotORM dostupné přes ->lock()). To je lepší než zámek.

[10] ON DUPLICATE KEY může třeba LAST_INSERT_ID nastavit na 0 (podle čehož pak poznáme, že se vložení nepodařilo). Ale přenositelné to samozřejmě není vůbec.

Jakub Vrána avatar

srigi

nový

Kedysi som na to v Doctrine 2 narazil a EM sa mi zamkol. Rozmyslal som, ze si na EM spravim factory a pri „onDuplicate“ vynimke si poziadam o novy EM, pozmenim data (napr. pridat „-1“) a znova skusam ulozit v loope do-while.

Je to ale poriadne nesystemove riesenie napr. na weboch s obrovskou navstevnostou, kde useri generuju obsah, by ten loop mohol iterovat mnoho-mnohokrat.

srigi avatar

Přidej komentář



cenzuruje váš poskytovatel připojení?

Kategorie

Čtu

Kamarádi