Skip to content

Instantly share code, notes, and snippets.

@shawnlindstrom
Last active October 19, 2024 05:55
Show Gist options
  • Save shawnlindstrom/42443d090e6d56a5a93c3ec90c46935b to your computer and use it in GitHub Desktop.
Save shawnlindstrom/42443d090e6d56a5a93c3ec90c46935b to your computer and use it in GitHub Desktop.
Twilio Service Provider for Laravel
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Twilio\Rest\Client;
class TwilioServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(Client::class, fn() =>
new Client(
$app->config['services.twilio.sid'],
$app->config['services.twilio.token']
)
);
}
}
@shawnlindstrom
Copy link
Author

Any idea how I can turn that into a Facade and just use Twilio::calls->create() anywhere in my app? I've been trying to get it to work the last couple of hours and am clearly missing something.

One idea would be to skip the service provider and just instantiate it inside a class then use a real-time facade:

<?php

namespace App;

use Twilio\Rest\Client;

class Call
{
     public function createCall($to, $options = [])
     {
          return (new Client(config.('twilio.sid'), config('twilio.token')))->calls->create($to, config('twilio.from'), $options);
     }
}

Then you could use it like so:

<?php

namespace App\Http\Controllers;

use Facades\App\Call;

class OutboundCallController extends Controller
{
     public function store()
     {
          $options = [
               // options like method, url, statusCallback, etc.
          ];
          Call::createCall('+17135551212', $options);     
     }
}

^The controller is contrived solely for the sake of an example. You'll find you end up having to do something like this if you ever get to working with Twilio subaccounts anyway. Generally, I'm making more specific types of calls so I just use a service class and call static methods. But, if you're only using your master account, just use the service provider and DI.

@shawnlindstrom
Copy link
Author

Future me here. Abstract all your Twilio calls and inject the Client into those services or whatever you want to call them. Never put yourself in a situation where you have to mock the Client in order to test. It's not a flex if you can even pull it off. It's bad implementation. Code that is easy to test is objectively better than code that is hard to test or untestable.

<?php

namespace App\Services\Twilio;

use Twilio\Rest\Client;
use Twilio\Rest\Api\V2010\Account\CallInstance;

class Call
{
    public function __construct(private readonly Client $twilio) {}
    
    pubflic function create(string $to, $options = []): 
    {
        return $twilio->calls->create($to, config('services.twilio.from_number'), $options); 
    }
}

Use it in your controller as a real-time facade or just use DI (preferrably);

<?php

namespace App\Http\Controllers;

use Facades\App\Services\Twilio\Call;

class OutboundCallController extends Controller
{
     public function store()
     {
          $options = [
               // options like method, url, statusCallback, etc.
          ];
          Call::create('+17135551212', $options);     
     }
}

Then don't forget to test (oversimplified example):

<?php
 
namespace Tests\Feature\Http\Controllers;
 
use Facades\Twilio\Service\Call;
use Tests\TestCase;
 
class OutboundCallControllerTest extends TestCase
{
    public function test_a_call_can_be_created(): void
    {
        $to = '+16501231234';
        Call::shouldReceive('create')->once()->with($to); 

        $this->post(route('call.store'))
            ->assertOk();
    }
}

Personally, i don't use real-time facades. Mocking the service directly is almost as simple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment