Hey guys,
Weekend post again hehe ๐. I'd like to share my latest approach and I actually find it really cool ๐ฅน.
I've been using PHP Enums not only for options, types, etc but also for Result responses now โญ๏ธ
Lemme show you how it works and how awesome is that ๐.
Results over Exceptions
This is one of the practices I've been using and I love it, I also have a post for that: No throws and happier life
TL;DR:
Throw is slow
Untyped
- E.g. if your function throws 4 exceptions => 4 untyped paths.
Increase production errors
- Callers won't usually wrap the
try/catch
- Callers won't usually wrap the
It reduces the reusable purpose of your function
I really like how this package neverthrow
add their reasons as well:
Throw
ing andcatching
is very similar to usinggoto
statements - in other words; it makes reasoning about your programs harder. Secondly, by usingthrow
you make the assumption that the caller of your function is implementingcatch
. This is a known source of errors. Example: One devthrow
s and another dev uses the function without prior knowledge that the function will throw. Thus, and edge case has been left unhandled and now you have unhappy users, bosses, cats, etc.With all that said, there are definitely good use cases for throwing in your program. But much less than you might think.
Use PHP Enum as the Result
Yeah, here come the main dishes ๐
Everything looks easier with a real-life example, so let's create a Result
class for creating a Vacation Leave Request.
The Result class
I will create a CreateLeaveRequestResult
enum CreateLeaveRequestResult
{
/**
* When the creating leave request is overlap with
* another leave requests
*/
case HAS_CONFLICTED_DATES;
/**
* When the user doesn't have enough leave balance
*/
case INSUFFICIENT_BALANCE;
/**
* All good and can create a new leave request
*/
case SUCCESS;
}
As we can see, with some simple notes & cases, we all know the outcome of the function that we are going to trigger. It makes our life easier โ .
Handle logic and return the Result
I have a LeaveRequestService@create
method like this:
class LeaveRequestService
{
public function create(LeaveRequestData $leave): CreateLeaveRequestResult
{
if ($this->hasConflictedDates($leave)) {
return CreateLeaveRequestResult::HAS_CONFLICTED_DATES;
}
if (!$this->hasEnoughBalance($leave)) {
return CreateLeaveRequestResult::INSUFFICIENT_BALANCE;
}
// all good
$leaveRequest = $this->createNewLeaveRequest($leave);
// send notifcations or do anything
// ...
return CreateLeaveRequestResult::SUCCESS;
}
}
Response in Controller
public function create(
LeaveRequestService $service
SubmitLeaveRequestRequest $request
): JsonResponse {
$leave = $request->getLeaveRequestData();
$result = $service->create($leave);
if ($result !== CreateLeaveRequestResult::SUCCESS) {
return new JsonResponse([
'outcome' => $result->name,
], 400);
}
return new JsonResponse([
'outcome' => 'SUCCESS',
]);
}
Awesome right? ๐
PROs
Fully typed in your functions.
Better than string/boolean response, especially for complex business logic/flows.
Happier callers, we always know what we'll receive after triggering the functions.
Considerations
Include additional data when returning the result
Yes, we can, can do either:
Create a new class which have the Enum result & your additional data
Use my
never-throw
package (written in PHP)- Really want to make it as much as same as the TS's neverthrow but still haven't gotten there ๐ฅน.
A bit of marketing
As you see in the above example. It relates to the PTO (personal time off) & Vacation tracker.
If your teams want a simple & awesome solution for that, try out Flamingo App ๐ฆฉ.
It's lovely, gorgeous, super easy to use, and has an awesome notification system that ensures everybody is in the loop ๐ฃ.
Conclusions
Well, that's it guys. I really like this way - making my functions/methods become fully typed.
Happy weekend!