Hi @realnsleo ,
First you're welcome, glad it worked out.
The big picture would be to create a service class (don't mind the "service" qualifier, it would be just a regular class) that decorates the calls to this third-party API.
Then you would register this service class into the container, calling the config before returning a new instance of it, and from this service class you would delegate the calls to the InvoiceNinja SDK classes.
This way every time you request this class from the container, you are sure the config would be run before calling it, and by consequence before any InvoiceNinja class is called.
Something like this:
First a Factory class
<?php
namespace App\Services;
use InvoiceNinja\Config;
class NinjaInvoiceService
{
public function __construct($url, $token)
{
Config::setUrl($url);
Config::setToken($token);
}
public function model($modelClass)
{
return new NinjaInvoiceModelDecorator($modelClass);
}
}
Then a Decorator class:
<?php
namespace App\Services;
use InvoiceNinja\Models\AbstractModel;
class NinjaInvoiceModelDecorator
{
private $modelClass;
public function __construct($modelClass)
{
if (! \is_subclass_of($modelClass, AbstractModel::class)) {
throw new \InvalidArgumentException('Model should be a InvoiceNinja model');
}
$this->modelClass = $modelClass;
}
/**
* This would create a new instance for when you need
* to call instance methods
*
* @param mixed ...$params
* @return \InvoiceNinja\Models\AbstractModel
*/
public function newInstance(...$params)
{
$modelClass = $this->modelClass;
return new $modelClass(...$params);
}
/**
* @return array
*/
public function all()
{
return forward_static_call([$this->modelClass, 'all']);
}
/**
* @param $id
* @return \InvoiceNinja\Models\AbstractModel
*/
public function find($id)
{
return forward_static_call([$this->modelClass, 'find'], $id);
}
// ... add every other public static methods
// from \InvoiceNinja\Models\AbstractModel
}
In your AppServiceProvider you would register the Factory class as a singleton:
<?php
namespace App\Providers;
use App\Services\NinjaInvoiceService;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(NinjaInvoiceService::class, function () {
// get this values from a config file
return new NinjaInvoiceService(
'https://ninja.dev/api/v1',
'My Token'
);
});
}
public function boot()
{
//
}
}
Lastly, in a controller you could inject this service class that would be resolved from the container:
public function index(NinjaInvoiceService $service)
{
return view('home')->with([
'clients' => $service->model(\InvoiceNinja\Models\Client::class)->all(),
]);
}
As the NinjaInvoiceService class is resolved from the container instead of using the new operator, the config is called before calling any model class.
This is a just proof-of-concept, might present some rough edges, and you would need to keep your service classes in sync with the InvoiceNinja SDK, so not much benefit on doing that other than having the config ran before.
On the other hand, one pro of using a decorator is actually decorating method calls instead of just forwarding it. For example you could add a log entry before every call forwarded to the underlying model.
As an example I hope it makes things clearer.