Wpis z mikrobloga

Ten problem prześladuje mnie od dawna i zastanawiam się czy jest do tego jakieś eleganckie rozwiązanie. Aktualnie korzystamy z php7.

Mam taki prosty kod:

$countries[] = $organizationUser->getOrganization()->getCountry->getId()

On zadziała wtedy, gdy getOrganization oraz getCountry nie zwróci NULL. A tak się może zdarzyć.

Zwykle zabezpieczam się po prostu ifem, tak:

$organization = $organizationUser->getOrganization();
if(!isnull($organization) && !isnull($organization->getCountry()))
$countries[] = $organization->getCountry()->getId();

Czy jest jakieś mniej zawiłe rozwiązanie? Zwykle są maksymalnie dwie relacje, ale zdarza się, że są trzy i ten if jest jeszcze większy. Próbowałem:

$countries[] = $organization->getCountry()->getId() ?? NULL

Ale wiadomo, błąd jest ten sam gdy cokolwiek będzie null. Zapis ternarny też bardzo nie ratuje przy dwóch relacjach i więcej.

Jest jakaś fajna rzecz do tego? To się za mną ciągnie od lat, ktoś musiał coś wymyślić :D

#programowanie #pytanie #php
  • 32
@foxbond: Sama ta konstrukcja jest już większa niż sam if. No i z samym błędem też coś trzeba zrobić by było elegancko, a ja bym nie chciał akurat robić nic. Więc byłby try i pusty catch. A gdy błąd wywali np. z doctrine albo jakąś inną rzeczą niż tylko null, to try..catch wyżej tego nie wyłapie tylko zignoruje. Więc gdyby chcieć to zrobić dobrze, to byłoby więcej kodu niż jest teraz.
@spike200: Napisałem to czysto teoretycznie, nie wiem czy tak zadziała. Zrobić

try {
$result = $chain()->instruction()->whatever()->you()->want();
}catch{
log("sth");
$result = getDefaultResult();
}

Ogólnie w internecie znalazłem tylko artykuł z 2013 i niestety poza upierdliwymi checkam inic innego wówczas nie było, kto wie, moze już jest xd.

Według mnie jest to nie do końca przemyślana struktura aplikacji, jeśli zastanowię się co ja niegdyś tworzyłem w php (najwyzej 5.0) to nigdy nie natrafiłem
@foxbond: To jest struktura Doctrine, to jest ORM akurat w zendzie, ale używa się tego też w Symfony i paru innych frameworkach. Jest to dosyć popularne i generalnie nie ma opcji, by to zmienić. Znaczy da się, ale to komplikuje jeszcze więcej rzeczy no i gdyby zwracać obiekt z getOrganization, np. nowy, pusty, to bardzo łatwo o błędy.

Co do twojego rozwiązania, to ma 6 linii, moje 3. Nawet w tych
@spike200: Jakby to było C to naturalnym kandydatem byłoby macro korzystające z lazy evaluation if statements. Może są jakieś nadbudówki do php(nie znam się) które dodają jakiś syntaktyczny cukier w postaci makr?
@cptyossarian123: A nie da się tak np. że
Robimy foldlist -> jako element początkowy obiekt startowy. Elementy tablicy to funkcje lambda kolejnych wywołań np. jeżeli mamy łańcuch zależności a->b->c->d to tablica funkcji wygląda tak w pseudo kodzie [x: x->b, x: x->c, x: x->d]. Funkcja foldlist bierze wynik n i produkuje wynik n + 1 na zasadzie: jeżeli n był null to zwróc null, jeżeli nie to zwróc wynik kolejnej funkcji?
@cptyossarian123: W javascript byłoby to mw. tak:

function lazy_eval(start, funcs) {
function fold(x, y) {
return x === undefined ? x : y(x);
}
return funcs.reduce(fold, start);
}

var a0 = {}
a0.b = {}
a0.b.c = {}
a0.b.c.d = 'ok0'

var a1 = {}
a1.b = {}
a1.b.c = "ok1"

console.log(
lazy_eval(a0, [x=>x.b, x=>x.c, x=>x.d]) // prints ok0
);

console.log(
lazy_eval(a1, [x=>x.b, x=>x.c, x=>x.d]) // prints undefined
);

console.log(
lazy_eval(a1,
@spike200: $organizationUser->getOrganization()->getCountry źle źle źle, po cholerę ci cały model organizationUser i najpewniej jakieś hydrowanie modeli getCountry i getOrganization jak potrzebujesz tylko głupie id? robi się odpowiednie zapytanie w bazie danych i wrzuca metodę do repozytorium i robi się:

$countries[] = $userOrganizationRepository->findCountriesIdByOrganizationIdUserId($organizationId, $userId);

odwoływanie się relacji, hydracja modelu itp itd tylko po to aby pobrać głupie id to spory błąd no ale tak to jest jak się po prostu klepie gównokod
@foxbond: tylko po co w ogóle zwracać obiekty, jak najprawdopodobniej do niczego innego ich nie wykorzystamy? nie wiem dokładnie jaki use case jest w tym przypadku, ale pełna hydracja modelu, pobieranie wszystkich kolumn i kilka zapytań dla głupiego pobrania id to głupota straszna imho, zwłaszcza że można to zrobić jednym zapytaniem
@spike200: znaczy organizationUser może jest ci potrzebny, ale w pozostałe dwa mocno wątpie, jeśli uzyskujesz dostęp do nich tylko po to aby pobrać to id to jest to imho głupota, chyba że potem je gdzieś wyświetlasz ale to wciąż - robiłbym oddzielną metodę w repo do pobrania userorganization wraz z tą organization oraz country jako partiale z wybranymi kolumnami których potrzeba i rezultat jako tablica
@Jurigag: To już zupełnie inna kwestia, jak mówiłem, gdzieś w podstawach aplikacja jest źle zaprojektowana, a ja tylko rzuciłem sposobami na podratowanie żeby nie trzeba było przepisywać od nowa :)

Sam wyzej napisałeś - całe zapytanie powinno zostac obsłużone przez DB.
@foxbond: znaczy no wiesz, w php często są takie kwiatki, rozumiem jakby to jeszcze był jakiś szybki orm ale doctrine jest najnwolniejszy z dostępnych dla php, także osobiście bym unikał takich konstrukcji
@Jurigag: Nie jestem pewny czy ze wględu na optymalizację warto robić aż tyle metod. Gdyby to była jakaś bardzo duża aplikacja, a to zapytanie często powtarzałoby się, to jeszcze przyznałbym Ci rację. Mimo to często w aplikacji potrzebujesz zwykłe ID organizacji albo jakiejś innej relacji, a doctrine wyciągania samego ID chyba nie ma, więc nie raz coś takiego trzeba napisać, tzn. wyciągnąć cały model. A w praktyce ten model i tak
@spike200: i dlatego właśnie aplikacje na symfony/laravelu są wolne xD bo ludzie myślą tak jak ty i dla jakiś banalnych rzeczy robią 5 zapytań, 5 hydracji obiektu itp itd i z zapytania które powinno trwać z 50-200ms robi się sekunda albo dwie, ja bym to robił na oddzielnym zapytaniu jeśli wiem że nie wykorzystam nigdzie tych pobranych obiektów, ba, sam nigdy nie pobieram pełnych rekordów ani nie zwracam obiektów dla wyświetlania
@Jurigag: No widzisz, a jakimś cudem ta aplikacja jest szybka, a zapytania nie trwają sekund. Imho bardzo przesadzasz i mówisz o jakichś skrajnościach.

Swoją drogą $organizationUser->getOrganization()->getCountry()->getId(), gdyby nie problem z samymi nullami nie hydruje ci żadnego obietku. Te relacje to tylko proxy, a więc ostateczne zapytanie, to tylko zapytanie o ID. Tylko $organizationUser jest tutaj, ale on jest potrzebny w innej części kodu.