Skip to content

Commit

Permalink
[SWP-2082] Caching with singleton like (#17)
Browse files Browse the repository at this point in the history
Added a new property withAuthentication to make have access from parent class. withAuthentication makes sure that the access token is fetched and attached to request header.
Renamed confusing httpClient for apiClient.
Added laravel compatibility table to README.md
Added phpcs
Added a cache provider, to allow different cache drivers/adapters usable by this library.
Added Laravel Cache Adapter supports from Laravel 5+.
Added AbstractCacheableRequest to cache requests, any request can be cached by extending AbstractCacheableRequest.
Added time conversion between seconds to minutes for access token expires_in response field.
  • Loading branch information
ersindemirtas-pp authored Nov 26, 2020
1 parent da257ac commit f6c4aa4
Show file tree
Hide file tree
Showing 42 changed files with 1,050 additions and 98 deletions.
46 changes: 45 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ A review is an evaluation of a publication, product, or company for example. In
"url": "git@github.com:pod-point/reviews-php.git"
}
```
2. Run the following command inside of the the desired project workspace.
2. Run the following command inside of the desired project workspace.
```bash
composer require pod-point/reviews-php
```
Expand Down Expand Up @@ -67,6 +67,14 @@ return [
];
```

The Trustpilot requires the access token to be cached, in order to ensure you do it when consuming this package ensure you need to set the cache driver.

Example:
```php
$cacheAdapter = new \PodPoint\Reviews\Cache\LaravelCacheAdapter();
\PodPoint\Reviews\Cache\CacheProvider::setInstance($cacheAdapter);
```

All the providers should respect the same interface.

```php
Expand All @@ -85,6 +93,22 @@ $trustpilot->merchant()->findReview((string) $reviewId);
$trustpilot->merchant()->getReviews((array) $serviceReviewsFilterOptions);
```

The ``PodPoint\Reviews\Providers\Trustpilot\Request\AccessTokenRequest`` requires Cache Adapter/Driver. If you are getting CacheAdapterException you must set a cache driver.

Example:
```
CacheProvider::setInstance(new LaravelCacheAdapter());
```

## Compatibility table
This package is compatible up to Laravel 7. If used with higher versions of Laravel, the guzzle package needs to be upgraded.

| Laravel Version | Package Version |
| ------------- | ------------- |
| ^5.2 | 0.1.* |
| ^6.0 | 0.1.* |
| ^7.0 | 0.1.* |

For more details about each provider request options see:
* [Trustpilot](https://github.com/Pod-Point/reviews-php/blob/master/src/Providers/Trustpilot/README.md)
* [ReviewsIO](https://github.com/Pod-Point/reviews-php/blob/master/src/Providers/ReviewsIo/README.md)
Expand All @@ -96,6 +120,26 @@ Reviews PHP follows [semantic versioning](https://semver.org/) specifications.
The MIT License (MIT). Please see [License File](https://github.com/Pod-Point/reviews-php/LICENCE) for more information.

## Development

### Caching

To cache Request's response, you must extend the Request class using the **AbstractCacheableRequest** class, this will automatically cache the response. The **AbstractCacheableRequest** has two optional parameters ``$cacheTtl`` and ``$cacheKey``, these can be overridden in demanded request. If no ``$cacheKey`` is set the ``getCacheableKey`` is used to set the cache key and it will hash the class name using sha1 to have unique cacheKey.

If the Request class requires customisation for send method, make sure to call the ``parent::send();`` method which does the cache calls.

If the cache TTL is in the response of the api request instead of using **AbstractCacheableRequest** use the **AbstractHasCacheTtlInResponse** class. The **AbstractHasCacheTtlInResponse** has ``$cacheTtlResponseField`` which defines the key holds the cache TTL in the response and will be used when setting cache.

#### CacheProvider

The CacheProvider is a singleton a special class, provides the adapters to be initialized within the ``AbstractCacheableRequest::__construct``.

#### Adding new Cache Adapters
The ``PodPoint\Reviews\Cache\CacheProvider`` acts a Cache Adapter/Driver provider, the cache adapter can be replaced using ``CacheProvider::setInstance`` or the ``CacheProvider::getInstance`` can be updated to return Cache Adapter/Driver.

#### Laravel Caching

By default, the ``PodPoint\Reviews\LaravelServiceProvider`` registers the LaravelCacheAdapter as an instance, if needs to be overridden a new extended ServiceProvider can be created to override the protected setCacheAdapter method.

### Testing

This project uses PHPUnit, run the following command to run the tests:
Expand Down
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
],
"require": {
"php": "^7.1",
"guzzlehttp/guzzle": "^6.5"
"guzzlehttp/guzzle": "^6.5",
"psr/simple-cache": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^7.5",
"mockery/mockery": "^1.3",
"orchestra/testbench": "^3.4"
"orchestra/testbench": "^3.4",
"squizlabs/php_codesniffer": "^3.5"
},
"autoload": {
"psr-4": {
Expand Down
20 changes: 10 additions & 10 deletions src/AbstractApiClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,26 @@ abstract class AbstractApiClient implements ApiClientInterface

/**
* The base uri that will be used to make request.
* @var
*
* @var string
*/
protected $baseUri;

/**
* AbstractApiClient constructor, creates an instance of http client.
*
* @param ClientInterface|null $httpClient
*/
public function __construct(ClientInterface $httpClient = null)
{
$this->httpClient = $httpClient ?? new Client([
'base_uri' => $this->baseUri,
]);
$this->httpClient = $httpClient ?? new Client(['base_uri' => $this->baseUri]);
}

/**
* Sends request to API with or without pre authentication and returns response.
*
* @param Request $request
* @param bool $withAuthentication
* @param boolean $withAuthentication
*
* @return ResponseInterface
*
Expand Down Expand Up @@ -80,6 +79,7 @@ public function getHttpClient(): ClientInterface
* Sets httpClient.
*
* @param ClientInterface $httpClient
*
* @return $this
*/
public function setHttpClient(ClientInterface $httpClient)
Expand All @@ -95,6 +95,7 @@ public function setHttpClient(ClientInterface $httpClient)
* When the response content body is empty, json encode will fail and return empty array instead.
*
* @param ResponseInterface $response
*
* @return array
*/
public function getResponseJson(ResponseInterface $response): array
Expand All @@ -112,13 +113,12 @@ public function getResponseJson(ResponseInterface $response): array
*/
public function addDefaultRequestHeaders(Request &$request)
{
foreach ($this->defaultRequestHeaders as $headerKey => $headerValue)
{
if(!$request->hasHeader($headerKey)) {
foreach ($this->defaultRequestHeaders as $headerKey => $headerValue) {
if (!$request->hasHeader($headerKey)) {
$request = $request->withHeader($headerKey, $headerValue);
}
}
}

public abstract function addAuthenticationHeader(Request &$request);
abstract public function addAuthenticationHeader(Request &$request);
}
68 changes: 68 additions & 0 deletions src/Cache/AbstractHasCacheTtlInResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace PodPoint\Reviews\Cache;

use PodPoint\Reviews\Request\AbstractCacheableRequest;

/**
* Class AbstractHasCacheTtlInResponse
*/
abstract class AbstractHasCacheTtlInResponse extends AbstractCacheableRequest
{
/**
* The name of the field which has expires or ttl.
*
* @var string
*/
protected $cacheTtlResponseField;

/**
* @return string
*/
public function getCacheTtlResponseField(): string
{
return $this->cacheTtlResponseField;
}

/**
* @param string $cacheTtlResponseField
*/
public function setCacheTtlResponseField(string $cacheTtlResponseField): void
{
$this->cacheTtlResponseField = $cacheTtlResponseField;
}

/**
* Returns the ttl from the body of the response.
*
* @param array $responseBody
* @param int|null $default
*
* @return int
*/
public function getCacheableTtlFromResponse(array $responseBody, int $default = null): int
{
if ($this->cacheTtlResponseField && isset($responseBody[$this->cacheTtlResponseField])) {
$ttl = (int) $responseBody[$this->cacheTtlResponseField];

return $this->convertFromSecondsToMinutes($ttl);
}

return $default;
}

/**
* Converts from seconds to minutes.
*
* @param $value
* @return int
*/
protected function convertFromSecondsToMinutes(int $value): int
{
if ($value <= 0) {
return 0;
}

return (int) $value / 60;
}
}
53 changes: 53 additions & 0 deletions src/Cache/CacheProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace PodPoint\Reviews\Cache;

use PodPoint\Reviews\Exceptions\CacheAdapterException;

/**
* Class CacheProvider
*/
class CacheProvider
{
/**
* Cache instance of driver/adapter.
*
* @var null
*/
private static $instance = null;

/**
* CacheFactory constructor.
*/
private function __construct()
{
//
}

/**
* Set instance.
*
* @param mixed $instance
*
* @return void
*/
public static function setInstance($instance): void
{
self::$instance = $instance;
}

/**
* Get instance of Cache Driver/Adapter.
*
* @return null
* @throws CacheAdapterException
*/
public static function getInstance()
{
if (self::$instance == null) {
throw new CacheAdapterException();
}

return self::$instance;
}
}
111 changes: 111 additions & 0 deletions src/Cache/LaravelCacheAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<?php

namespace PodPoint\Reviews\Cache;

use Illuminate\Support\Facades\Cache;

/**
* Class LaravelCache
*/
class LaravelCacheAdapter
{
/**
* Returns cache by key.
*
* @param string $key
* @param null $default
*
* @return mixed
*/
public function get(string $key, $default = null)
{
return Cache::get($key, $default);
}

/**
* Check if cache exists.
*
* @param $key
*
* @return boolean
*/
public function has($key): bool
{
return Cache::has($key);
}

/**
* Sets cache.
*
* @param string $key
* @param $value
* @param null $ttl
*
* @return boolean
*/
public function set(string $key, $value, $ttl = null)
{
return Cache::put($key, $value, $ttl);
}

/**
* Deletes cache.
*
* @param string $key
*
* @return boolean
*/
public function delete(string $key): bool
{
return Cache::forget($key);
}

public function clear()
{
//
}

/**
* Returns multiple caches.
*
* @param array $keys
* @param null $default
*
* @return array
*/
public function getMultiple(array $keys, $default = null)
{
$cache = [];

foreach ($keys as $key) {
$cache[$key] = Cache::get($key);
}

return $cache;
}

/**
* Sets multiple cache values.
*
* @param array $values
* @param integer|null $ttl
*/
public function setMultiple(array $values, int $ttl = null)
{
foreach ($values as $cacheKey => $value) {
$this->set($cacheKey, $value, $ttl);
}
}

/**
* Deletes multiple cache keys.
*
* @param array $keys
*/
public function deleteMultiple(array $keys)
{
foreach ($keys as $key) {
$this->delete($key);
}
}
}
Loading

0 comments on commit f6c4aa4

Please sign in to comment.