Completed
Push — master ( 0be494...bb7d96 )
by Tomáš
04:09
created

DoctrineExtension::parseConfig()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.3183

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 10
cts 16
cp 0.625
rs 8.439
c 0
b 0
f 0
cc 5
eloc 16
nc 10
nop 0
crap 6.3183
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Portiny\Doctrine\Adapter\Nette\DI;
6
7
use Doctrine\Common\Cache\ArrayCache;
8
use Doctrine\Common\Cache\Cache;
9
use Doctrine\Common\EventManager;
10
use Doctrine\Common\EventSubscriber;
11
use Doctrine\DBAL\Connection;
12
use Doctrine\DBAL\Tools\Console\Command\ImportCommand;
13
use Doctrine\ORM\Configuration;
14
use Doctrine\ORM\EntityManager;
15
use Doctrine\ORM\Events;
16
use Doctrine\ORM\Mapping\UnderscoreNamingStrategy;
17
use Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand;
18
use Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand;
19
use Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand;
20
use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand;
21
use Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand;
22
use Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand;
23
use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand;
24
use Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand;
25
use Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand;
26
use Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand;
27
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
28
use Doctrine\ORM\Tools\ResolveTargetEntityListener;
29
use Nette\DI\CompilerExtension;
30
use Nette\DI\ContainerBuilder;
31
use Nette\DI\Statement;
32
use Nette\PhpGenerator\ClassType;
33
use Nette\Utils\AssertionException;
34
use Nette\Utils\Validators;
35
use Portiny\Doctrine\Adapter\Nette\Tracy\DoctrineSQLPanel;
36
use Portiny\Doctrine\Contract\Provider\ClassMappingProviderInterface;
37
use Portiny\Doctrine\Contract\Provider\EntitySourceProviderInterface;
38
use Tracy\IBarPanel;
39
40
class DoctrineExtension extends CompilerExtension
41
{
42
	/**
43
	 * @var string
44
	 */
45
	public const DOCTRINE_SQL_PANEL = DoctrineSQLPanel::class;
46
47
	/**
48
	 * @var array
49
	 */
50
	public static $defaults = [
51
		'debug' => TRUE,
52
		'dbal' => [
53
			'type_overrides' => [],
54
			'types' => [],
55
			'schema_filter' => NULL,
56
		],
57
		'prefix' => 'doctrine.default',
58
		'proxyDir' => '%tempDir%/cache/proxies',
59
		'sourceDir' => NULL,
60
		'targetEntityMappings' => [],
61
		'metadata' => [],
62
		'functions' => [],
63
	];
64
65
	private $entitySources = [];
66
67
	private $classMappings = [];
68
69
	/**
70
	 * {@inheritdoc}
71
	 */
72 1
	public function loadConfiguration(): void
73
	{
74 1
		$config = $this->parseConfig();
75
76 1
		$builder = $this->getContainerBuilder();
77 1
		$name = $config['prefix'];
78
79 1
		$builder->addDefinition($name . '.config')
80 1
			->setType(Configuration::class)
81 1
			->addSetup(
82 1
				new Statement('setFilterSchemaAssetsExpression', [$config['dbal']['schema_filter']])
83
			);
84
85 1
		$builder->addDefinition($name . '.connection')
86 1
			->setType(Connection::class)
87 1
			->setFactory('@' . $name . '.entityManager::getConnection');
88
89 1
		$builder->addDefinition($name . '.entityManager')
90 1
			->setType(EntityManager::class)
91 1
			->setFactory(
92 1
				'\Doctrine\ORM\EntityManager::create',
93
				[
94 1
					$config['connection'],
95 1
					'@' . $name . '.config',
96 1
					'@Doctrine\Common\EventManager',
97
				]
98
			);
99
100 1
		$builder->addDefinition($name . '.namingStrategy')
101 1
			->setType(UnderscoreNamingStrategy::class);
102
103 1
		$builder->addDefinition($name . '.resolver')
104 1
			->setType(ResolveTargetEntityListener::class);
105
106 1
		if ($this->hasIBarPanelInterface()) {
107 1
			$builder->addDefinition($this->prefix($name . '.diagnosticsPanel'))
108 1
				->setType(self::DOCTRINE_SQL_PANEL);
109
		}
110
111
		// import Doctrine commands into Portiny/Console
112 1
		if($this->hasPortinyConsole()){
113
			$commands = [
114
				ConvertMappingCommand::class,
115
				CreateCommand::class,
116
				DropCommand::class,
117
				GenerateEntitiesCommand::class,
118
				GenerateProxiesCommand::class,
119
				ImportCommand::class,
120
				MetadataCommand::class,
121
				QueryCommand::class,
122
				ResultCommand::class,
123
				UpdateCommand::class,
124
				ValidateSchemaCommand::class,
125
			];
126
			foreach ($commands as $index => $command) {
127
				$builder->addDefinition($name . '.command.' . $index)
128
					->setType($command);
129
			}
130
131
			if ($helperSets = $builder->findByType('\Symfony\Component\Console\Helper\HelperSet')) {
132
				$helperSet = reset($helperSets);
133
				$helperSet->addSetup('set', [new Statement(EntityManagerHelper::class), 'em']);
134
			}
135
		}
136 1
	}
137
138
	/**
139
	 * {@inheritdoc}
140
	 */
141 1
	public function beforeCompile(): void
142
	{
143 1
		$config = $this->getConfig(self::$defaults);
144 1
		$name = $config['prefix'];
145
146 1
		$builder = $this->getContainerBuilder();
147 1
		$cache = $this->getCache($name, $builder);
148
149 1
		$configDefinition = $builder->getDefinition($name . '.config')
150 1
			->setFactory(
151 1
				'\Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration',
152
				[
153 1
					array_values($this->entitySources),
154 1
					$config['debug'],
155 1
					$config['proxyDir'],
156 1
					$cache,
157
					FALSE,
158
				]
159
			)
160 1
			->addSetup('setNamingStrategy', ['@' . $name . '.namingStrategy']);
161
162 1
		foreach ($config['functions'] as $functionName => $function) {
163 1
			$configDefinition->addSetup('addCustomStringFunction', [$functionName, $function]);
164
		}
165
166 1
		foreach ($this->classMappings as $source => $target) {
167
			$builder->getDefinition($name . '.resolver')
168
				->addSetup('addResolveTargetEntity', [$source, $target, []]);
169
		}
170
171 1
		$this->processDbalTypes($name, $config['dbal']['types']);
172 1
		$this->processDbalTypeOverrides($name, $config['dbal']['type_overrides']);
173 1
		$this->processEventSubscribers($name);
174 1
	}
175
176
	/**
177
	 * {@inheritdoc}
178
	 */
179 1
	public function afterCompile(ClassType $classType): void
180
	{
181 1
		$initialize = $classType->methods['initialize'];
182 1
		if ($this->hasIBarPanelInterface()) {
183 1
			$initialize->addBody('$this->getByType(\'' . self::DOCTRINE_SQL_PANEL . '\')->bindToBar();');
184
		}
185 1
	}
186
187
	/**
188
	 * @throws AssertionException
189
	 */
190 1
	private function parseConfig(): array
191
	{
192 1
		$config = $this->getConfig(self::$defaults);
193 1
		$this->classMappings = $config['targetEntityMappings'];
194 1
		$this->entitySources = $config['metadata'];
195
196 1
		foreach ($this->compiler->getExtensions() as $extension) {
197 1
			if ($extension instanceof ClassMappingProviderInterface) {
198
				$entityMapping = $extension->getClassMapping();
199
				Validators::assert($entityMapping, 'array');
200
				$this->classMappings = array_merge($this->classMappings, $entityMapping);
201
			}
202
203 1
			if ($extension instanceof EntitySourceProviderInterface) {
204
				$entitySource = $extension->getEntitySource();
205
				Validators::assert($entitySource, 'array');
206 1
				$this->entitySources = array_merge($this->entitySources, $entitySource);
207
			}
208
		}
209
210 1
		if ($config['sourceDir']) {
211
			$this->entitySources[] = $config['sourceDir'];
212
		}
213
214 1
		return $config;
215
	}
216
217 1
	private function getCache(string $prefix, ContainerBuilder $containerBuilder): string
218
	{
219 1
		$cacheServiceName = $containerBuilder->getByType(Cache::class);
220 1
		if ($cacheServiceName !== NULL && strlen($cacheServiceName) > 0) {
221
			return '@' . $cacheServiceName;
222
		}
223
224 1
		$containerBuilder->addDefinition($prefix . '.cache')
225 1
			->setType(ArrayCache::class);
226
227 1
		return '@' . $prefix . '.cache';
228
	}
229
230 1
	private function hasIBarPanelInterface(): bool
231
	{
232 1
		return interface_exists(IBarPanel::class);
233
	}
234
235 1
	private function hasPortinyConsole(): bool
236
	{
237 1
		return class_exists('\Portiny\Console\Adapter\Nette\DI\ConsoleExtension');
238
	}
239
240 1
	private function hasEventManager(ContainerBuilder $containerBuilder): bool
241
	{
242 1
		$eventManagerServiceName = $containerBuilder->getByType(EventManager::class);
243 1
		return $eventManagerServiceName !== NULL && strlen($eventManagerServiceName) > 0;
244
	}
245
246 1
	private function processDbalTypes(string $name, array $types): void
247
	{
248 1
		$builder = $this->getContainerBuilder();
249 1
		$entityManagerDefinition = $builder->getDefinition($name . '.entityManager');
250
251 1
		foreach ($types as $type => $className) {
252 1
			$entityManagerDefinition->addSetup(
253 1
				'if ( ! Doctrine\DBAL\Types\Type::hasType(?)) { Doctrine\DBAL\Types\Type::addType(?, ?); }',
254 1
				[$type, $type, $className]
255
			);
256
		}
257 1
	}
258
259 1
	private function processDbalTypeOverrides(string $name, array $types): void
260
	{
261 1
		$builder = $this->getContainerBuilder();
262 1
		$entityManagerDefinition = $builder->getDefinition($name . '.entityManager');
263
264 1
		foreach ($types as $type => $className) {
265 1
			$entityManagerDefinition->addSetup('Doctrine\DBAL\Types\Type::overrideType(?, ?);', [$type, $className]);
266
		}
267 1
	}
268
269
270 1
	private function processEventSubscribers(string $name)
271
	{
272 1
		$builder = $this->getContainerBuilder();
273
274 1
		if ($this->hasEventManager($builder)) {
275
			$eventManagerDefinition = $builder->getDefinition($builder->getByType(EventManager::class))
276
				->addSetup('addEventListener', [Events::loadClassMetadata, '@' . $name . '.resolver']);
277
		} else {
278 1
			$eventManagerDefinition = $builder->addDefinition($name . '.eventManager')
279 1
				->setType(EventManager::class)
280 1
				->addSetup('addEventListener', [Events::loadClassMetadata, '@' . $name . '.resolver']);
281
		}
282
283 1
		foreach (array_keys($builder->findByType(EventSubscriber::class)) as $serviceName) {
284 1
			$eventManagerDefinition->addSetup('addEventSubscriber', ['@' . $serviceName]);
285
		}
286 1
	}
287
}
288