Many times we use to write the code without using the latest possible features which our language or framework provides. In this article I would like to present how we can use a power of Laravel Custom Collection for type hinting problem.
First of all, few word about variables type hinting in functions and errors messages which they produce.
You can write a function with parameters:
1 2 3 4 5 6 7 8 |
SomeClass { function getProducts($order) { // for super simple example: return $order->products; } } |
If you use in code like this:
1 2 |
$order = [] getProducts( $order ) |
You will probably see the error like this:
1 |
Trying to get property 'products' of non-object |
But if you add type hint:
1 2 3 4 5 6 7 |
SomeClass { public function getProducts(Order $order) { //… } } |
Then the info will be more concrete:
1 |
Argument 1 passed to getProducts() must be an instance of Order, array given, called in YourClass.php on line XX |
(for details check the php documentation – link)
Another nice feature which starts with php7 is that we can specify what will return us the method (link).
So another simple example:
1 2 3 4 5 6 7 |
SomeClass { public function getProductsWeight(Order $order) : int { return $order->products->sum(‚weight’); } } |
But if you will write:
1 2 3 4 5 6 7 |
SomeClass { public function getProductsWeight(Order $order) : int { return $order; } } |
Then error will be
1 |
Return value of getProductsWeight() must be of the type integer, object returned |
This could be really helpful when you creating interfaces or when you only planning your methods without logic implementation…
1 2 3 4 |
SomeInterface { public function getProductsWeight (Order $order) : int } |
Then someone who will write a class which implements this interface, cannot make a mistake, because both parameter and returned value are specified.
OK, this is enough for the intro, now I wanna to present my problem and solution of course
I was writing code which gets waybill for order:
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; } } |
And it was OK, I was pretty sure that I need to pass Order object and I will receive Waybill and can do something with it.
But I was wondering how can I type hint when I need to pass a collection of orders, and also get an collection of Waybills…
Of course I can do something like:
1 2 3 4 5 6 7 8 |
use Illuminate\Database\Eloquent\Collection SomeClass { public function deliver(Collection $orders) : Collection
{ //… } } |
But if somewhere in the code I will do something like that:
1 |
deliver($payments) |
…Hmm this also will work because $payments is also a collection type. But the problem will be probably somewhere inside of the method, when I will want to use methods from Order which are not covered in $payments object.
The same happens with the returned type.
So how to pass a collection without losing the destination object interface?
Fortunately Laravel has a solution for that – we can create a CustomCollection for our models.
All I need to do is add newColletion method to my Model class (actually override it):
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); } } |
Now, Eloquent will always return a OrdersCollection instead of standard Collection.
And also create this Class, which even could be empty:
1 2 3 4 5 6 |
use Illuminate\Database\Eloquent\Collection; class OrdersCollection extends Collection { } |
analogously for Waybill.
And finally I can made some changes in my method:
1 2 3 4 5 6 7 8 9 |
use OrdersCollection; use WaybillsCollection; SomeClass { public function deliver(OrdersCollection $orders) : WaybillsCollection
{ //… } } |
And then be sure what we need to pass, and what we get in return!
PS. Of course, you can create some methods in CustomCollection class and then use them as you wish.
author: Przemek Tkaczyk from DevPark Laravel team