Bardzo często piszemy kod nie wykorzystując możliwości, które dają nam nowe wersje języka lub frameworka którego używamy. Poniżej przedstawię jakie możliwości dają nam Laravel Custom Collection w przypadku typowania zmiennych w funkcjach.
Na początek parę słów na temat samego typowania zmiennych oraz otrzymywanych błędów z tym związanych.
Napiszmy metodę z parametrem:
1 2 3 4 5 6 7 8 |
SomeClass { function getProducts($order) { // for super simple example: return $order->products; } } |
Jeśli dalej w kodzie użyjemy jej w taki sposób:
1 2 |
$order = [] getProducts( $order ) |
Prawdopodobnie zobaczymy takie błąd:
1 |
Trying to get property 'products' of non-object |
Jednakże jeśli dodamy typowanie zmiennej:
1 2 3 4 5 6 7 |
SomeClass { public function getProducts(Order $order) { //… } } |
To treść błędu będzie nieco bardziej konkretna:
1 |
Argument 1 passed to getProducts() must be an instance of Order, array given, called in YourClass.php on line XX |
(po szczegóły zapraszam do dokumentacji php – link)
Kolejną możliwością która została zaimplementowaną w php7 jest możliwość typowania zwracanej wartości przez daną metodę (link):
Kolejny bardzo prosty przykład, jak to działa:
1 2 3 4 5 6 7 |
SomeClass { public function getProductsWeight(Order $order) : int { return $order->products->sum(‚weight’); } } |
Ale jeśli zmienimy logikę metody:
1 2 3 4 5 6 7 |
SomeClass { public function getProductsWeight(Order $order) : int { return $order; } } |
To otrzymamy błąd:
1 |
Return value of getProductsWeight() must be of the type integer, object returned |
Takie typowanie może być bardzo pomocne przy planowaniu klas (bez implementacji szczegółowej logiki) albo przy tworzeniu interfejsów:
1 2 3 4 |
SomeInterface { public function getProductsWeight (Order $order) : int } |
Wtedy ktoś kto będzie pisał klasę która implementuje ten interfejs nie będzie mógł popełnić pomyłki, ponieważ zarówno parametr jak i zwracana wartość maja zadeklarowany typ.
Tyle tytułem wstępu, przejdźmy do mojego problemu no I oczywiście jego rozwiązania:
Pisałem kod który miał pobierać list przewozowy dla danego zamówienia:
1 2 3 4 5 6 7 8 9 10 11 |
use Order; use Waybill; SomeClass { public function deliver(Order $order) : Waybill { // do some stuff to get $waybill return $waybill; } } |
I takie rozwiązanie było OK, byłem pewien co muszę przekazać jako parametr do tej funkcji: Order, oraz co dostanę jako wynik tej metody: Waybill.
Problem pojawił się gdy chciałem jako parametr dać kolekcję zamówień i w zwrotce otrzymać kolekcję listów przewozowych, jak takie coś typować?
Oczywiście, mogłem zrobić coś takiego:
1 2 3 4 5 6 7 8 |
use Illuminate\Database\Eloquent\Collection SomeClass { public function deliver(Collection $orders) : Collection
{ //… } } |
Jednak, jeśli gdzieś w kodzie bym omyłkowo użył funkcji tak:
1 |
deliver($payments) |
…Hmm, to też by zadziałało, bo $payments jest typem Collection, ale problem pojawi się przypuszczalnie dalej, w środku metody gdy będę tam chciał użyć metod/właściwości z $order które nie będą dostępne w obiekcie $payment.
Dokładnie tak samo będzie z typem zwracanym.
Jak zatem można rozwiązać ten problem?
Na szczęście Laravel ma na to pewne rozwiązanie, mianowicie musimy utworzyć Custom Collection dla naszych modeli.
Cała praca polega na dodaniu do klasy modelu metody (a w zasadzie jej nadpisaniu) newColletion:
1 2 3 4 5 6 7 8 9 10 11 |
use OrdersCollection; class Order extends Model { /// … public function newCollection(array $models = []) { return new OrdersCollection($models); } } |
Od teraz, za każdym razem Eloquent będzie zwracał nam OrdersCollection zamiast standardowego Collection.
Oraz oczywiście stworzenie klasy którą powyższa metoda nam zwraca (sama klasa może być nawet pusta):
1 2 3 4 5 6 |
use Illuminate\Database\Eloquent\Collection; class OrdersCollection extends Collection { } |
Analogicznie zrobimy dla klasy Waybill
I w końcu możemy ulepszyć naszą metodę deliver:
1 2 3 4 5 6 7 8 9 |
use OrdersCollection; use WaybillsCollection; SomeClass { public function deliver(OrdersCollection $orders) : WaybillsCollection
{ //… } } |
I teraz jesteśmy pewni co musimy podać jako parametr oraz do dostaniemy po wykonaniu tej metody.
PS. Oczywiście do klasy OrdersCollection można dodawać metody które będą nam gdzieś usprawniały pracę.
autor: Przemek Tkaczyk from DevPark Laravel team