diff --git a/config/doctrine.php b/config/doctrine.php index 696930a6..d6745335 100644 --- a/config/doctrine.php +++ b/config/doctrine.php @@ -156,7 +156,7 @@ | Configure meta-data, query and result caching here. | Optionally you can enable second level caching. | - | Available: apc|array|file|memcached|php_file|redis|void + | Available: apc|array|file|illuminate|memcached|php_file|redis|void | */ 'cache' => [ diff --git a/src/Configuration/Cache/FileCacheProvider.php b/src/Configuration/Cache/FileCacheProvider.php index 58361738..89708b55 100644 --- a/src/Configuration/Cache/FileCacheProvider.php +++ b/src/Configuration/Cache/FileCacheProvider.php @@ -5,6 +5,7 @@ use Doctrine\Common\Cache\FilesystemCache; use Illuminate\Contracts\Config\Repository; use LaravelDoctrine\ORM\Configuration\Driver; +use function storage_path; class FileCacheProvider implements Driver { @@ -28,8 +29,10 @@ public function __construct(Repository $config) */ public function resolve(array $settings = []) { + $path = $settings['path'] ?? $this->config->get('cache.stores.file.path', storage_path('framework/cache')); + return new FilesystemCache( - $this->config->get('cache.stores.file.path', storage_path('framework/cache')) + $path ); } } diff --git a/src/Configuration/Cache/IlluminateCacheProvider.php b/src/Configuration/Cache/IlluminateCacheProvider.php index be033c6e..2bf739a1 100644 --- a/src/Configuration/Cache/IlluminateCacheProvider.php +++ b/src/Configuration/Cache/IlluminateCacheProvider.php @@ -2,10 +2,12 @@ namespace LaravelDoctrine\ORM\Configuration\Cache; +use const E_USER_DEPRECATED; use Illuminate\Contracts\Cache\Factory; +use InvalidArgumentException; use LaravelDoctrine\ORM\Configuration\Driver; -abstract class IlluminateCacheProvider implements Driver +class IlluminateCacheProvider implements Driver { /** * @var Factory @@ -32,8 +34,18 @@ public function __construct(Factory $cache) */ public function resolve(array $settings = []) { + $store = $this->store ?? $settings['store'] ?? null; + + if ($store === null) { + throw new InvalidArgumentException('Please specify the `store` when using the "illuminate" cache driver.'); + } + + if ($this->store && isset($settings['store'])) { + trigger_error('Using driver "' . $this->store . '" with a custom store is deprecated. Please use the "illuminate" driver.', E_USER_DEPRECATED); + } + return new IlluminateCacheAdapter( - $this->cache->store($this->store) + $this->cache->store($store) ); } } diff --git a/src/Configuration/Cache/PhpFileCacheProvider.php b/src/Configuration/Cache/PhpFileCacheProvider.php index 0edc6584..800ce70e 100644 --- a/src/Configuration/Cache/PhpFileCacheProvider.php +++ b/src/Configuration/Cache/PhpFileCacheProvider.php @@ -29,8 +29,10 @@ public function __construct(Repository $config) */ public function resolve(array $settings = []) { + $path = $settings['path'] ?? $this->config->get('cache.stores.file.path', storage_path('framework/cache')); + return new PhpFileCache( - $this->config->get('cache.stores.file.path', storage_path('framework/cache')) + $path ); } } diff --git a/src/Configuration/Connections/MasterSlaveConnection.php b/src/Configuration/Connections/MasterSlaveConnection.php index 71933172..cd0bc9ad 100644 --- a/src/Configuration/Connections/MasterSlaveConnection.php +++ b/src/Configuration/Connections/MasterSlaveConnection.php @@ -51,6 +51,10 @@ public function resolve(array $settings = []) $resolvedSettings['serverVersion'] = $settings['serverVersion']; } + if (!empty($settings['defaultTableOptions'])) { + $resolvedSettings['defaultTableOptions'] = $settings['defaultTableOptions']; + } + return $resolvedSettings; } diff --git a/src/EntityManagerFactory.php b/src/EntityManagerFactory.php index 3e577752..acc074af 100644 --- a/src/EntityManagerFactory.php +++ b/src/EntityManagerFactory.php @@ -360,9 +360,10 @@ private function applyNamedCacheConfiguration($cacheName) $defaultDriver = $this->config->get('doctrine.cache.default', 'array'); $defaultNamespace = $this->config->get('doctrine.cache.namespace'); - $driver = $this->config->get('doctrine.cache.' . $cacheName . '.driver', $defaultDriver); + $settings = $this->config->get('doctrine.cache.' . $cacheName, []); + $driver = $settings['driver'] ?? $defaultDriver; - $cache = $this->cache->driver($driver); + $cache = $this->cache->driver($driver, $settings); if ($namespace = $this->config->get('doctrine.cache.' . $cacheName . '.namespace', $defaultNamespace)) { $cache->setNamespace($namespace); diff --git a/src/Facades/EntityManager.php b/src/Facades/EntityManager.php index abfef68e..e2970ef7 100644 --- a/src/Facades/EntityManager.php +++ b/src/Facades/EntityManager.php @@ -21,7 +21,7 @@ * @method static \Doctrine\ORM\Cache|null getCache() * @method static \Doctrine\DBAL\Connection getConnection() * @method static \Doctrine\ORM\Query\Expr getExpressionBuilder() - * @method statuc \Doctrine\ORM\Utility\IdentifierFlattener getIdentifierFlattener() + * @method static \Doctrine\ORM\Utility\IdentifierFlattener getIdentifierFlattener() * @method static void beginTransaction() * @method static mixed transactional(callable $func) * @method static void commit() diff --git a/src/Testing/ConfigRepository.php b/src/Testing/ConfigRepository.php index 09072b5b..e88dc337 100644 --- a/src/Testing/ConfigRepository.php +++ b/src/Testing/ConfigRepository.php @@ -3,6 +3,7 @@ namespace LaravelDoctrine\ORM\Testing; use Illuminate\Contracts\Config\Repository; +use Illuminate\Support\Arr; class ConfigRepository implements Repository { @@ -23,7 +24,7 @@ public function all() public function get($key, $default = null) { - return $this->items[$key] ?? $default; + return Arr::get($this->items, $key, $default); } public function set($key, $value = null) diff --git a/src/Testing/FactoryBuilder.php b/src/Testing/FactoryBuilder.php index 2d1fd6bb..76929af3 100644 --- a/src/Testing/FactoryBuilder.php +++ b/src/Testing/FactoryBuilder.php @@ -115,7 +115,7 @@ public function create(array $attributes = []) if ($this->amount === 1) { $manager->persist($results); - $this->callAfterCreating(collect($results)); + $this->callAfterCreating(collect([$results])); } else { foreach ($results as $result) { $manager->persist($result); diff --git a/tests/Configuration/Connections/MasterSlaveConnectionTest.php b/tests/Configuration/Connections/MasterSlaveConnectionTest.php index 0ace8057..e48eaaac 100644 --- a/tests/Configuration/Connections/MasterSlaveConnectionTest.php +++ b/tests/Configuration/Connections/MasterSlaveConnectionTest.php @@ -89,7 +89,11 @@ private function getInputConfig() 'port' => 3309 ], ], - 'serverVersion' => '5.8', + 'serverVersion' => '5.8', + 'defaultTableOptions' => [ + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_unicode_ci', + ] ]; } @@ -136,6 +140,10 @@ private function getExpectedConfig() 'unix_socket' => 'unix_socket', 'prefix' => 'prefix' ], + 'defaultTableOptions' => [ + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_unicode_ci', + ], ]; } @@ -221,6 +229,11 @@ private function getOracleExpectedConfig() $expectedConfigOracle['master']['user'] = 'homestead1'; $expectedConfigOracle['serverVersion'] = '5.8'; + $expectedConfigOracle['defaultTableOptions'] = [ + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_unicode_ci', + ]; + return $expectedConfigOracle; } @@ -239,6 +252,11 @@ private function getPgsqlExpectedConfig() $expectedConfigPgsql['slaves'][1]['sslmode'] = 'sslmode'; $expectedConfigPgsql['serverVersion'] = '5.8'; + $expectedConfigPgsql['defaultTableOptions'] = [ + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_unicode_ci', + ]; + return $expectedConfigPgsql; } @@ -276,7 +294,11 @@ private function getSqliteExpectedConfig() 'memory' => true, 'path' => ':memory', ], - 'serverVersion' => '5.8', + 'serverVersion' => '5.8', + 'defaultTableOptions' => [ + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_unicode_ci', + ] ]; } diff --git a/tests/EntityManagerFactoryTest.php b/tests/EntityManagerFactoryTest.php index 395f29b2..064bd09c 100644 --- a/tests/EntityManagerFactoryTest.php +++ b/tests/EntityManagerFactoryTest.php @@ -16,6 +16,7 @@ use Illuminate\Contracts\Config\Repository; use Illuminate\Contracts\Container\Container; use LaravelDoctrine\ORM\Configuration\Cache\CacheManager; +use LaravelDoctrine\ORM\Configuration\Cache\IlluminateCacheAdapter; use LaravelDoctrine\ORM\Configuration\Connections\ConnectionManager; use LaravelDoctrine\ORM\Configuration\LaravelNamingStrategy; use LaravelDoctrine\ORM\Configuration\MetaData\MetaDataManager; @@ -485,6 +486,242 @@ public function test_can_set_repository_factory() $this->assertEntityManager($manager); } + public function test_illuminate_cache_provider_custom_store() + { + m::resetContainer(); + + $config = new ConfigRepository([ + 'database.connections.mysql' => [ + 'driver' => 'mysql' + ], + 'doctrine' => [ + 'meta' => 'annotations', + 'connection' => 'mysql', + 'paths' => ['Entities'], + 'proxies' => [ + 'path' => 'dir', + 'auto_generate' => false, + 'namespace' => 'namespace' + ], + + 'cache' => [ + 'metadata' => [ + 'driver' => 'illuminate', + 'store' => 'myStoreName' + ] + ] + ], + 'doctrine.custom_datetime_functions' => [], + 'doctrine.custom_numeric_functions' => [], + 'doctrine.custom_string_functions' => [] + ]); + + $container = new \Illuminate\Container\Container(); + $container->singleton(Repository::class, function () use ($config) { + return $config; + }); + + $cache = M::mock(Illuminate\Contracts\Cache\Repository::class); + + $factory = M::mock(\Illuminate\Contracts\Cache\Factory::class); + $factory->shouldReceive('store')->with('myStoreName')->andReturn($cache); + + $container->singleton(Illuminate\Contracts\Cache\Factory::class, function () use ($factory) { + return $factory; + }); + + $factory = new EntityManagerFactory( + $container, + new Setup(), + new MetaDataManager($container), + new ConnectionManager($container), + new CacheManager($container), + $config, + new EntityListenerResolver($container) + ); + + $manager = $factory->create($config->get('doctrine')); + + $this->assertInstanceOf(IlluminateCacheAdapter::class, $manager->getConfiguration()->getMetadataCacheImpl()); + } + + public function test_illuminate_cache_provider_redis() + { + m::resetContainer(); + + $config = new ConfigRepository([ + 'database.connections.mysql' => [ + 'driver' => 'mysql' + ], + 'doctrine' => [ + 'meta' => 'annotations', + 'connection' => 'mysql', + 'paths' => ['Entities'], + 'proxies' => [ + 'path' => 'dir', + 'auto_generate' => false, + 'namespace' => 'namespace' + ], + + 'cache' => [ + 'metadata' => [ + 'driver' => 'redis', + ] + ] + ], + 'doctrine.custom_datetime_functions' => [], + 'doctrine.custom_numeric_functions' => [], + 'doctrine.custom_string_functions' => [] + ]); + + $container = new \Illuminate\Container\Container(); + $container->singleton(Repository::class, function () use ($config) { + return $config; + }); + + $cache = M::mock(Illuminate\Contracts\Cache\Repository::class); + + $factory = M::mock(\Illuminate\Contracts\Cache\Factory::class); + $factory->shouldReceive('store')->with('redis')->andReturn($cache); + + $container->singleton(Illuminate\Contracts\Cache\Factory::class, function () use ($factory) { + return $factory; + }); + + $factory = new EntityManagerFactory( + $container, + new Setup(), + new MetaDataManager($container), + new ConnectionManager($container), + new CacheManager($container), + $config, + new EntityListenerResolver($container) + ); + + $manager = $factory->create($config->get('doctrine')); + + $this->assertInstanceOf(IlluminateCacheAdapter::class, $manager->getConfiguration()->getMetadataCacheImpl()); + } + + public function test_illuminate_cache_provider_invalid_store() + { + m::resetContainer(); + + $config = new ConfigRepository([ + 'database.connections.mysql' => [ + 'driver' => 'mysql' + ], + 'doctrine' => [ + 'meta' => 'annotations', + 'connection' => 'mysql', + 'paths' => ['Entities'], + 'proxies' => [ + 'path' => 'dir', + 'auto_generate' => false, + 'namespace' => 'namespace' + ], + + 'cache' => [ + 'metadata' => [ + 'driver' => 'illuminate', + ] + ] + ], + 'doctrine.custom_datetime_functions' => [], + 'doctrine.custom_numeric_functions' => [], + 'doctrine.custom_string_functions' => [] + ]); + + $container = new \Illuminate\Container\Container(); + $container->singleton(Repository::class, function () use ($config) { + return $config; + }); + + $cache = M::mock(Illuminate\Contracts\Cache\Repository::class); + + $factory = M::mock(\Illuminate\Contracts\Cache\Factory::class); + $factory->shouldReceive('store')->with('myStoreName')->andReturn($cache); + + $container->singleton(Illuminate\Contracts\Cache\Factory::class, function () use ($factory) { + return $factory; + }); + + $factory = new EntityManagerFactory( + $container, + new Setup(), + new MetaDataManager($container), + new ConnectionManager($container), + new CacheManager($container), + $config, + new EntityListenerResolver($container) + ); + + $this->expectException(InvalidArgumentException::class); + + $this->expectExceptionMessage('Please specify the `store` when using the "illuminate" cache driver.'); + $factory->create($config->get('doctrine')); + } + + public function test_php_file_cache_custom_path() + { + m::resetContainer(); + + $config = new ConfigRepository([ + 'database.connections.mysql' => [ + 'driver' => 'mysql' + ], + 'doctrine' => [ + 'meta' => 'annotations', + 'connection' => 'mysql', + 'paths' => ['Entities'], + 'proxies' => [ + 'path' => 'dir', + 'auto_generate' => false, + 'namespace' => 'namespace' + ], + + 'cache' => [ + 'metadata' => [ + 'driver' => 'php_file', + 'path' => 'myCustomPath' + ] + ] + ], + 'doctrine.custom_datetime_functions' => [], + 'doctrine.custom_numeric_functions' => [], + 'doctrine.custom_string_functions' => [] + ]); + + $container = new \Illuminate\Container\Container(); + $container->singleton(Repository::class, function () use ($config) { + return $config; + }); + + $cache = M::mock(Illuminate\Contracts\Cache\Repository::class); + + $factory = M::mock(\Illuminate\Contracts\Cache\Factory::class); + $factory->shouldReceive('store')->with('myStoreName')->andReturn($cache); + + $container->singleton(Illuminate\Contracts\Cache\Factory::class, function () use ($factory) { + return $factory; + }); + + $factory = new EntityManagerFactory( + $container, + new Setup(), + new MetaDataManager($container), + new ConnectionManager($container), + new CacheManager($container), + $config, + new EntityListenerResolver($container) + ); + + $manager = $factory->create($config->get('doctrine')); + + $this->assertInstanceOf(\Doctrine\Common\Cache\PhpFileCache::class, $manager->getConfiguration()->getMetadataCacheImpl()); + $this->assertStringEndsWith('myCustomPath', $manager->getConfiguration()->getMetadataCacheImpl()->getDirectory()); + } + public function test_wrapper_connection() { m::resetContainer(); @@ -592,8 +829,8 @@ protected function mockConfig($driverConfig = ['driver' => 'mysql'], $strictCall foreach ($this->caches as $cache) { $expectation = $this->config->shouldReceive('get') - ->with('doctrine.cache.' . $cache . '.driver', 'array') - ->andReturn('array'); + ->with('doctrine.cache.' . $cache, []) + ->andReturn(['driver' => 'array']); $strictCallCountChecking ? $expectation->once() : $expectation->never(); }