diff --git a/README.md b/README.md
index 241ce91..db630c4 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# Kununu Projections
-Projections are a temporary storage and are a way to access data faster than fetching it from a regular storage.
+Projections are a temporary storage and are a way to access data faster than fetching it from a regular storage (e.g. getting data from a cache vs from the database).
-Data needs to be projected first, so then it's projection can be accessed, without the need to access the actual source of truth, which is usually a slower process.
+Data needs to be projected first so that its projection can be accessed without the need to access the actual source of truth, which is usually a slower process.
Projections have a short lifetime, and are not updated automatically if data in source of truth changes. So they need to be frequently refreshed.
@@ -17,14 +17,15 @@ It also includes an implementation of the projection over the Symfony's Tag Awar
### Add custom private repositories to composer.json
```json
-"repositories": [
- ...
- {
- "type": "vcs",
- "url": "https://github.com/kununu/projections.git",
- "no-api": true
- }
-]
+{
+ "repositories": [
+ {
+ "type": "vcs",
+ "url": "https://github.com/kununu/projections.git",
+ "no-api": true
+ }
+ ]
+}
```
### Require this library to your project
@@ -48,23 +49,37 @@ composer require jms/serializer-bundle
## Usage
-### `ProjectionItem`
+### `ProjectionItemInterface`
-A projection is represented by an object that implements `ProjectionItem` interface. This object is called **projection item** and holds on its properties the data to be projected.
+A projection is represented by an object that implements `ProjectionItemInterface` interface. This object is called **projection item** and holds on its properties the data to be projected.
+
+```php
+interface ProjectionItemInterface
+{
+ public function getKey(): string;
+
+ public function getTags(): Tags;
+}
+```
Here's an example of projection item:
```php
namespace Kununu\Example;
-class ExampleProjectionItem implements ProjectionItem
+use Kununu\Projections\ProjectionItemInterface;
+use Kununu\Projections\Tag\ProjectionTagGenerator;
+
+final class ExampleProjectionItem implements ProjectionItemInterface
{
+ use ProjectionTagGenerator;
+
private $id;
private $someValue;
public function __construct(string $id, string $someValue)
{
- $this->id = $id;
+ $this->id = $id;
$this->someValue = $someValue;
}
@@ -75,62 +90,66 @@ class ExampleProjectionItem implements ProjectionItem
public function getTags(): Tags
{
- return new Tags(new Tag('example_tag'), new Tag($this->id));
+ // This is in ProjectionTagGenerator trait
+ return ProjectionTagGenerator::createTagsFromArray('example_tag', $this->id);
+ // It is functional equivalent to:
+ //
+ // return new Tags(new Tag('example_tag'), new Tag($this->id));
}
}
```
-The `getKey()` and `getTags()` methods must be implemented.
-
-The `getKey()` method is the unique identifier of the projection. If projections are stored with the same key then they will be overridden.
+The following methods must be implemented:
+
+* `getKey()` returns the unique identifier of the projection. If projections are stored with the same key then they will be overridden.
-The `getTags()` method serves to mark the projection item with a tag. These can be used later for bulk operations on projections, like delete all projections with a certain tag.
-
-### `ProjectionItemIterable`
+* `getTags()` returns the tags marked on the projection item. These can be used later for bulk operations on projections, like delete all projections with a certain tag.
-The package also offers an extension of `ProjectionItem` designed to store generic data (the data itself will be any PHP iterable, like an array).
+### `ProjectionItemIterableInterface`
-The interface is `ProjectionItemIterable` and must implement the following methods:
+The package also offers an extension of `ProjectionItemInterface` designed to store generic data (the data itself will be any PHP iterable, like an array).
+
+The interface is `ProjectionItemIterableInterface` and must implement the following methods:
```php
- interface ProjectionItemIterable extends ProjectionItem
- {
- public function storeData(iterable $data): ProjectionItemArrayData;
+interface ProjectionItemIterableInterface extends ProjectionItemInterface
+{
+ public function storeData(iterable $data): ProjectionItemIterableInterface;
- public function data(): iterable;
- }
+ public function data(): iterable;
+}
```
-A trait called `ProjectionItemIterableTrait` is provided with those methods already implemented and with the data stored as an array, so just use it your projection item classes and you're good to go.
+A trait called `ProjectionItemIterableTrait` is provided with those methods already implemented and with the data stored as an array, so just use it your projection item classes, and you're good to go.
-Just bear in mind that the trait is only implementing the methods defined in `ProjectionItemIterable` and not those of `ProjectionItem` so it is still responsibility of your projection item class to implement them!
+Just bear in mind that the trait is only implementing the methods defined in `ProjectionItemIterableInterface` and not those of `ProjectionItemInterface` so it is still responsibility of your projection item class to implement them!
-### `ProjectionRepository`
+### `ProjectionRepositoryInterface`
-The **projection item** is projected through a repository which implements `ProjectionRepository` interface.
+The **projection item** is projected through a repository which implements `ProjectionRepositoryInterface` interface.
This holds methods to get, add and delete the projections. The methods are used by passing a **projection item** object.
- ```php
- interface ProjectionRepository
- {
- public function add(ProjectionItem $item): void;
+```php
+interface ProjectionRepositoryInterface
+{
+ public function add(ProjectionItemInterface $item): void;
- public function addDeferred(ProjectionItem $item): void;
+ public function addDeferred(ProjectionItemInterface $item): void;
- public function flush(): void;
+ public function flush(): void;
- public function get(ProjectionItem $item): ?ProjectionItem;
+ public function get(ProjectionItemInterface $item): ?ProjectionItemInterface;
- public function delete(ProjectionItem $item): void;
+ public function delete(ProjectionItemInterface $item): void;
- public function deleteByTags(Tags $tags): void;
- }
- ```
+ public function deleteByTags(Tags $tags): void;
+}
+```
* `add()` method immediately projects the item.
* `addDeferred()` method sets items to be projected, but they are only projected when `flush()` is called.
- * `get()` method gets a projected item. If it not projected, then `null` is returned.
+ * `get()` method gets a projected item. If it is not projected, then `null` is returned.
* `delete()` method deletes item from projection
* `deleteByTags()` method deletes all projected items that have at least one of the tags passed as argument
@@ -145,11 +164,7 @@ This repository is called `CachePoolProjectionRepository`.
Besides the Symfony's Tag Aware Cache Pool interface, this repository uses the JMS Serializer. The following snippet is the repository's constructor:
```php
- public function __construct(TagAwareAdapterInterface $cachePool, SerializerInterface $serializer)
- {
- $this->cachePool = $cachePool;
- $this->serializer = $serializer;
- }
+public function __construct(TagAwareAdapterInterface $cachePool, SerializerInterface $serializer);
```
So there is the need to define the serialization config for the Projection items. For instance, for the previous `ExampleProjectionItem` example, here is an example of the JMS Serializer XML config for this class:
@@ -158,10 +173,10 @@ So there is the need to define the serialization config for the Projection items
-
-
+
+
-
+
```
This should be saved in a `ExampleProjectionItem.xml` file.
@@ -176,14 +191,13 @@ Create a cache pool with Symfony config. Here's an example for the cache pool to
```yaml
framework:
- cache:
- prefix_seed: "example"
- default_memcached_provider: "memcached://172.0.0.1:1121"
- pools:
- example.cache.projections:
- adapter: cache.adapter.memcached
- default_lifetime: 3600
-
+ cache:
+ prefix_seed: "example"
+ default_memcached_provider: "memcached://172.0.0.1:1121"
+ pools:
+ example.cache.projections:
+ adapter: cache.adapter.memcached
+ default_lifetime: 3600
```
This automatically creates a `example.cache.projections` service. In this case the lifetime for the projections is 3600 seconds = 1 hour.
@@ -192,34 +206,37 @@ Here is assumed that the `jms/serializer-bundle` was required. The minimum confi
```yaml
jms_serializer:
- metadata:
- directories:
- projections:
- namespace_prefix: "Kununu\Example"
- path: "%kernel.root_dir%/Repository/Resources/config/serializer"
+ metadata:
+ directories:
+ projections:
+ namespace_prefix: "Kununu\Example"
+ path: "%kernel.root_dir%/Repository/Resources/config/serializer"
```
where `%kernel.root_dir%/Repository/Resources/config/serializer` is the directory where is the JMS Serializer configuration files for the projection items, which means the previous `ExampleProjectionItem.xml` file is inside.
Please notice that the namespace prefix of the projection item class is also defined in here.
-Next define the `CachePoolProjectionRepository` as a Symfony service:
+Next define your custom instance of `CachePoolProjectionRepository` as a Symfony service:
```yaml
services:
- _defaults:
- autowire: true
- autoconfigure: true
-
- Kununu/Projections/Repository/CachePoolProjectionRepository:
- class: Kununu\Projections\Repository\CachePoolProjectionRepository
- arguments:
- - '@example.cache.projections'
- - '@jms_serializer'
-
- example.cache.projections.tagged:
- class: Symfony\Component\Cache\Adapter\TagAwareAdapter
- decorates: 'example.cache.projections'
+ _defaults:
+ autowire: true
+ autoconfigure: true
+
+ # A tag-aware cache adapter
+ example.cache.projections.tagged:
+ class: Symfony\Component\Cache\Adapter\TagAwareAdapter
+ decorates: 'example.cache.projections'
+
+ # My cached repository
+ app.my.cached.repository:
+ class: Kununu\Projections\Repository\CachePoolProjectionRepository
+ arguments:
+ - '@example.cache.projections'
+ - '@jms_serializer'
+
```
Note that the `TagAwareAdapter` is added as a decorator for the cache pool service.
@@ -228,35 +245,62 @@ Now you can inject the repository's service. Example:
```yaml
App\Infrastructure\UseCase\Query\GetProfileCommonByUuid\DataProvider\ProjectionDataProvider:
- arguments:
- - '@Kununu/Projections/Repository/CachePoolProjectionRepository'
+ arguments:
+ - '@app.my.cached.repository'
```
-And inside the respective class we should depend only on the `ProjectionRepository` interface.
+And inside the respective class we should depend only on the `ProjectionRepositoryInterface` interface instance to project/get/delete data from the cache.
```php
-class ProjectionDataProvider
+use Kununu\Projections\ProjectionRepositoryInterface;
+use Kununu\Projections\Tag\Tag;
+use Kununu\Projections\Tag\Tags;
+
+final class ProjectionDataProvider
{
private $projectionRepository;
- public function __construct(ProjectionRepository $projectionRepository)
+ public function __construct(ProjectionRepositoryInterface $projectionRepository)
{
$this->projectionRepository = $projectionRepository;
}
- ...
+ public function someAction(): void
+ {
+ // We can use the projection repository to fetch/store data in the cache
+ $item = new MyProjectionItemClass('the key for this item');
+ $value = $this->projectionRepository->get($item);
+
+ // Cache miss
+ if (null === $value) {
+ // Is up to you to define what data to store on the item
+ $item->setProperty1('value');
+ $item->setProperty2(2040);
+
+ $this->projectionRepository->add($item);
+ } else {
+ // Cache hit
+ // Use item fetched from the cache
+ var_export($item->getProperty1());
+ }
+ }
+
+ public function removeItems(): void
+ {
+ // Remove all items in the cache that are tagged with 'a-tag'...
+ $this->projectionRepository->deleteByTags(new Tags(new Tag('a-tag')));
+ }
}
```
Now we can start reading, setting and deleting from the cache pool :)
+### `CacheCleanerInterface`
-### `CacheCleaner`
-
-Sometimes we need to force the cleaning of caches. In order to do this the library offers an interface called `CacheCleaner`:
+Sometimes we need to force the cleaning of caches. In order to do this the library offers an interface called `CacheCleanerInterface`:
```php
-interface CacheCleaner
+interface CacheCleanerInterface
{
public function clear(): void;
}
@@ -266,30 +310,28 @@ It only has one method called `clear` which should as the name says clear the da
#### AbstractCacheCleanerByTags
-The interface `CacheCleaner` by itself is not really useful. One of the most common cases when cleaning/invalidating caches is to delete a series of data.
+The interface `CacheCleanerInterface` by itself is not really useful. One of the most common cases when cleaning/invalidating caches is to delete a series of data.
The `AbstractCacheCleanerByTags` provides a base class that will allow you to invalidate cache items by **Tags**.
-As we already have seen, the `ProjectionRepository` already has a method called `deleteByTags`, so this class will combine that usage and abstract it.
+As we already have seen, the `ProjectionRepositoryInterface` already has a method called `deleteByTags`, so this class will combine that usage and abstract it.
-So your cache cleaner class by tags should be instantiated with a `ProjectionRepository` instance (and also with a PSR logger instance), and simply implement the `getTags` method which must return the `Tags` collection that will be passed to the `deleteByTags` on the repository instance.
+So your cache cleaner class by tags should be instantiated with a `ProjectionRepositoryInterface` instance (and also with a PSR logger instance), and simply implement the `getTags` method which must return the `Tags` collection that will be passed to the `deleteByTags` on the repository instance.
```php
public function __construct(ProjectionRepository $projectionRepository, LoggerInterface $logger);
-
abstract protected function getTags(): Tags;
```
Example:
```php
-
-use Kununu\Projections\CacheCleaner\CacheCleaner;
+use Kununu\Projections\CacheCleaner\CacheCleanerInterface;
use Kununu\Projections\CacheCleaner\AbstractCacheCleanerByTags;
-class MyCacheCleaner extends AbstractCacheCleanerByTags
+final class MyCacheCleaner extends AbstractCacheCleanerByTags
{
protected function getTags(): Tags
{
@@ -300,11 +342,11 @@ class MyCacheCleaner extends AbstractCacheCleanerByTags
}
};
-class MyClass
+final class MyClass
{
private $cacheCleaner;
- public function __construct(CacheCleaner $cacheCleaner)
+ public function __construct(CacheCleanerInterface $cacheCleaner)
{
$this->cacheCleaner = $cacheCleaner;
}
@@ -322,7 +364,6 @@ $myClass = new MyClass($cacheCleaner);
// When I call `myMethod` it will call `MyCacheCleaner` and delete all cache entries that
// are tagged with 'my-tag1' and 'my-tag2'
$myClass->myMethod();
-
```
##### AbstractCacheCleanerTestCase
@@ -351,7 +392,7 @@ The `TAGS` constant must be the tags that you expect that your cache cleaner cla
For the example above we are expecting that `MyCacheCleaner::getTags` will return a `Tags` collection with the same tags defined in the constant, e.g.:
```php
-class MyCacheCleaner extends AbstractCacheCleanerByTags
+final class MyCacheCleaner extends AbstractCacheCleanerByTags
{
protected function getTags(): Tags
{
@@ -370,13 +411,12 @@ Example:
```php
-use Kununu\Projections\CacheCleaner\CacheCleaner;
+use Kununu\Projections\CacheCleaner\CacheCleanerInterface;
use Kununu\Projections\CacheCleaner\AbstractCacheCleanerByTags;
-
// Continuing our example, let's add more cache cleaners...
-class MySecondCacheCleaner extends AbstractCacheCleanerByTags
+final class MySecondCacheCleaner extends AbstractCacheCleanerByTags
{
protected function getTags(): Tags
{
@@ -384,8 +424,7 @@ class MySecondCacheCleaner extends AbstractCacheCleanerByTags
}
};
-
-class MyThirdCacheCleaner implements CacheCleaner
+final class MyThirdCacheCleaner implements CacheCleanerInterface
{
public function clear(): void
{
@@ -426,26 +465,41 @@ Usually the flow is always the same.
- Return the data
- Rinse and repeat...
-So the `AbstractCachedProvider` will help you in reducing the boiler plate for those scenarios.
+So the `AbstractCachedProvider` will help you in reducing the boilerplate for those scenarios.
Your "provider" class should extend it and for each method where you need to use the flow described above you just need to call the `getAndCacheData` method:
```php
-protected function getAndCacheData(ProjectionItemIterable $item, callable $dataGetter): ?iterable;
+protected function getAndCacheData(
+ ProjectionItemIterableInterface $item,
+ callable $dataGetter,
+ callable ...$preProjections
+): ?iterable;
```
- The `$item` parameter is a projection item that will be used to build the cache key
- The `$dataGetter` is your custom function that should return an `iterable` with you data or null if no data was found
+ - Signature of the callable function:
+ - `function(): ?iterable`
+- The `$preProjections` are your custom functions that should manipulate the item/data before they are projected:
+ - Signature of the callable functions:
+ - `function(ProjectionItemIterableInterface $item, iterable $data): ?iterable`
+ - The callable should return the `$data` that will be stored on the `ProjectionItemIterableInterface` instance via `storeData` method, so **do not call that method directly** in your callable!
+ - The data will be propagated for each callable passed
+ - If you want to *not store* data on the cache, then your callable should return `null` and the pre-processor chain will end
An example:
```php
+use Kununu\Projections\ProjectionRepositoryInterface;
+use Kununu\Projections\ProjectionItemIterableInterface;
+
interface MyProviderInterface
{
public function getCustomerData(int $customerId): ?iterable;
}
-class MyProvider implements MyProviderInterface
+final class MyProvider implements MyProviderInterface
{
public function getCustomerData(int $customerId): ?iterable
{
@@ -456,12 +510,18 @@ class MyProvider implements MyProviderInterface
}
}
-class MyCachedProvider extends AbstractCachedProvider implements MyProviderInterface
+/**
+ * This class will decorate any MyProviderInterface instance (e.g. MyProvider) to use projections and read/write from cache
+ */
+final class MyCachedProvider extends AbstractCachedProvider implements MyProviderInterface
{
private $myProvider;
- public function __construct(MyProviderInterface $myProvider, ProjectionRepository $projectionRepository, LoggerInterface $logger)
- {
+ public function __construct(
+ MyProviderInterface $myProvider,
+ ProjectionRepositoryInterface $projectionRepository,
+ LoggerInterface $logger
+ ) {
parent::__construct($projectionRepository, $logger);
$this->myProvider = $myProvider;
}
@@ -470,8 +530,28 @@ class MyCachedProvider extends AbstractCachedProvider implements MyProviderInter
{
return $this->getAndCacheData(
new CustomerByIdProjectionItem($customerId),
+ // This callable will get the data when there is a cache miss (e.g. data was not found on the cache)
function() use ($customerId): ?iterable {
return $this->myProvider->getCustomerData($customerId);
+ },
+ // Additional callables to do pre-processing before projecting the items to the cache. They are optional
+ // and only called in the event of a cache miss (and after the data getter callable returns the data)
+ function(ProjectionItemIterableInterface $item, iterable $data): ?iterable {
+ // A case where I don't want to store the projection because it does not have
+ // relevant information
+ if($data['customer_id'] > 200) {
+ return null;
+ }
+
+ // We could also add more info here...
+ // E.g.: we fetch some data from database, but we need to call some external API to get additional data
+ // This is a perfect place to do that
+ $data['new_value'] = 500;
+
+ // We could also set/change properties on the item
+ $item->setUuid('f712e7af-41d0-4c3d-bbdb-0643197f9eed');
+
+ return $data;
}
);
}
@@ -492,7 +572,7 @@ In order to help you unit testing your cached providers implementations the `Cac
Just make you test class extend it and override the `METHODS` constant and implement the `getProvider` method.
-The `getProvider` is were you should create the "decorated" cached provider you want to test. E.g:
+The `getProvider` is where you should create the "decorated" cached provider you want to test. E.g:
```php
protected function getProvider($originalProvider): AbstractCachedProvider
@@ -510,10 +590,9 @@ The `METHODS` constant should contain the methods of your provider class.
For our example above to test the `getCustomerData` method:
```php
- protected const METHODS = [
- 'getCustomerData',
- ];
-
+protected const METHODS = [
+ 'getCustomerData',
+];
```
Now, for each method defined in the `METHODS` constant you need to create a PHPUnit data provider method.
diff --git a/composer.json b/composer.json
index 75bb9e3..04839e5 100644
--- a/composer.json
+++ b/composer.json
@@ -14,7 +14,7 @@
"symfony/browser-kit": "^4.4",
"symfony/cache": "^4.4",
"jms/serializer-bundle": "^3.8",
- "kununu/testing-bundle": "^15.0",
+ "kununu/testing-bundle": "^16.0",
"kununu/scripts": "*"
},
"suggest": {
@@ -30,5 +30,18 @@
"psr-4": {
"Kununu\\Projections\\Tests\\": "tests/"
}
+ },
+ "scripts": {
+ "test": "phpunit --no-coverage tests",
+ "test-coverage": "phpunit --coverage-clover tests/.results/coverage.xml --coverage-html tests/.results/html/ tests"
+ },
+ "scripts-descriptions": {
+ "test": "Run all tests",
+ "test-coverage": "Run all tests with coverage report"
+ },
+ "config": {
+ "allow-plugins": {
+ "kununu/scripts": true
+ }
}
}
diff --git a/src/CacheCleaner/AbstractCacheCleanerByTags.php b/src/CacheCleaner/AbstractCacheCleanerByTags.php
index 28d9462..0240c07 100644
--- a/src/CacheCleaner/AbstractCacheCleanerByTags.php
+++ b/src/CacheCleaner/AbstractCacheCleanerByTags.php
@@ -3,16 +3,16 @@
namespace Kununu\Projections\CacheCleaner;
-use Kununu\Projections\ProjectionRepository;
+use Kununu\Projections\ProjectionRepositoryInterface;
use Kununu\Projections\Tag\Tags;
use Psr\Log\LoggerInterface;
-abstract class AbstractCacheCleanerByTags implements CacheCleaner
+abstract class AbstractCacheCleanerByTags implements CacheCleanerInterface
{
private $projectionRepository;
private $logger;
- public function __construct(ProjectionRepository $projectionRepository, LoggerInterface $logger)
+ public function __construct(ProjectionRepositoryInterface $projectionRepository, LoggerInterface $logger)
{
$this->projectionRepository = $projectionRepository;
$this->logger = $logger;
diff --git a/src/CacheCleaner/CacheCleanerChain.php b/src/CacheCleaner/CacheCleanerChain.php
index 0b3e938..bbde4e1 100644
--- a/src/CacheCleaner/CacheCleanerChain.php
+++ b/src/CacheCleaner/CacheCleanerChain.php
@@ -3,11 +3,11 @@
namespace Kununu\Projections\CacheCleaner;
-final class CacheCleanerChain implements CacheCleaner
+final class CacheCleanerChain implements CacheCleanerInterface
{
private $cacheCleaners;
- public function __construct(CacheCleaner ...$cacheCleaners)
+ public function __construct(CacheCleanerInterface ...$cacheCleaners)
{
$this->cacheCleaners = $cacheCleaners;
}
diff --git a/src/CacheCleaner/CacheCleaner.php b/src/CacheCleaner/CacheCleanerInterface.php
similarity index 78%
rename from src/CacheCleaner/CacheCleaner.php
rename to src/CacheCleaner/CacheCleanerInterface.php
index ab61225..449dfad 100644
--- a/src/CacheCleaner/CacheCleaner.php
+++ b/src/CacheCleaner/CacheCleanerInterface.php
@@ -3,7 +3,7 @@
namespace Kununu\Projections\CacheCleaner;
-interface CacheCleaner
+interface CacheCleanerInterface
{
public function clear(): void;
}
diff --git a/src/Exception/ProjectionException.php b/src/Exception/ProjectionException.php
index 040b888..7c5e151 100644
--- a/src/Exception/ProjectionException.php
+++ b/src/Exception/ProjectionException.php
@@ -1,4 +1,5 @@
-data = $data;
} else {
@@ -23,9 +24,7 @@ public function storeData(iterable $data): ProjectionItemIterable
return $this;
}
- throw new BadMethodCallException(
- sprintf('Class using this trait must be a %s', ProjectionItemIterable::class)
- );
+ throw new BadMethodCallException(sprintf('Class using this trait must be a %s', ProjectionItemIterableInterface::class));
}
public function data(): iterable
diff --git a/src/ProjectionRepository.php b/src/ProjectionRepository.php
deleted file mode 100644
index c62c8e6..0000000
--- a/src/ProjectionRepository.php
+++ /dev/null
@@ -1,20 +0,0 @@
-projectionRepository = $projectionRepository;
$this->logger = $logger;
}
- protected function getAndCacheData(ProjectionItemIterable $item, callable $dataGetter): ?iterable
- {
+ protected function getAndCacheData(
+ ProjectionItemIterableInterface $item,
+ callable $dataGetter,
+ callable ...$preProjections
+ ): ?iterable {
$this->logger->info('Getting data from cache', [self::CACHE_KEY => $item->getKey()]);
$projectedItem = $this->projectionRepository->get($item);
- if ($projectedItem instanceof ProjectionItemIterable) {
+ if ($projectedItem instanceof ProjectionItemIterableInterface) {
$this->logger->info(
'Item hit! Returning data from the cache',
[
@@ -39,16 +42,43 @@ protected function getAndCacheData(ProjectionItemIterable $item, callable $dataG
}
$this->logger->info('Item not hit! Fetching data...', [self::CACHE_KEY => $item->getKey()]);
+
$data = $dataGetter();
+
if (is_iterable($data)) {
- $this->projectionRepository->add($item->storeData($data));
- $this->logger->info('Item saved into cache and returned', [self::CACHE_KEY => $item->getKey(), self::DATA => $data]);
+ // Manipulate data before projection if callers are defined
+ foreach ($preProjections as $preProjection) {
+ $data = $preProjection($item, $data);
+ // If pre-projection callable returns null means we do not have relevant information.
+ // We will not store the item in the cache and will break the pre-projection chain
+ if (null === $data) {
+ $this->logger->info('Item not stored in the cache!', [self::DATA => $data]);
+ $data = null;
+
+ break;
+ }
+ }
- return $data;
+ if (null !== $data) {
+ $this->projectionRepository->add($item->storeData($data));
+ $this->logger->info(
+ 'Item saved into cache and returned',
+ [self::CACHE_KEY => $item->getKey(), self::DATA => $data]
+ );
+ }
+ } else {
+ $this->logger->info('No data fetched and stored into cache!', [self::CACHE_KEY => $item->getKey()]);
+ $data = null;
}
- $this->logger->info('No data fetched and stored into cache!', [self::CACHE_KEY => $item->getKey()]);
+ return $data;
+ }
+
+ protected function invalidateCacheItemByKey(ProjectionItemIterableInterface $projectionItem): self
+ {
+ $this->logger->info('Deleting cache item', ['cache_key' => $projectionItem->getKey()]);
+ $this->projectionRepository->delete($projectionItem);
- return null;
+ return $this;
}
}
diff --git a/src/Repository/CachePoolProjectionRepository.php b/src/Repository/CachePoolProjectionRepository.php
index d752713..c2b025c 100644
--- a/src/Repository/CachePoolProjectionRepository.php
+++ b/src/Repository/CachePoolProjectionRepository.php
@@ -1,16 +1,17 @@
-cachePool = $cachePool;
+ $this->cachePool = $cachePool;
$this->serializer = $serializer;
}
- public function add(ProjectionItem $item): void
+ public function add(ProjectionItemInterface $item): void
{
$cacheItem = $this->createCacheItem($item);
@@ -32,7 +33,7 @@ public function add(ProjectionItem $item): void
}
}
- public function addDeferred(ProjectionItem $item): void
+ public function addDeferred(ProjectionItemInterface $item): void
{
$cacheItem = $this->createCacheItem($item);
@@ -48,7 +49,7 @@ public function flush(): void
}
}
- public function get(ProjectionItem $item): ?ProjectionItem
+ public function get(ProjectionItemInterface $item): ?ProjectionItemInterface
{
$cacheItem = $this->cachePool->getItem($item->getKey());
@@ -59,7 +60,7 @@ public function get(ProjectionItem $item): ?ProjectionItem
return $this->serializer->deserialize($cacheItem->get(), get_class($item), self::JMS_SERIALIZER_FORMAT);
}
- public function delete(ProjectionItem $item): void
+ public function delete(ProjectionItemInterface $item): void
{
if (!$this->cachePool->deleteItem($item->getKey())) {
throw new ProjectionException('Not possible to delete projection item on cache pool');
@@ -73,7 +74,7 @@ public function deleteByTags(Tags $tags): void
}
}
- private function createCacheItem(ProjectionItem $item): CacheItemInterface
+ private function createCacheItem(ProjectionItemInterface $item): CacheItemInterface
{
$cacheItem = $this->cachePool->getItem($item->getKey());
$cacheItem->set($this->serializer->serialize($item, self::JMS_SERIALIZER_FORMAT));
diff --git a/src/Tag/ProjectionTagGenerator.php b/src/Tag/ProjectionTagGenerator.php
index 59b8b1c..3fae8af 100644
--- a/src/Tag/ProjectionTagGenerator.php
+++ b/src/Tag/ProjectionTagGenerator.php
@@ -1,17 +1,19 @@
-tagsAsStrings);
+ return array_keys($this->tags);
}
private function add(Tag $tag): void
{
$value = $tag->value();
- if (isset($this->tagsAsStrings[$value])) {
+ if (isset($this->tags[$value])) {
return;
}
- $this->tags[] = $tag;
- $this->tagsAsStrings[$value] = true;
+ $this->tags[$value] = true;
}
}
diff --git a/src/TestCase/CacheCleaner/AbstractCacheCleanerTestCase.php b/src/TestCase/CacheCleaner/AbstractCacheCleanerTestCase.php
index 0edb715..6995e01 100644
--- a/src/TestCase/CacheCleaner/AbstractCacheCleanerTestCase.php
+++ b/src/TestCase/CacheCleaner/AbstractCacheCleanerTestCase.php
@@ -4,9 +4,9 @@
namespace Kununu\Projections\TestCase\CacheCleaner;
use JMS\Serializer\SerializerInterface;
-use Kununu\Projections\CacheCleaner\CacheCleaner;
+use Kununu\Projections\CacheCleaner\CacheCleanerInterface;
use Kununu\Projections\Exception\ProjectionException;
-use Kununu\Projections\ProjectionRepository;
+use Kununu\Projections\ProjectionRepositoryInterface;
use Kununu\Projections\Repository\CachePoolProjectionRepository;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
@@ -55,7 +55,7 @@ public function testCacheCleanerFail(): void
$this->cacheCleaner->clear();
}
- abstract protected function getCacheCleaner(ProjectionRepository $projectionRepository, LoggerInterface $logger): CacheCleaner;
+ abstract protected function getCacheCleaner(ProjectionRepositoryInterface $projectionRepository, LoggerInterface $logger): CacheCleanerInterface;
protected function setUp(): void
{
diff --git a/src/TestCase/Provider/CachedProviderTestCase.php b/src/TestCase/Provider/CachedProviderTestCase.php
index ced4a2c..1dfed25 100644
--- a/src/TestCase/Provider/CachedProviderTestCase.php
+++ b/src/TestCase/Provider/CachedProviderTestCase.php
@@ -3,8 +3,8 @@
namespace Kununu\Projections\TestCase\Provider;
-use Kununu\Projections\ProjectionItemIterable;
-use Kununu\Projections\ProjectionRepository;
+use Kununu\Projections\ProjectionItemIterableInterface;
+use Kununu\Projections\ProjectionRepositoryInterface;
use Kununu\Projections\Provider\AbstractCachedProvider;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -20,19 +20,19 @@ abstract class CachedProviderTestCase extends TestCase
/**
* @dataProvider getAndCacheDataDataProvider
*
- * @param $originalProvider
- * @param string $method
- * @param array $args
- * @param ProjectionItemIterable $item
- * @param ProjectionItemIterable|null $projectedItem
- * @param iterable|null $providerData
+ * @param $originalProvider
+ * @param string $method
+ * @param array $args
+ * @param ProjectionItemIterableInterface $item
+ * @param ProjectionItemIterableInterface|null $projectedItem
+ * @param iterable|null $providerData
*/
public function testGetAndCacheData(
$originalProvider,
string $method,
array $args,
- ProjectionItemIterable $item,
- ?ProjectionItemIterable $projectedItem,
+ ProjectionItemIterableInterface $item,
+ ?ProjectionItemIterableInterface $projectedItem,
?iterable $providerData
): void {
// Get from cache
@@ -81,8 +81,13 @@ abstract protected function getProvider($originalProvider): AbstractCachedProvid
*
* @return mixed|MockObject
*/
- protected function createExternalProvider(string $providerClass, string $method, array $args, bool $expected, ?iterable $data)
- {
+ protected function createExternalProvider(
+ string $providerClass,
+ string $method,
+ array $args,
+ bool $expected,
+ ?iterable $data
+ ): MockObject {
$provider = $this->createMock($providerClass);
$invocationMocker = $provider
->expects($expected ? $this->once() : $this->never())
@@ -97,12 +102,12 @@ protected function createExternalProvider(string $providerClass, string $method,
}
/**
- * @return ProjectionRepository|MockObject
+ * @return MockObject|ProjectionRepositoryInterface
*/
- protected function getProjectionRepository(): ProjectionRepository
+ protected function getProjectionRepository(): ProjectionRepositoryInterface
{
if (null === $this->projectionRepository) {
- $this->projectionRepository = $this->createMock(ProjectionRepository::class);
+ $this->projectionRepository = $this->createMock(ProjectionRepositoryInterface::class);
}
return $this->projectionRepository;
diff --git a/tests/Functional/App/Kernel.php b/tests/Functional/App/Kernel.php
index 4a78ba2..046e38e 100644
--- a/tests/Functional/App/Kernel.php
+++ b/tests/Functional/App/Kernel.php
@@ -1,4 +1,5 @@
-getProjectDir().'/config/bundles.php';
+ $contents = require $this->getProjectDir() . '/config/bundles.php';
foreach ($contents as $class => $envs) {
if ($envs[$this->environment] ?? $envs['all'] ?? false) {
yield new $class();
@@ -32,22 +33,22 @@ public function getProjectDir(): string
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
{
- $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php'));
+ $container->addResource(new FileResource($this->getProjectDir() . '/config/bundles.php'));
$container->setParameter('container.dumper.inline_class_loader', true);
- $confDir = $this->getProjectDir().'/config';
+ $confDir = $this->getProjectDir() . '/config';
- $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob');
- $loader->load($confDir.'/{packages}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
- $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob');
- $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob');
+ $loader->load($confDir . '/{packages}/*' . self::CONFIG_EXTS, 'glob');
+ $loader->load($confDir . '/{packages}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, 'glob');
+ $loader->load($confDir . '/{services}' . self::CONFIG_EXTS, 'glob');
+ $loader->load($confDir . '/{services}_' . $this->environment . self::CONFIG_EXTS, 'glob');
}
protected function configureRoutes(RouteCollectionBuilder $routes): void
{
- $confDir = $this->getProjectDir().'/config';
+ $confDir = $this->getProjectDir() . '/config';
- $routes->import($confDir.'/{routes}/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob');
- $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob');
- $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob');
+ $routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, '/', 'glob');
+ $routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, '/', 'glob');
+ $routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, '/', 'glob');
}
}
diff --git a/tests/Functional/App/Repository/ProjectionItemStub.php b/tests/Functional/App/Repository/ProjectionItemStub.php
index c2fff08..00d10fd 100644
--- a/tests/Functional/App/Repository/ProjectionItemStub.php
+++ b/tests/Functional/App/Repository/ProjectionItemStub.php
@@ -1,12 +1,13 @@
-
-
+
diff --git a/tests/Functional/App/config/bootstrap.php b/tests/Functional/App/config/bootstrap.php
index 9f7c361..272dcf6 100644
--- a/tests/Functional/App/config/bootstrap.php
+++ b/tests/Functional/App/config/bootstrap.php
@@ -1,6 +1,7 @@
['all' => true],
- Kununu\TestingBundle\KununuTestingBundle::class => ['all' => true],
- JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
+ Kununu\TestingBundle\KununuTestingBundle::class => ['all' => true],
+ JMS\SerializerBundle\JMSSerializerBundle::class => ['all' => true],
];
diff --git a/tests/Functional/App/config/packages/cache.yaml b/tests/Functional/App/config/packages/cache.yaml
index d032624..4ea1622 100644
--- a/tests/Functional/App/config/packages/cache.yaml
+++ b/tests/Functional/App/config/packages/cache.yaml
@@ -1,5 +1,5 @@
framework:
- cache:
- pools:
- app.cache.projections:
- adapter: cache.adapter.array
+ cache:
+ pools:
+ app.cache.projections:
+ adapter: cache.adapter.array
diff --git a/tests/Functional/App/config/packages/framework.yaml b/tests/Functional/App/config/packages/framework.yaml
index 41d89fd..d15b639 100644
--- a/tests/Functional/App/config/packages/framework.yaml
+++ b/tests/Functional/App/config/packages/framework.yaml
@@ -1,7 +1,7 @@
framework:
- test: true
- secret: 'secret'
- session:
- storage_id: session.storage.mock_file
- php_errors:
- log: true
+ test: true
+ secret: 'secret'
+ session:
+ storage_id: session.storage.mock_file
+ php_errors:
+ log: true
diff --git a/tests/Functional/App/config/packages/jms_serializer.yaml b/tests/Functional/App/config/packages/jms_serializer.yaml
index 9f6c346..3c17d53 100644
--- a/tests/Functional/App/config/packages/jms_serializer.yaml
+++ b/tests/Functional/App/config/packages/jms_serializer.yaml
@@ -1,4 +1,4 @@
jms_serializer:
- default_context:
- serialization:
- serialize_null: true
+ default_context:
+ serialization:
+ serialize_null: true
diff --git a/tests/Functional/App/config/projections.yaml b/tests/Functional/App/config/projections.yaml
index 356d4d8..b167b19 100644
--- a/tests/Functional/App/config/projections.yaml
+++ b/tests/Functional/App/config/projections.yaml
@@ -1,17 +1,17 @@
imports:
- - { resource: '../Repository/Resources/config/jms_serializer.yaml' }
+ - { resource: '../Repository/Resources/config/jms_serializer.yaml' }
services:
- _defaults:
- autowire: true
- autoconfigure: true
+ _defaults:
+ autowire: true
+ autoconfigure: true
- Kununu\Projections\Repository\:
- resource: '../../../../src/Repository/*'
- bind:
- $cachePool: '@app.cache.projections'
- public: true
+ Kununu\Projections\Repository\:
+ resource: '../../../../src/Repository/*'
+ bind:
+ $cachePool: '@app.cache.projections'
+ public: true
- app.cache.projections.tagged:
- class: Symfony\Component\Cache\Adapter\TagAwareAdapter
- decorates: 'app.cache.projections'
+ app.cache.projections.tagged:
+ class: Symfony\Component\Cache\Adapter\TagAwareAdapter
+ decorates: 'app.cache.projections'
diff --git a/tests/Functional/App/config/services.yaml b/tests/Functional/App/config/services.yaml
index f7c8e95..11dbc1b 100644
--- a/tests/Functional/App/config/services.yaml
+++ b/tests/Functional/App/config/services.yaml
@@ -1,7 +1,7 @@
imports:
- - { resource: projections.yaml }
+ - { resource: projections.yaml }
services:
- _defaults:
- autowire: true
- autoconfigure: true
+ _defaults:
+ autowire: true
+ autoconfigure: true
diff --git a/tests/Functional/Repository/CachePoolProjectionRepositoryTest.php b/tests/Functional/Repository/CachePoolProjectionRepositoryTest.php
index 87757a1..460ebab 100644
--- a/tests/Functional/Repository/CachePoolProjectionRepositoryTest.php
+++ b/tests/Functional/Repository/CachePoolProjectionRepositoryTest.php
@@ -8,6 +8,7 @@
use Kununu\Projections\Tag\Tags;
use Kununu\Projections\Tests\Functional\App\Repository\ProjectionItemStub;
use Kununu\TestingBundle\Test\FixturesAwareTestCase;
+use Kununu\TestingBundle\Test\Options\Options;
final class CachePoolProjectionRepositoryTest extends FixturesAwareTestCase
{
@@ -97,9 +98,7 @@ public function testDeleteByTags(): void
protected function setUp(): void
{
- parent::setUp();
-
- $this->loadCachePoolFixtures('app.cache.projections', []);
+ $this->loadCachePoolFixtures('app.cache.projections', Options::create());
$this->projectionRepository = $this->getFixturesContainer()->get(CachePoolProjectionRepository::class);
diff --git a/tests/Unit/CacheCleaner/AbstractCacheCleanerByTagsTest.php b/tests/Unit/CacheCleaner/AbstractCacheCleanerByTagsTest.php
index f8535c5..a53ee30 100644
--- a/tests/Unit/CacheCleaner/AbstractCacheCleanerByTagsTest.php
+++ b/tests/Unit/CacheCleaner/AbstractCacheCleanerByTagsTest.php
@@ -4,9 +4,9 @@
namespace Kununu\Projections\Tests\Unit\CacheCleaner;
use Kununu\Projections\CacheCleaner\AbstractCacheCleanerByTags;
-use Kununu\Projections\CacheCleaner\CacheCleaner;
-use Kununu\Projections\ProjectionRepository;
-use Kununu\Projections\Tag\Tag;
+use Kununu\Projections\CacheCleaner\CacheCleanerInterface;
+use Kununu\Projections\ProjectionRepositoryInterface;
+use Kununu\Projections\Tag\ProjectionTagGenerator;
use Kununu\Projections\Tag\Tags;
use Kununu\Projections\TestCase\CacheCleaner\AbstractCacheCleanerTestCase;
use Psr\Log\LoggerInterface;
@@ -15,12 +15,16 @@ final class AbstractCacheCleanerByTagsTest extends AbstractCacheCleanerTestCase
{
protected const TAGS = ['my-tag1', 'my-tag2'];
- protected function getCacheCleaner(ProjectionRepository $projectionRepository, LoggerInterface $logger): CacheCleaner
- {
+ protected function getCacheCleaner(
+ ProjectionRepositoryInterface $projectionRepository,
+ LoggerInterface $logger
+ ): CacheCleanerInterface {
return new class($projectionRepository, $logger) extends AbstractCacheCleanerByTags {
+ use ProjectionTagGenerator;
+
protected function getTags(): Tags
{
- return new Tags(new Tag('my-tag1'), new Tag('my-tag2'));
+ return $this->createTagsFromArray('my-tag1', 'my-tag2');
}
};
}
diff --git a/tests/Unit/CacheCleaner/CacheCleanerChainTest.php b/tests/Unit/CacheCleaner/CacheCleanerChainTest.php
index 6fee0f2..703383e 100644
--- a/tests/Unit/CacheCleaner/CacheCleanerChainTest.php
+++ b/tests/Unit/CacheCleaner/CacheCleanerChainTest.php
@@ -3,8 +3,8 @@
namespace Kununu\Projections\Tests\Unit\CacheCleaner;
-use Kununu\Projections\CacheCleaner\CacheCleaner;
use Kununu\Projections\CacheCleaner\CacheCleanerChain;
+use Kununu\Projections\CacheCleaner\CacheCleanerInterface;
use PHPUnit\Framework\TestCase;
final class CacheCleanerChainTest extends TestCase
@@ -20,9 +20,9 @@ public function testCacheCleanerChain(): void
$chain->clear();
}
- private function createCacheCleaner(): CacheCleaner
+ private function createCacheCleaner(): CacheCleanerInterface
{
- $cleaner = $this->createMock(CacheCleaner::class);
+ $cleaner = $this->createMock(CacheCleanerInterface::class);
$cleaner
->expects($this->once())
diff --git a/tests/Unit/ProjectionItemIterableTraitTest.php b/tests/Unit/ProjectionItemIterableTraitTest.php
index 257765d..8995488 100644
--- a/tests/Unit/ProjectionItemIterableTraitTest.php
+++ b/tests/Unit/ProjectionItemIterableTraitTest.php
@@ -1,10 +1,11 @@
-assertEquals(['a', 'b', 5], $validClass->data());
- $invalidClass = new class {
+ $invalidClass = new class() {
use ProjectionItemIterableTrait;
};
diff --git a/tests/Unit/Provider/AbstractCachedProviderTest.php b/tests/Unit/Provider/AbstractCachedProviderTest.php
index b392c5c..34222fa 100644
--- a/tests/Unit/Provider/AbstractCachedProviderTest.php
+++ b/tests/Unit/Provider/AbstractCachedProviderTest.php
@@ -31,7 +31,7 @@ public function getDataDataProvider(): array
$originalProvider = new MyProviderStub();
return [
- 'cache_miss_and_data_from_external_provider' => [
+ 'cache_miss_and_data_from_external_provider' => [
$originalProvider,
self::METHOD_GET_DATA,
[2],
@@ -39,7 +39,15 @@ public function getDataDataProvider(): array
null,
$data,
],
- 'cache_miss_and_no_data_from_external_provider' => [
+ 'cache_miss_and_data_from_external_provider_not_relevant' => [
+ $originalProvider,
+ self::METHOD_GET_DATA,
+ [3],
+ new MyStubProjectionItem(3),
+ null,
+ null,
+ ],
+ 'cache_miss_and_no_data_from_external_provider' => [
$originalProvider,
self::METHOD_GET_DATA,
[1],
@@ -47,7 +55,7 @@ public function getDataDataProvider(): array
null,
null,
],
- 'cache_hit' => [
+ 'cache_hit' => [
$originalProvider,
self::METHOD_GET_DATA,
[2],
@@ -58,6 +66,31 @@ public function getDataDataProvider(): array
];
}
+ public function testInvalidateCacheItemByKey(): void
+ {
+ $provider = $this->getProvider(new MyProviderStub());
+
+ $this->getLogger()
+ ->expects($this->once())
+ ->method('info')
+ ->with(
+ 'Deleting cache item',
+ ['cache_key' => 'my_data_1']
+ );
+
+ $this->getProjectionRepository()
+ ->expects($this->once())
+ ->method('delete')
+ ->with(new MyStubProjectionItem(1));
+
+ $provider->invalidateItem(1);
+ }
+
+ /**
+ * @param $originalProvider
+ *
+ * @return MyCachedProviderStub|AbstractCachedProvider
+ */
protected function getProvider($originalProvider): AbstractCachedProvider
{
return new MyCachedProviderStub($originalProvider, $this->getProjectionRepository(), $this->getLogger());
diff --git a/tests/Unit/Provider/MyCachedProviderStub.php b/tests/Unit/Provider/MyCachedProviderStub.php
index 46db90d..d3d0f67 100644
--- a/tests/Unit/Provider/MyCachedProviderStub.php
+++ b/tests/Unit/Provider/MyCachedProviderStub.php
@@ -3,7 +3,8 @@
namespace Kununu\Projections\Tests\Unit\Provider;
-use Kununu\Projections\ProjectionRepository;
+use Kununu\Projections\ProjectionItemIterableInterface;
+use Kununu\Projections\ProjectionRepositoryInterface;
use Kununu\Projections\Provider\AbstractCachedProvider;
use Psr\Log\LoggerInterface;
@@ -11,19 +12,34 @@ final class MyCachedProviderStub extends AbstractCachedProvider implements MyPro
{
private $provider;
- public function __construct(MyProviderStubInterface $provider, ProjectionRepository $projectionRepository, LoggerInterface $logger)
- {
+ public function __construct(
+ MyProviderStubInterface $provider,
+ ProjectionRepositoryInterface $projectionRepository,
+ LoggerInterface $logger
+ ) {
parent::__construct($projectionRepository, $logger);
$this->provider = $provider;
}
- public function getData(int $id): ?array
+ public function getData(int $id): ?iterable
{
return $this->getAndCacheData(
new MyStubProjectionItem($id),
- function() use ($id): ?array {
+ function() use ($id): ?iterable {
return $this->provider->getData($id);
+ },
+ function(ProjectionItemIterableInterface $item, iterable $data): ?iterable {
+ assert($item instanceof MyStubProjectionItem);
+
+ return $item->getKey() === 'my_data_3'
+ ? null
+ : $data;
}
);
}
+
+ public function invalidateItem(int $id): void
+ {
+ $this->invalidateCacheItemByKey(new MyStubProjectionItem($id));
+ }
}
diff --git a/tests/Unit/Provider/MyProviderStub.php b/tests/Unit/Provider/MyProviderStub.php
index a74860c..783b768 100644
--- a/tests/Unit/Provider/MyProviderStub.php
+++ b/tests/Unit/Provider/MyProviderStub.php
@@ -5,7 +5,7 @@
final class MyProviderStub implements MyProviderStubInterface
{
- public function getData(int $id): ?array
+ public function getData(int $id): ?iterable
{
return
$id === 1
diff --git a/tests/Unit/Provider/MyProviderStubInterface.php b/tests/Unit/Provider/MyProviderStubInterface.php
index bf00d38..f782254 100644
--- a/tests/Unit/Provider/MyProviderStubInterface.php
+++ b/tests/Unit/Provider/MyProviderStubInterface.php
@@ -5,5 +5,5 @@
interface MyProviderStubInterface
{
- public function getData(int $id): ?array;
+ public function getData(int $id): ?iterable;
}
diff --git a/tests/Unit/Provider/MyStubProjectionItem.php b/tests/Unit/Provider/MyStubProjectionItem.php
index b6991c6..e62ee66 100644
--- a/tests/Unit/Provider/MyStubProjectionItem.php
+++ b/tests/Unit/Provider/MyStubProjectionItem.php
@@ -3,12 +3,12 @@
namespace Kununu\Projections\Tests\Unit\Provider;
-use Kununu\Projections\ProjectionItemIterable;
+use Kununu\Projections\ProjectionItemIterableInterface;
use Kununu\Projections\ProjectionItemIterableTrait;
use Kununu\Projections\Tag\ProjectionTagGenerator;
use Kununu\Projections\Tag\Tags;
-final class MyStubProjectionItem implements ProjectionItemIterable
+final class MyStubProjectionItem implements ProjectionItemIterableInterface
{
use ProjectionItemIterableTrait;
use ProjectionTagGenerator;
diff --git a/tests/Unit/Repository/CacheItemStub.php b/tests/Unit/Repository/CacheItemStub.php
index 7c9a2ba..c10837a 100644
--- a/tests/Unit/Repository/CacheItemStub.php
+++ b/tests/Unit/Repository/CacheItemStub.php
@@ -1,4 +1,5 @@
-key;
}
@@ -21,12 +22,12 @@ public function get()
return $this->value;
}
- public function isHit()
+ public function isHit(): bool
{
return $this->isHit;
}
- public function set($value)
+ public function set($value): CacheItemStub
{
$this->value = $value;
@@ -64,7 +65,7 @@ public function tag($tags): CacheItemInterface
return $this;
}
- public function getTags() : array
+ public function getTags(): array
{
return $this->tags;
}
diff --git a/tests/Unit/Repository/CachePoolProjectionRepositoryTest.php b/tests/Unit/Repository/CachePoolProjectionRepositoryTest.php
index 6be9075..4177f10 100644
--- a/tests/Unit/Repository/CachePoolProjectionRepositoryTest.php
+++ b/tests/Unit/Repository/CachePoolProjectionRepositoryTest.php
@@ -1,24 +1,22 @@
-expects($this->once())
->method('serialize')
->with($item, 'json')
- ->willReturnCallback(function (ProjectionItemIterableDummy $item): string {
+ ->willReturnCallback(function(ProjectionItemIterableDummy $item): string {
return json_encode([
'key' => $item->getKey(),
'stuff' => $item->getStuff(),
diff --git a/tests/Unit/Repository/ProjectionItemDummy.php b/tests/Unit/Repository/ProjectionItemDummy.php
index 4f774b7..3431c1e 100644
--- a/tests/Unit/Repository/ProjectionItemDummy.php
+++ b/tests/Unit/Repository/ProjectionItemDummy.php
@@ -1,18 +1,18 @@
-stuff;
}
- public function setStuff(string $stuff): ProjectionItem
+ public function setStuff(string $stuff): ProjectionItemInterface
{
$this->stuff = $stuff;
diff --git a/tests/Unit/Repository/ProjectionItemIterableDummy.php b/tests/Unit/Repository/ProjectionItemIterableDummy.php
index 9420e13..962da62 100644
--- a/tests/Unit/Repository/ProjectionItemIterableDummy.php
+++ b/tests/Unit/Repository/ProjectionItemIterableDummy.php
@@ -1,22 +1,21 @@
-stuff;
}
- public function setStuff(string $stuff): ProjectionItem
+ public function setStuff(string $stuff): ProjectionItemInterface
{
$this->stuff = $stuff;
diff --git a/tests/Unit/Tag/ProjectionTagGeneratorTest.php b/tests/Unit/Tag/ProjectionTagGeneratorTest.php
index d23c1e4..7482161 100644
--- a/tests/Unit/Tag/ProjectionTagGeneratorTest.php
+++ b/tests/Unit/Tag/ProjectionTagGeneratorTest.php
@@ -1,4 +1,5 @@
-