Reputation: 354
I am building an application that uses an external API through an already made library. Let us imagine this external service provides weather information for a given place. We have a controller like this:
class WeatherController
{
public function show($place, WeatherLibrary $api)
{
return $api->getWeatherFor($place);
}
}
It looks okay, but this API has a requests per minute limit that creates the need of a caching system. I was thinking about using the native Cache API Laravel provides. But, to keep my code organized I want to avoid having the Cache part of the logic in my controllers like this:
use Illuminate\Support\Facades\Cache;
class WeatherController
{
public function show($place, WeatherLibrary $api)
{
return Cache::get($place, function() use ($place, $api) {
$result = $api->getWeatherFor($place);
Cache::put($place, $result, 60);
return $result;
});
}
}
What approach should I take to organize this? I was thinking about the repository pattern, but I'm not so sure if it's the correct way to do it, as repositories have, at least, CRUD-like operations, and this “repository” would have custom methods according to the external service business logic.
Upvotes: 0
Views: 324
Reputation: 5552
Going from bishop's comment, you could create a proxy class like this:
class WeatherLibraryProxy
{
/**
* @var WeatherLibrary
*/
protected $driver;
public function __construct(WeatherLibrary $driver)
{
$this->driver = $driver;
}
/**
* Catch all method calls and either serve results from the cache
* or defer to the underlying api driver and cache the result
*/
public function __call($method, $parameters)
{
$cache_key = $method . implode(',', $parameters);
return cache()->remember(
$cache_key,
now()->addMinutes(60),
function () use ($method, $parameters) {
return $this->driver->$method(...$parameters);
});
}
}
Now any shared functionality (like checking remaining rate limits) can be put into your Proxy class, which you use everywhere in your app instead of the underlying WeatherLibrary class.
Then in your controller, change WeatherLibrary
to WeatherLibraryProxy
:
class WeatherController
{
public function show($place, WeatherLibraryProxy $api)
{
return $api->getWeatherFor($place);
}
}
Laravel's service container should automatically inject the WeatherLibrary to your proxy's constructor. If it doesn't, then you can instruct Laravel how to build a new instance in your AppServiceProvider.php:
$this->app->bind(WeatherLibrary::class, function ($app) {
return new WeatherLibrary($arg1, $arg2, ...);
});
More on automatic injection: https://laravel.com/docs/6.0/container#automatic-injection
Upvotes: 2