• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Symfony czy w serwisach można rzucać wyjątkami

Mały hosting, OGROMNE możliwości
0 głosów
1,509 wizyt
pytanie zadane 1 lutego 2019 w PHP przez `Krzychuu Stary wyjadacz (13,940 p.)
edycja 1 lutego 2019 przez `Krzychuu

Witam

Czy mogę tworzyć wyjątki w klasie serwisu? np. 

throw new HttpException(400, "Nazwa zajęta!");

Załóżmy że dodaje rekord do bazy danych w serwisie sprawdzam czy istnieje rekord o takiej samej nazwie jeżeli istnieje to lepiej będzie wyrzucić wyjątek taki jak wyżej czy zwrócić do kontrolera błąd i tam zwrócić nazwę i kod błędu?

3 odpowiedzi

+2 głosów
odpowiedź 1 lutego 2019 przez Comandeer Guru (607,980 p.)
wybrane 2 lutego 2019 przez `Krzychuu
 
Najlepsza

Usługa raczej powinna rzucać wyjątki dotyczące tego co robi, np. brak połączenia z bazą – wyjątek NoDBConnectionException lub podobny. Warstwa HTTP to nie jest coś, czym powinna się zajmować usługa.

Dodatkowo wyjątki powinny dotyczyć – jak sama nazwa sugeruje – sytuacji wyjątkowych. Owszem, można wszelkie błędy obsługiwać przy pomocy wyjątków, ale wówczas w kontrolerze narośnie nam wiele bloków try/catch obsługujących poszczególne rzeczy. W tym wypadku zwróciłbym po prostu informację, że rekord nie został dodany, z opcjonalną informacją czemu. Innymi słowy: wyjątki zostawiłbym dla błędów niezależnych od naszego kodu (brak połączenia z bazą, niemożność zapisu do pliku itd.), a błędy związane bezpośrednio z działaniem aplikacji czy błędnie wprowadzonymi przez użytkownika danymi (a zatem błędami, o których użytkownik powinien się dowiedzieć) obsługiwałbym "normalnie".

Ogólnie to widziałbym to jakoś tak:

<?php
public function handleRequest( $req ) {
	try {
		if ( !$this->myService->saveToDB( $req ) ) {
			// Nie zapisano, trzeba wyświetlić błąd użytkownikowi.
		}
	} catch( Exception $e ) {
		// Baza się krzakła.
		throw new HTTPException( 500 );
	}
}

W sumie tutaj jest fajny artykuł opisujący dokładniej to, o czym napisałem: http://adam.wroclaw.pl/2015/05/wyjatki-kiedy-jak-i-po-co/

Oczywiście można też stosować podejście zupełnie odwrotne: https://zawarstwaabstrakcji.pl/20170309-walidacja-w-architekturze-wielowarstwowej/ – pytanie, na ile chcemy komplikować całość.

komentarz 2 lutego 2019 przez HaKIM Szeryf (87,590 p.)

Nie ma najlepszej. Wszystko zależy od projektu, języka, frameworka lub jego braku, architektury, czasu (deadline) i masy innych czynników.

Powiedziawszy to możesz zajrzeć do:

https://symfony.com/doc/current/validation.html

I odpowiedzieć sobie na pytanie czy jest to dobre rozwiązanie dla Twojego projektu.

komentarz 2 lutego 2019 przez `Krzychuu Stary wyjadacz (13,940 p.)
Tzn chodzi mi bardziej jaki response zwrócić, żeby odczytać że konkretnie o ten błąd chodzi.
komentarz 2 lutego 2019 przez HaKIM Szeryf (87,590 p.)

Jeśli chodzi o konflikt dwóch takich samych rekordów to (w Symfony):

$message = $exception->getMessage() ?? 'Something went wrong';

return new Response($message, Response::HTTP_CONFLICT);

I sprawdzasz np. Ajax zwracany status code.

Ale z tego co widziałem to Ty korzystasz z walidatora Symfony, a tam to trochę inaczej wygląda.

Odsyłam do dokumentacji: https://symfony.com/doc/current/validation.html

komentarz 2 lutego 2019 przez `Krzychuu Stary wyjadacz (13,940 p.)
Dziękuje za pomoc :)
komentarz 2 lutego 2019 przez Comandeer Guru (607,980 p.)

Nie potrafię sobie wyobrazić apki, gdzie każda z warstw może jedynie odnieść się do warstwy pod nią. T.j. UI tylko może odnieść się do Application i łapy precz od Infra czy Domain.

Warstwy wgl nie powinny się odnosić do siebie. Jedna warstwa przekazuje innej warstwie dane i czeka na wynik – cała interakcja.

Mi to brzmi jak wyjątek albo przynajmniej boolean na steroidach.

if ($validator->validate($data) ) {
    var_dump($validator->getErrors());
}

 

+1 głos
odpowiedź 1 lutego 2019 przez ShiroUmizake Nałogowiec (46,300 p.)
Ja bym zrobił zwrotkę do kontrolera i tam obsłużył wyjątek. Bo potem się zakopiesz w try/catch :P
komentarz 1 lutego 2019 przez `Krzychuu Stary wyjadacz (13,940 p.)
W Symfony można rzucać wyjątkami bez try/catch
1
komentarz 1 lutego 2019 przez HaKIM Szeryf (87,590 p.)

W Symfony można rzucać wyjątkami bez try/catch

Co z tego? Wszędzie możesz rzucać bez try catch. Try catch nie ma nic do rzucania wyjątkami, tylko ich przechwytywania.

Imo. głos w dół niezasłużony, bo ShiroUmizake podpowiedział b. dobrze.

1
komentarz 1 lutego 2019 przez `Krzychuu Stary wyjadacz (13,940 p.)
Szczerze to o tym nie widziałem, myślałem nad napisaniem event lisntener do przechwytywania błędu.

Ps. Głos w dół nie był ode mnie
1
komentarz 1 lutego 2019 przez HaKIM Szeryf (87,590 p.)
Ja sobie tak to załatwiłem u siebie:

https://github.com/HaKIMus/smartphones/blob/develop/src/Controller/Api/Smartphones/SmartphonesApi.php

Jestem bardzo dumny z tego kodzika, bo wszystkie try i catch wyizolowałem do handlera. :)

A tu sam exception handler:

https://github.com/HaKIMus/smartphones/blob/develop/src/Controller/Api/Handlers/SmartphonesApiHandler.php

W następnej wersji powinienem dodać do niego abstrakcje, po której będą rozszerzały wszystkie controller handlery aby uzyskać dostęp do chronionych metod t.j. returnResponse itp.
komentarz 1 lutego 2019 przez `Krzychuu Stary wyjadacz (13,940 p.)
Fajnie to wygląda, jakbym miał napisany handler to wtedy nie byłoby problemu z używaniem wyjątków w serwisach?, Rzucanie wyjątków można zaliczyć do logiki czy takie sprawy bardziej powinny być w kontrolerze ?
1
komentarz 1 lutego 2019 przez HaKIM Szeryf (87,590 p.)

Nie ma żadnego problemu z używaniem wyjątków gdziekolwiek, gdzie jest to potrzebne. Grunt, żeby je gdzieś obsłużyć, bo inaczej User dostanie Internal 500 na twarz a w gorszym wypadku, przy źle skonfigurowanej produkcji, exception ze wszystkimi informacjami.

Rzucanie wyjątków można zaliczyć do logiki czy takie sprawy bardziej powinny być w kontrolerze ?

Jest to logika, ale to tak jakbyś się czepiał ifów czy loopów w templatkach. Koszt wyizolowania tych rzeczy jest większy, niżeli trzymanie tego w kontrolerze.

komentarz 1 lutego 2019 przez `Krzychuu Stary wyjadacz (13,940 p.)
Dziękuje za pomoc i cenne informacje :)
1
komentarz 1 lutego 2019 przez HaKIM Szeryf (87,590 p.)

Tak nawiasem mówiąc to w tym kodzie:

throw new HttpException(400, "Nazwa zajęta!");

nadaje się prędzej 409 statusik.

No i ja polecam Ci korzystać z Response::HTTP_* - Jeśli lookniesz wgłąb klasy Response to zauważysz, że mają oni tam listę constów ze http statusami.

class Response
{
    const HTTP_CONTINUE = 100;
    const HTTP_SWITCHING_PROTOCOLS = 101;
    const HTTP_PROCESSING = 102;            // RFC2518
    const HTTP_EARLY_HINTS = 103;           // RFC8297
    const HTTP_OK = 200;
    const HTTP_CREATED = 201;
    const HTTP_ACCEPTED = 202;
    const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
    const HTTP_NO_CONTENT = 204;
    const HTTP_RESET_CONTENT = 205;
    const HTTP_PARTIAL_CONTENT = 206;
    const HTTP_MULTI_STATUS = 207;          // RFC4918
    const HTTP_ALREADY_REPORTED = 208;      // RFC5842
    const HTTP_IM_USED = 226;               // RFC3229
    const HTTP_MULTIPLE_CHOICES = 300;
    const HTTP_MOVED_PERMANENTLY = 301;
    const HTTP_FOUND = 302;
    const HTTP_SEE_OTHER = 303;
    const HTTP_NOT_MODIFIED = 304;
    const HTTP_USE_PROXY = 305;
    const HTTP_RESERVED = 306;
    const HTTP_TEMPORARY_REDIRECT = 307;
    const HTTP_PERMANENTLY_REDIRECT = 308;  // RFC7238
    const HTTP_BAD_REQUEST = 400;
    const HTTP_UNAUTHORIZED = 401;
    const HTTP_PAYMENT_REQUIRED = 402;
    const HTTP_FORBIDDEN = 403;
    const HTTP_NOT_FOUND = 404;
    const HTTP_METHOD_NOT_ALLOWED = 405;
    const HTTP_NOT_ACCEPTABLE = 406;
    const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
    const HTTP_REQUEST_TIMEOUT = 408;
    const HTTP_CONFLICT = 409;
    const HTTP_GONE = 410;
    const HTTP_LENGTH_REQUIRED = 411;
    const HTTP_PRECONDITION_FAILED = 412;
    const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
    const HTTP_REQUEST_URI_TOO_LONG = 414;
    const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
    const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
    const HTTP_EXPECTATION_FAILED = 417;
    const HTTP_I_AM_A_TEAPOT = 418;                                               // RFC2324
    const HTTP_MISDIRECTED_REQUEST = 421;                                         // RFC7540
    const HTTP_UNPROCESSABLE_ENTITY = 422;                                        // RFC4918
    const HTTP_LOCKED = 423;                                                      // RFC4918
    const HTTP_FAILED_DEPENDENCY = 424;                                           // RFC4918

    [...]
}

https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-18#section-7.4.10

komentarz 1 lutego 2019 przez `Krzychuu Stary wyjadacz (13,940 p.)
Dziękuje, faktycznie lepiej to będzie wyglądać
+1 głos
odpowiedź 1 lutego 2019 przez Ehlert Ekspert (215,210 p.)
Osobiście nie aż tak często plącze się w try catch, a wyjątkami się rzuca. Kluczowe mogą być dla Ciebie: EventSubscriber oraz event Kernel.EXCEPTION

Podobne pytania

0 głosów
1 odpowiedź 308 wizyt
pytanie zadane 8 marca 2021 w PHP przez michal_php Stary wyjadacz (13,700 p.)
0 głosów
1 odpowiedź 214 wizyt
pytanie zadane 30 września 2020 w PHP przez User007 Bywalec (2,420 p.)
0 głosów
2 odpowiedzi 442 wizyt
pytanie zadane 3 września 2018 w PHP przez BetBet Użytkownik (550 p.)

93,715 zapytań

142,629 odpowiedzi

323,260 komentarzy

63,257 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj

Twierdza Linux. Bezpieczeństwo dla dociekliwych

Aby uzyskać rabat -10%, użyjcie kodu pasja-linux, wpisując go w specjalne pole w koszyku.

...