Jak správně řešit výjimky?
- Jiří Nápravník
- Člen | 710
Jak správně na výjimky? Jak řešíte případ, když ukládáte např. něco do databáze. Může nastat logicky při ukládání chyba, někde v modelu či až v PDO. Jak to řešíte?
- Pedpokládáte, že chyba nastane výjímečně a spolíháte tak na úspěch. A když už se to náhodou stane, tak tu máme errorpresenter a ten musí návštěvníkům stačit?
- Nebo je zachytáváte? Nastavíte adekvátní flashmessage a přesměrujete?
Já se je snažím zachytávat, ale přeci jen těch případů, kdy něco dělám s databází je mnoho a je to pomalu na upsání se. Nemluvě o tom, že když tu výjimku už odchytím, tak jí stejně musím zalogovat, ať se o ní také nějak dozvím – či-li musím volat Debugger:log, nicméně zároveň by bylo pěkné aby se mi v development módu vyhazovala laděnka, a ne se potají logovala (což Debugger::log dělá). Je to nějak řešitelné?
No a pak je tu ta otázka jde tohle vše nějak usnadnit? Koukal jsem, že někteří používají onError nad $application, ale není to přeci jen moc overkill a ne zrovna dvakrát hezké, alespoň dle mě, nebo jaký na to máte názor?
Usnadnit to chci z toho důvodu, že teď mi sice stačí jen nastavit flashmessage a zavolat Debugger::log, ale pokud budu chtít třeba nějakou tu kontrolu, zda jsem na development mašině a chci tedy zobrazit laděnku v celé kráse, tak už je to více řádku a copy pastovat to vše je hodně bad practise…
- Milo
- Nette Core | 1283
@Jiří Nápravník: Debugger se chová tak jak
popisuješ. V devel módu vyskočí laděnka, v produkci se jen loguje. Pokud
ale vyvíjíš na serveru a přistpuješ z jiného PC jako klient, musíš si
nastavit, pro jaké IP klienta bude devel mód. Např:
Debugger::enable('192.168.0.10')
.
- Jiří Nápravník
- Člen | 710
Filip Procházka napsal(a):
Díky Filipe za odkaz na super přednášku. Či-li pokud to dobře chápu, tak v tomhle případě odchytávat vyjímku nemá smysl, jelikož je to System Failure. Protože sloupečky mám dobře namapované (a pokud ne tak je to usage exception, která musím opravit) a jinak hrozí jen nějaké selhání databáze či podobně a to neřešit… (beru pro jednoduchost, klasické ulžení, kde nejsou žádné unique apod)
- Filip Procházka
- Moderator | 4668
Tam kde je očekávané že může nastat jiná než SQL syntax chyba (což by se stávat nemělo nikdy) tak odchytávat. Pokud očekáváš connection chyby tak to taky nemá smysl chytat, pokud ti nejede databáze nemá smysl se snažit a nechal bych to padat na error presenter do pětikila.
- Jiří Nápravník
- Člen | 710
Filip Procházka napsal(a):
Tam kde je očekávané že může nastat jiná než SQL syntax chyba (což by se stávat nemělo nikdy) tak odchytávat.
A co konkrétně tedy myslíš „jinou než syntax chybou“? kudlajz nade mnou píše odchytávat každý update/insert apod. ale já pravě od Honzy tvrdíka pochopil spíše, že to ne, jen v případě pokud nějakou chybu očekávám, jako třeba duplicate entry apod. Prootže pokud vyloučím syntax errory tak u standardnich insertů, erroru do jedné tabulky, v podstatě pak hrozí opravdu snad jen spadlá databáze, což nemá smysl chytat.
Můžeme být úplně konkrétní, když používám tvoje Kdyby\Doctrine mám odchytávat DBALException, při volání save nad jednou entitou, nebo při flushy? Pokud neočekávám nějakou specialitu s duplicate, či optimistic lockingem…
- Filip Procházka
- Moderator | 4668
jen v případě pokud nějakou chybu očekávám, jako třeba duplicate entry apod.
^this
hrozí opravdu snad jen spadlá databáze, což nemá smysl chytat.
^this
Můžeme být úplně konkrétní, když používám tvoje Kdyby\Doctrine …
Chytám jenom NoResultException
, pokud by to operace (select)
mohla vyhodit. Jinak vůbec nic.
- Jiří Nápravník
- Člen | 710
Ještě si dovolím vytáhnout tohle témá s příbuznou věcí.
Řekněme, že v modelových třídách mám
findById($id) – nedělá nic jiného než zavolá $this->dao->find($id)
a vráti
a potom
deleteById($id) – maže podle id
delete($entity) – maže přímo entitu
Z presenteru chci volat logicky přes $this->facade->deleteById()
a v tom deleteById() mám
find – to mi vrátí entitu – kterou pošlu do delete($entity), která jen
prostě smaže.
No a teď co udělat pokud člověk zadá pokyn pro mazání neexistujícího id. Teď mi findById vrací v tomhle případě null. Mám v delete a případně i v dalších podobných metodách kontrolovat všude, zda to náhodou není null a kdyžtak informovat. Nebo prostě u toho findById() vyhodit výjimku o nenalezení, která probublá k presenteru, kde si to odchytím a dám E404?
Snazší je pro mě bez pochyby exception, ale vidím i názory, vracet null, vždy překontrolovat (tady vidím hlavní problém – duplikace a hlavně zapomenutí kontroly), a pak vrátit případně false a kdyžtak nasměrovat e404. Jak to řešíte?
- Jiří Nápravník
- Člen | 710
No tak, ta stránka neexistuje, takže 404 je dle mě na místě. Ale o to mi teď moc nejde, mě jde spíše o to, jak to obstarat z pohledu modelu. Jestli mám u toho findu vyhazovat exception a nechat probublat až na presenter, kde to ošetřím. Nebo zda find vyhodit null, a u delete/update vracet false a pak to ošetřit. Mě se více líbí výjimky na othle, ale nevím jak to řeší profíci.
- Šaman
- Člen | 2666
Milo napsal(a):
Jestli se metoda jmenuje
findById()
, vracel bych NULL. Kdyby se jmenovalagetById()
vyhodil bych nějakou NotFound vyjímku.
Osobně používám konvenci, že find* metoda vrací pole, nebo kolekci (takže v tomto případě prázdnou), get* vrací konkrétní záznam, nebo NULL.
V každém případě bych při nevyhledání záznamu výjimku neházel, protože nikdy nevíš, jestli budeš hledat klíčový prvek (který pokud nenajdeš, budeš házet 404) anebo můžeš hledat něco nedůležitého a neexistenci záznamu budeš řešit třeba jen nezobrazením nějaké sekce v šabloně.
- Jiří Nápravník
- Člen | 710
Díky za reakce.
Jde mi o „akademicky nejčistší“ řešení a zatím to vypadá, že vede spíše názor vracet NULL a kontrolovat. Osobně jsem čekal spíše opačný názor, už jenom kvůli tomu opomenutí kontroly na NULL.
No pak je tu taky ta varianta, přejmenovat find na get, protože já v podstatě to findById, používám jako vy getById :-)
Nicméně pokud tedy teď ohlédneme od nějakého názvosloví. Tak jak vy řešíte takový klasický use case na smazání položky. Konkrétně tu fázi kdy vyhledávate entitu ke smazání/update. Vyhazujete tam výjimku nebo kontrolujete na null?
- Šaman
- Člen | 2666
Konvence get/find, findOne/find, apod… jsou na tobě. Já se tu svoji naučil používat v ORM od Petra Procházky a vyhovuje mi. Viděl jsem ale různé a pokud nepracuješ v týmu (pak je to na domluvě), tak je to jen na tobě.
Jde o to, že řešit co se má stát při nenačtení záznamu by měl až
presenter, protože otázka, jestli zobrazit chybovou stránku, nebo jen něco
tiše nevypsat je dle mého prezenční logika. Varianta s parametrem
$required
vypadá použitelně. Já osobně si kontroluji výsledek
a rozhoduji se podle toho, zda je NULL.
- Jiří Nápravník
- Člen | 710
Určitě souhlasím, že zobrazení chybové stránky je věc presenteru. Taky to nikam do modelu netahám. Jde mi jen o to zda dát vědět presenteru v tomhle případě přes vrácení null (nebo v případe delete/update spíše false) nebo přes výjimku, kterou vyhodí už find.
- duke
- Člen | 650
Já to vidím tak, že nízkoúrovňové služby (např. repozitáře – přesněji DAO) by v metodách jako findFoo či getBar při nenalezení záznamu výjimky vyhazovat neměly (spíše bych vracel NULL, prázdné pole, či nějaký speciální objekt představující nulový výsledek) (a když už, tak jen s použitím parametru $require či $required, jak již bylo nastíněno). To, že repozitář nenalezl záznam podle zadaných kriterií přeci není nijak výjimečné. Ale situace, kdy nadřazená služba používající takovýto repozitář má za úkol něco, k čemuž nutně onen záznam potřebuje, a takový záznam od repozitáře nezíská, to už je pro mě jasný kandidát na vyhození výjimky (tj. z nadřazené služby).
Takže pro to mazání bych nejspíš v ideálním případě vyhazoval výjimku v nějaké službě, která by byla v hierarchii služeb mezi presenterem a repozitářem, resp. DAO.
- Jiří Nápravník
- Člen | 710
duke: díky za reakci já to vidím velmi podobně. Já tady neřeším, repository (resp. dao) ale až o tu úrpveň výše, co mám fasádu…