keep in mind that auth()->user() will be null outside the context of http requests, ex when the app is booted by crontab to perform a scheduled command, booted by supervisor to perform a queued job or you are running something from the cli
Injection or not for a service used by a service provider ?
Hello,
It's the first time I really use a service provider.
I have written this one.
class DatasourceServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
$this->app->singleton(DatasourceInterface::class, function (Application $app) {
$user = auth()->user();
$datasource = $user->company->datasource?->value;
if ($datasource) {
switch ($datasource) {
case 'pipedrive':
return new PipedriveDataService();
case 'googlesheet':
return new GoogleSheetDataService();
default:
throw new Exception('Unsupported data source', 404);
}
}
});
}
/**
* Bootstrap services.
*/
public function boot(): void
{
//
}
}
And the related service.
class PipedriveDataService implements DatasourceInterface
{
private $url;
private $token;
public function __construct()
{
$this->url = config('services.pipedrive.api_url');
$credentials = auth()->user()->company->datasource_credentials;
$this->token = $credentials['pipedrive_token'];
}
public function getUsers()
{
$users = null;
$response = Http::withQueryParameters([
'api_token' => $this->token,
])->get($this->url.'users');
if ($response->successful()) {
$users = Arr::map($response->object()->data, function (object $user, string $key) {
return (object) [
'source_id' => $user->id,
'name' => $user->name,
'email' => $user->email,
];
});
}
return $users;
}
}
Is it a good idea to use auth()->user() inside the service ? Or is it better to inject the user directly in the service provider ?
Furthermore I need the credentials in each service, is it a good idea to retrieve them in the service or would it be better to retrieve them in the service provider and pass them in the service constructor ?
Other advice ?
Thanks for your suggestions.
V
@vincent15000 Service providers are used to register services in the container. However, you don’t really seem to be taking advantage of this as your class is still “asking” for things rather than having dependencies provided.
For example, your PipedriveDataService is asking for a configuration value, and then using a helper to magically get the authenticated user, instead of these dependencies being given to the class. And as @krisi_gjika points out, this will also fail in non-HTTP contexts where there isn’t a notion of an authenticated user.
Instead, you should be making these things arguments in your class’s constructor, and resolving values for those parameters and providing them in your service provider:
class PipedriveDataService implements DatasourceInterface
{
protected string $url;
protected User $user;
protected string $token;
public function __construct(string $url, User $user)
{
$this->url = $url;
$this->user = $user;
$this->token = $this->user->company->datasource_credentials['pipedrive_token'];
}
}
public function register(): void
{
$this->app->singleton(DatasourceInterface::class, function (): void {
$user = $this->app->make('auth')->user();
return match ($user->company->datasource->value) {
'pipedrive' => new PipedriveDataService(
url: $this->app->make('config')->get('services.pipedrive.api_url'),
user: $user,
),
};
});
}
The PipedriveDataService now requires a URL and a user to be instantiated, and the service provider resolves and passes those arguments. Your class is no longer using helpers to magically grab values from somewhere that may return null or not be set at all.
Please or to participate in this conversation.