Refactoring to Events
I know, we’re all used to seeing that kind of messy code with a lot of ifs and elses, right?
But today we're gonna try to change this a little bit.
Well, let’s start with a sample, it’s a class that handles Stripe calls.
getContent(), true);
if (! $this->isInTestingEnvironment() && ! $this->eventExistsOnStripe($payload['id'])) {
return;
}
$method = 'handle'.studly_case(str_replace('.', '_', $payload['type']));
if (method_exists($this, $method)) {
return $this->{$method}($payload);
} else {
return $this->missingMethod();
}
}
protected function handleCustomerSubscriptionDeleted(array $payload)
{
$user = $this->getUserByStripeId($payload['data']['object']['customer']);
if ($user) {
$user->subscriptions->filter(function ($subscription) use ($payload) {
return $subscription->stripe_id === $payload['data']['object']['id'];
})->each(function ($subscription) {
$subscription->markAsCancelled();
});
}
return new Response('Webhook Handled', 200);
}
No, never your entire life you would say that I got this code from Cashier Repository. Never.
That's fine, right? If we receive an event called customer.subscription.deleted our method handleCustomerSubscriptionDeleted will handle it!
But that’s fine when your application is starting and you don’t need to handle too much stuff. What if we need to receive all Stripe methods? Should we take the same approach?
Nope!
We have better ways to handle that, thanks to Laravel Events.
All we need to do to create a more consistent and understandable code, is to create an event for every hook that you want to receive.
So if you need to listen to Stripe’s customer subscription deleted hook, you should create an event called StripeCustomerSubscriptionDeleted, and then create the listeners that would handle that event.
So we can make our controller thinner, moving responsibilities to event listeners:
<?php
namespace App\Http\Controller;
(...)
class WebhookController extends Controller
{
public function handleWebhook(Request $request)
{
$payload = json_decode($request->getContent(), true);
if (! $this->isInTestingEnvironment() && ! $this->eventExistsOnStripe($payload['id'])) {
return;
}
$class = '\App\Event\Stripe' . studly_case(str_replace('.', '_', $payload['type']));
if (class_exists($class)) {
return new $class($payload);
} else {
return $this->missingMethod();
}
}
}
Now every event that Stripe sends to us, it will try to call an event. We just need to create and handle it.
And then we create our event:
<?php
namespace App\Event;
class Stripe Customer Subscription Deleted
{
/**
* Create a new instance.
*
* @param array $payload
*/
public function __construct(array $payload)
{
$this->payload = $payload;
}
public function getCustomer()
{
// Get the current customer based on $payload
}
}
And our listener:
<?php
namespace App\EventListener;
final class Stripe Cancel Subscription
{
/**
* Handle the event.
*
* @param mixed $event
*/
public function handle($event)
{
$customer = $event->getCustomer();
$customer->subscriptions->filter(function ($subscription) use ($event) {
return $subscription->stripe_id === $event->payload['data']['object']['id'];
})->each(function ($subscription) {
$subscription->markAsCancelled();
});
}
}
Now that we created our event and listener, we need to tell Laravel how to handle it. To do this, we just need to add them to the EventServiceProvider:
...
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
/*
* Stripe Billing Events
*/
\App\Event\StripeCustomerSubscriptionDeleted::class => [
\App\EventListener\StripeCancelSubscription:class
],
And that’s it!
You can apply this in an infinite type of situations when you're handling a lot of conditions.
Just create events and separate your handlers! Your code will be cleaner and easier to understand.
Of course my example was only to explain a given situation, I really think that this should only be applied if you have a lot of events and listeners needed to be handled in your workflow, otherwise just keep with the default stuff.
Originally published on MEDIUM