pdigital's avatar

Register a Soap client to the Laravel Service Provider

I'm building a webapp in Laravel which consumes multiple external REST API's, in which I have to authenticate myself and retrieve an access token before I'm able to do perform requests. I have built that like so:

ExampleAPIServiceProvider.php

class ExampleAPIServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(ExampleClient::class, function () {
            return new ExampleClient(
                accessToken: $this->getAccessToken()
            );
        });
    }

    private function getAccessToken(): ?AccessToken
    {
        return $this->accessToken ?? $this->requestAccessToken($clientId, $clientSecret);
    }
}

This enables me to perform requests like so:

ExampleController.php

class ExampleController extends Controller
{
    public function __construct(protected ExampleClient $client) { ... }

    public function index()
    {
        $response = $this->client->getExamles();
    }
}

I'm trying to consume another service, but this is a SOAP service. I know I can use the SoapClient(). If I understand correctly, with soap I first have to provide the url I'm trying to fetch data from and then do authentication. This is the example that the Soap Server I'm trying to consume provides:

try {
    $soap = new SoapClient($webservice_url);
    $res = $soap->Authenticate(array('accessKey' => $key));
    if (!isset($res->AuthenticateResult)) exit();
    $sess_id = $res->AuthenticateResult;

    $xmlvar = new SoapVar('<ns1:xmlDoc>'.$xml.'</ns1:xmlDoc>', XSD_ANYXML);
    $res = $soap->ProcessJournal(array('sessionID' => $sess_id, 'administrationID' => $admin_id, 'xmlDoc' => $xmlvar));
} catch (SoapFault $e) {
    // throw exception...
}

I'd like to build a small wrapper client for this SoapClient and register that to the service provider as well. However I'm not sure how, as it seems that authentication happens after providing the webservice url. With the example from the Soap Server, it would mean that I'd have to provide the $sess_id and $administrationId each time I want to consume the service.

I think the result I'd like to have is to be able to call this from example the controller:

$this->soapClient->url($url)->ProcessJournal(...);

How do I go about doing registering it to the service provider? Or is there any other solution which would allow me to provide the authentication credentials only once?

Thanks in advance.

0 likes
2 replies
ksi's avatar

You can try something like this (if you really see the value in the wrapper):

Class YourSoapApiClient
{
    private $session_id;

    public function __construct(private string $access_key); // set in your SP or use config() whatever you prefer

    public function getJournal($someParams)
    {
        $this->init();
        $this->soap->ProcessJournal([
            ‘SessionID’ => $this->session_id,
           …$someParams,
        ]);
    }

    private function init()
    {
        If (!$this->initiated) {
            $this->soap = new SoapClient(‘webservice_url’);
            $authResponse = $this->soap->Authenticate([‘accessKey’ => $this->access_key]); // or config(‘your_api_key’)
            if (!isset($authResponse->AuthenticateResult)) {
                  Throw new \Exception(‘cannot authenticate connection with YOUR_SOAP_SERVICE’)
            }
            $this->session_id = $authResponse->AuthenticateResult;
        }
    }
}

Extra: I suggest you never put things like this in a ServiceProvider - I assume $this->requestAccessToken Is actually making API requests to an external service:

    private function getAccessToken(): ?AccessToken
    {
        return $this->accessToken ?? $this->requestAccessToken($clientId, $clientSecret);
    }

With this in your SP you put yourself at risk of breaking the whole application whenever this call fails (API is down, your code outdated, request fails for whatever reason). This would happen if you put the ExampleClient As a dependency in something else than a controller and unknowingly this class is being instantiated upon every call to your app.

Been there, done that - not recommended experience ;)

pdigital's avatar

@ksi Thanks for the advice. Just curious, how would you resolve this issue so that retrieving the access token does not happen in de service provider? Or would you not use a SP at all? An example would be great!

Please or to participate in this conversation.