Skip to content

Commit

Permalink
Replace doctrine/cache with PSR-6
Browse files Browse the repository at this point in the history
  • Loading branch information
jankonas committed Jul 21, 2024
1 parent e0809ab commit 266f398
Show file tree
Hide file tree
Showing 6 changed files with 265 additions and 100 deletions.
101 changes: 49 additions & 52 deletions .docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [Setup](#setup)
- [Relying](#relying)
- [Configuration](#configuration)
- [Caching](#caching)
- [Mapping](#mapping)
- [Attributes](#attributes)
- [XML](#xml)
Expand All @@ -33,10 +34,9 @@ extensions:

## Relying

Take advantage of enpowering this package with 3 extra packages:
Take advantage of empowering this package with 2 extra packages:

- `doctrine/dbal`
- `doctrine/cache`
- `symfony/console`


Expand All @@ -58,56 +58,6 @@ extensions:
> Doctrine DBAL provides powerful database abstraction layer with many features for database schema introspection, schema management and PDO abstraction.

### `doctrine/cache`

This package relies on `doctrine/cache`, use prepared [nettrine/cache](https://github.com/contributte/doctrine-cache) integration.

```bash
composer require nettrine/cache
```

```neon
extensions:
nettrine.cache: Nettrine\Cache\DI\CacheExtension
```

[Doctrine ORM](https://www.doctrine-project.org/projects/orm.html) needs [Doctrine Cache](https://www.doctrine-project.org/projects/cache.html) to be configured. If you register `nettrine/cache` extension it will detect it automatically.

`CacheExtension` sets up cache for all important parts: `queryCache`, `resultCache`, `hydrationCache`, `metadataCache` and `secondLevelCache`.

This is the default configuration, it uses the autowired driver.

```neon
extensions:
nettrine.orm: Nettrine\ORM\DI\OrmExtension
nettrine.orm.cache: Nettrine\ORM\DI\OrmCacheExtension
```

You can also specify a single driver or change the `nettrine.orm.cache.defaultDriver` for all of them.

```neon
nettrine.orm.cache:
defaultDriver: App\DefaultOrmCacheDriver
queryCache: App\SpecialDriver
resultCache: App\SpecialOtherDriver
hydrationCache: App\SpecialDriver('foo')
metadataCache: @cacheDriver
```

`secondLevelCache` uses autowired driver (or `defaultDriver`, if specified) for `CacheConfiguration` setup, but you can also replace it with custom `CacheConfiguration`.

```neon
nettrine.orm.cache:
secondLevelCache: @cacheConfigurationFactory::create('bar')
```

You can turn off `secondLevelCache` by setting it to `false`:

```neon
nettrine.orm.cache:
secondLevelCache: false
```

### `symfony/console`

This package relies on `symfony/console`, use prepared [contributte/console](https://github.com/contributte/console) integration.
Expand Down Expand Up @@ -184,6 +134,53 @@ Take a look at real **Nettrine ORM** configuration example at [contributte/webap
4. You have to configure entity mapping (see below), otherwise you will get `It's a requirement to specify a Metadata Driver` error.


### Caching

You can set up [caching](https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.html) by registering `Nettrine\ORM\DI\OrmCacheExtension`:

```neon
extensions:
nettrine.orm: Nettrine\ORM\DI\OrmExtension
nettrine.orm.cache: Nettrine\ORM\DI\OrmCacheExtension
```

By default, all caches are configured to use the autowired [cache storage](https://doc.nette.org/cs/caching#toc-sluzby-di). You can configure them to other [storage](https://doc.nette.org/cs/caching#toc-uloziste), [cache](https://api.nette.org/caching/master/Nette/Caching/Cache.html) or [cache pool](https://www.php-fig.org/psr/psr-6/#cacheitempoolinterface).

You can use the `nettrine.orm.cache.defaultDriver` to set the caching driver for all caches that are not explicitly configured or configure the caches one by one.

If you want to turn cache off, you can use `DevNullStorage` to do so.

All options are demonstrated in following configuration example:

```neon
nettrine.orm.cache:
# use different storage
defaultDriver: Nette\Caching\Storages\MemoryStorage
# use cache object
queryCache: Nette\Caching\Cache(namespace: 'orm-query-cache')
# use cache pool object
resultCache: Contributte\Psr6\CachePool(Nette\Caching\Cache(namespace: 'orm-result-cache'))
# use registered service (must be of type `Nette\Caching\Storage`, `Nette\Caching\Cache` or `Psr\Cache\CacheItemPoolInterface`)
hydrationCache: @service
# turn off caching
metadataCache: Nette\Caching\Storages\DevNullStorage
```

`secondLevelCache` uses autowired driver (or `defaultDriver`, if specified) for `CacheConfiguration` setup, but you can also replace it with custom `CacheConfiguration`.

```neon
nettrine.orm.cache:
secondLevelCache: @cacheConfigurationFactory::create('bar')
```

You can turn off `secondLevelCache` by setting it to `false`:

```neon
nettrine.orm.cache:
secondLevelCache: false
```


## Mapping

Doctrine ORM needs to know where your entities are located and how they are described (mapping).
Expand Down
11 changes: 6 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@
],
"require": {
"php": ">=8.1",
"contributte/psr6-caching": "^0.2 || ^0.3",
"doctrine/orm": "^2.14 || ^3.0",
"nette/di": "^3.1.2",
"symfony/console": "^5.3.0 || ^6.2.0 || ^7.0.0 ",
"nettrine/cache": "^0.4.0 || ^0.5.0",
"nettrine/dbal": "^0.8.0 || ^0.9.0",
"doctrine/orm": "^2.14 || ^3.0",
"doctrine/common":"^3.4.3"
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"symfony/console": "^5.3.0 || ^6.2.0 || ^7.0.0 "
},
"require-dev": {
"contributte/phpstan": "^0.1",
"contributte/qa": "^0.4",
"contributte/tester": "^0.3",
"contributte/phpstan": "^0.1",
"mockery/mockery": "^1.3.1",
"tracy/tracy": "^2.10.3"
},
"conflict": {
"doctrine/persistence": "<3.0.0",
"nette/caching": "<3.1.0",
"nette/di": "<3.0.6",
"nette/schema": "<1.1.0"
},
Expand Down
126 changes: 110 additions & 16 deletions src/DI/OrmCacheExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

namespace Nettrine\ORM\DI;

use Doctrine\Common\Cache\Cache;
use Contributte\Psr6\CachePool;
use Contributte\Psr6\CachePoolFactory;
use Doctrine\ORM\Cache\CacheConfiguration;
use Doctrine\ORM\Cache\DefaultCacheFactory;
use Doctrine\ORM\Cache\RegionsConfiguration;
use Nette\Caching\Cache;
use Nette\Caching\Storage;
use Nette\DI\Definitions\Definition;
use Nette\DI\Definitions\Statement;
use Nette\Schema\Expect;
use Nette\Schema\Schema;
use stdClass;
use Throwable;

/**
* @property-read stdClass $config
Expand All @@ -30,7 +34,7 @@ public function getConfigSchema(): Schema
]);
}

public function loadConfiguration(): void
public function beforeCompile(): void
{
// Validates needed extension
$this->validate();
Expand All @@ -56,7 +60,7 @@ private function loadQueryCacheConfiguration(): void
$config = $this->config;
$configurationDef = $this->getConfigurationDef();

$configurationDef->addSetup('setQueryCacheImpl', [
$configurationDef->addSetup('setQueryCache', [
$this->buildCacheDriver($config->queryCache, 'queryCache'),
]);
}
Expand All @@ -66,7 +70,7 @@ private function loadResultCacheConfiguration(): void
$config = $this->config;
$configurationDef = $this->getConfigurationDef();

$configurationDef->addSetup('setResultCacheImpl', [
$configurationDef->addSetup('setResultCache', [
$this->buildCacheDriver($config->resultCache, 'resultCache'),
]);
}
Expand All @@ -76,7 +80,7 @@ private function loadHydrationCacheConfiguration(): void
$config = $this->config;
$configurationDef = $this->getConfigurationDef();

$configurationDef->addSetup('setHydrationCacheImpl', [
$configurationDef->addSetup('setHydrationCache', [
$this->buildCacheDriver($config->hydrationCache, 'hydrationCache'),
]);
}
Expand All @@ -86,7 +90,7 @@ private function loadMetadataCacheConfiguration(): void
$config = $this->config;
$configurationDef = $this->getConfigurationDef();

$configurationDef->addSetup('setMetadataCacheImpl', [
$configurationDef->addSetup('setMetadataCache', [
$this->buildCacheDriver($config->metadataCache, 'metadataCache'),
]);
}
Expand Down Expand Up @@ -133,31 +137,121 @@ private function loadSecondLevelCacheConfiguration(): void
/**
* @param string|mixed[]|Statement|null $config
*/
private function buildCacheDriver(string|array|Statement|null $config, string $prefix): Definition|string
private function buildCacheDriver(string|array|Statement|null $config, string $prefix): Definition
{
$builder = $this->getContainerBuilder();

// Driver is defined
if ($config !== null && $config !== []) { // Nette converts explicit null to an empty array
return $builder->addDefinition($this->prefix($prefix))
->setFactory($config)
->setAutowired(false);
return $this->buildCacheDriverDefinition($config, $prefix);
}

// If there is default cache, don't create it
if ($builder->hasDefinition($this->prefix('defaultCache'))) {
return $builder->getDefinition($this->prefix('defaultCache'));
}

// Create default driver
if ($this->config->defaultDriver !== null && $this->config->defaultDriver !== []) { // Nette converts explicit null to an empty array
return $builder->addDefinition($this->prefix('defaultCache'))
->setFactory($this->config->defaultDriver)
return $this->buildCacheDriverDefinition($this->config->defaultDriver, 'defaultCache');
}

/**
* @param string|mixed[]|Statement|null $config
*/
private function buildCacheDriverDefinition(string|array|Statement|null $config, string $prefix): Definition
{
$builder = $this->getContainerBuilder();

// Driver is defined
if ($config !== null && $config !== []) { // Nette converts explicit null to an empty array
if (is_string($config)) {
$config = $this->resolveCacheDriverDefinitionString($config, $this->prefix($prefix));
}

if ($config instanceof Statement) {
$entity = $config->getEntity();

if (is_string($entity) && is_a($entity, Storage::class, true)) {
$entity = Cache::class;
$config = new Statement(
$entity,
[
'storage' => $config,
'namespace' => $this->prefix($prefix),
]
);
}

if (is_string($entity) && is_a($entity, Cache::class, true)) {
return $builder->addDefinition($this->prefix($prefix))
->setFactory(new Statement(CachePool::class, [$config]))
->setAutowired(false);
}
}

return $builder->addDefinition($this->prefix($prefix))
->setFactory($config)
->setAutowired(false);
}

// No default driver provided, create CacheItemPoolInterface with autowired Storage

// ICachePoolFactory doesn't have to be registered in DI container
if ($builder->hasDefinition($this->prefix('cachePoolFactory')) === false) {
$builder->addDefinition($this->prefix('cachePoolFactory'))
->setFactory(CachePoolFactory::class)
->setAutowired(false);
}

// No default driver provider, fallback to Cache::class
return '@' . Cache::class;
return $builder->addDefinition($this->prefix($prefix))
->setFactory('@' . $this->prefix('cachePoolFactory') . '::create', [$this->prefix($prefix)])
->setAutowired(false);
}

private function resolveCacheDriverDefinitionString(string $config, string $cacheNamespace): string|Statement
{
$builder = $this->getContainerBuilder();

if (str_starts_with($config, '@')) {
$service = substr($config, 1);

if ($builder->hasDefinition($service)) {
$definition = $builder->getDefinition($service);
} else {
try {
$definition = $builder->getDefinitionByType($service);
} catch (Throwable) {
$definition = null;

Check warning on line 223 in src/DI/OrmCacheExtension.php

View check run for this annotation

Codecov / codecov/patch

src/DI/OrmCacheExtension.php#L222-L223

Added lines #L222 - L223 were not covered by tests
}
}

$type = $definition?->getType();

if ($type === null) {
return $config;

Check warning on line 230 in src/DI/OrmCacheExtension.php

View check run for this annotation

Codecov / codecov/patch

src/DI/OrmCacheExtension.php#L230

Added line #L230 was not covered by tests
}

if (is_a($type, Storage::class, true)) {
return new Statement(
Cache::class,
[
'storage' => $config,
'namespace' => $cacheNamespace,
]
);
}

if (is_a($type, Cache::class, true)) {
return new Statement(CachePool::class, [$config]);
}

return $config;
}

if (is_a($config, Storage::class, true)) {
return new Statement($config);
}

return $config;

Check warning on line 254 in src/DI/OrmCacheExtension.php

View check run for this annotation

Codecov / codecov/patch

src/DI/OrmCacheExtension.php#L254

Added line #L254 was not covered by tests
}

}
Loading

0 comments on commit 266f398

Please sign in to comment.