Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ For details on how to use the MLC Service and Factory directly.

[MultiLevelCacheService and MultiLevelCacheFactory Documentation](documentation/mlc-service-and-factory.md)

# FAQ

We now have an FAQ with the most common questions on how to do things.

[Look Here for all FAQ goodness](documentation/faq.md)

## License

MIT
Expand Down
11 changes: 11 additions & 0 deletions documentation/faq.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# FAQ

## I want to:

- [Create a simple Cached Version of a service i have](faq/01_simple_example.md)
- [Only use the [Redis/In-Memory] cache and not the others](faq/02_configure_the_mlc.md)
- [Have an additional interface on my Cached Service](faq/03_additional_interface.md)

## That's nice but:
- [What if the DTO i return changes, doesn't this cause problems?](faq/but_01_data_version.md)
- [What if my method uses internal properties the MLC doesn't know about?](faq/but_02_additional_cache_key_getter.md)
93 changes: 93 additions & 0 deletions documentation/faq/01_simple_example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Simple example of a cached service:

Given you have the following Service that you want a cached Version of.

```php
<?php

declare(strict_types=1);

namespace App\Service;

class TestService
{
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return "something";
}

}
```

## Preparing the source service
First we need to tell the MLC what methods can be cached and how long to hold the data for.

We do that by adding the `MlcCacheableMethod` attribute to each method we want cached and set the `ttlSeconds` property accordingly.

```php
<?php

declare(strict_types=1);

namespace App\Service;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;

class TestService
{

#[MlcCacheableMethod(ttlSeconds: 300)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return "something";
}

}
```

## Generating the Cached Version

We now run the `ddev mlc-make App.Service.TestService` command.

```
$ ddev mlc-make App.Service.TestService
========================================================
Create or Update a cached service...
========================================================


Parsing command line arguments...
| Argument | Value |
------------------------------------------
| service | App.Service.TestService |

Resolving service class for 'App.Service.TestService'...
Service class found: App\Service\TestService

------------------------------------
Generating cached service.
------------------------------------

Use statement for interface 'App\Interface\Service\TestServiceInterface' does not exists in file: /var/www/html/src/Service/TestService.php
Attempt adding it.
Added interface 'App\Interface\Service\TestServiceInterface' to class 'App\Service\TestService' in file: /var/www/html/src/Service/TestService.php

Cached service generated successfully.

| Key | Value |
--------------------------------------------------------------------------------
| Class | /var/www/html/src/Service/TestServiceCached.php |
| Interface | /var/www/html/src/Interface/Service/TestServiceInterface.php |
```

## Using the Service

To use the new Service you can:
- Swap the `TestService` to `TestServiceCached` to always use the cached version, or
- Set the Injection to `TestServiceInterface` and configure the dependency injection via your symfony services.yaml
60 changes: 60 additions & 0 deletions documentation/faq/02_configure_the_mlc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Configuring the CSG to use a specific cache configuration

We have this service:

```php
<?php

declare(strict_types=1);

namespace App\Service;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;

class TestService
{

#[MlcCacheableMethod(ttlSeconds: 300)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return "something";
}

}
```

What we now want to do is add the `MlcCacheableService` attribute to the class and set the `cacheType` property.

For our example we will use `Redis`, but every other option from `CacheTypeEnum` works too.

```php
<?php

declare(strict_types=1);

namespace App\Service;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableService;
use Tbessenreither\MultiLevelCache\Enum\CacheTypeEnum;

#[MlcCacheableService(cacheType: CacheTypeEnum::REDIS)]
class TestService
{

#[MlcCacheableMethod(ttlSeconds: 300)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return "something";
}

}
```

Then you update the cached service by running `ddev mlc-update`. (You probably don't need to, but better be save than sorry)
61 changes: 61 additions & 0 deletions documentation/faq/03_additional_interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Adding an additional interface to the cached service

We have this service:

```php
<?php

declare(strict_types=1);

namespace App\Service;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;

class TestService
{

#[MlcCacheableMethod(ttlSeconds: 300)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return "something";
}

}
```

What we now want to do is add the `MlcCacheableService` attribute to the class and set the `additionalInterface` property.

For our example we will use `SomeGreatInterface`.

```php
<?php

declare(strict_types=1);

namespace App\Service;

use App\Interface\SomeGreatInterface;
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;
use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableService;
use Tbessenreither\MultiLevelCache\Enum\CacheTypeEnum;

#[MlcCacheableService(additionalInterface: SomeGreatInterface::class)]
class TestService
{

#[MlcCacheableMethod(ttlSeconds: 300)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return "something";
}

}
```

Then you update the cached service by running `ddev mlc-update`.
62 changes: 62 additions & 0 deletions documentation/faq/but_01_data_version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Preventing data colissions when a DTO changes

We have this service:

```php
<?php

declare(strict_types=1);

namespace App\Service;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;

class TestService
{

#[MlcCacheableMethod(ttlSeconds: 300)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return "something";
}

}
```

What we want to do is tell the MLC about the change in Data structure.

We do this by setting the `dataVersion` property of either the `MlcCacheableMethod` or as a default in `MlcCacheableService`. The later will act as the default data version for all cached methods.

```php
<?php

declare(strict_types=1);

namespace App\Service;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;

class TestService
{

#[MlcCacheableMethod(
ttlSeconds: 300,
dataVersion: '1',
)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return "something";
}

}
```

The Data Version can be any string. I would recommend starting with 1 and then just counting up every time you change something.

That's it. Now the MLC knows about your DTO changes and can keep the variants separate.
83 changes: 83 additions & 0 deletions documentation/faq/but_02_additional_cache_key_getter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Exposing internal properties to the MLC for Key generation

The response of the cached method is dependend on some internal state that is not present in the method arguments.

Let's say we have this example:

```php
<?php

declare(strict_types=1);

namespace App\Service;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;

class TestService
{
private string $stringToReturn = 'something';

#[MlcCacheableMethod(ttlSeconds: 300)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return $this->stringToReturn;
}

public function setStringToReturn(string $string): void
{
$this->stringToReturn = $string;
}

}
```

This would cause the MLC to cache the first response and then ignore all changes to `$this->stringToReturn`. That's not good.

Let's fix this by setting the `additionalCacheKeyGetter` property of `MlcCacheableService`.

```php
<?php

declare(strict_types=1);

namespace App\Service;

use Tbessenreither\MultiLevelCache\CachedServiceGenerator\Attribute\MlcCacheableMethod;

#[MlcCacheableService(additionalCacheKeyGetter: 'getAdditionalCacheKeys')]
class TestService
{
private string $stringToReturn = 'something';

#[MlcCacheableMethod(ttlSeconds: 300)]
public function doSomething(): string
{
// very expensive operation here, e.g. a call to an external API or a complex calculation
sleep(2); // Simulate a delay

return $this->stringToReturn;
}

public function setStringToReturn(string $string): void
{
$this->stringToReturn = $string;
}

public function getAdditionalCacheKeys(): array
{
return [
'importantString' => $this->stringToReturn,
];
}

}
```

Now the MLC is aware that there is more to your Service function than the method attributes and everything should work as expected.

Additional Bonus Fact:

You can return any serializable data through the getter. As long as PHPs `serialize()` can handle it, the MLC is fine with it.
Loading
Loading