Complex classes like OrmExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use OrmExtension, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | class OrmExtension extends Nette\DI\CompilerExtension |
||
34 | { |
||
35 | |||
36 | const ANNOTATION_DRIVER = 'annotations'; |
||
37 | const PHP_NAMESPACE = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\\\\]*'; |
||
38 | const TAG_CONNECTION = 'doctrine.connection'; |
||
39 | const TAG_ENTITY_MANAGER = 'doctrine.entityManager'; |
||
40 | const TAG_BIND_TO_MANAGER = 'doctrine.bindToManager'; |
||
41 | const TAG_REPOSITORY_ENTITY = 'doctrine.repositoryEntity'; |
||
42 | const DEFAULT_PROXY_NAMESPACE = 'Kdyby\GeneratedProxy'; |
||
43 | const KDYBY_METADATA_NAMESPACE = 'Kdyby\Doctrine'; |
||
44 | |||
45 | /** |
||
46 | * @var array |
||
47 | */ |
||
48 | public $managerDefaults = [ |
||
49 | 'metadataCache' => 'default', |
||
50 | 'queryCache' => 'default', |
||
51 | 'resultCache' => 'default', |
||
52 | 'hydrationCache' => 'default', |
||
53 | 'secondLevelCache' => [ |
||
54 | 'enabled' => FALSE, |
||
55 | 'factoryClass' => Doctrine\ORM\Cache\DefaultCacheFactory::class, |
||
56 | 'driver' => 'default', |
||
57 | 'regions' => [ |
||
58 | 'defaultLifetime' => 3600, |
||
59 | 'defaultLockLifetime' => 60, |
||
60 | ], |
||
61 | 'fileLockRegionDirectory' => '%tempDir%/cache/Doctrine.Cache.Locks', // todo fix |
||
62 | 'logging' => '%debugMode%', |
||
63 | ], |
||
64 | 'classMetadataFactory' => Kdyby\Doctrine\Mapping\ClassMetadataFactory::class, |
||
65 | 'defaultRepositoryClassName' => Kdyby\Doctrine\EntityRepository::class, |
||
66 | 'repositoryFactoryClassName' => Kdyby\Doctrine\RepositoryFactory::class, |
||
67 | 'queryBuilderClassName' => Kdyby\Doctrine\QueryBuilder::class, |
||
68 | 'autoGenerateProxyClasses' => '%debugMode%', |
||
69 | 'namingStrategy' => Doctrine\ORM\Mapping\UnderscoreNamingStrategy::class, |
||
70 | 'quoteStrategy' => Doctrine\ORM\Mapping\DefaultQuoteStrategy::class, |
||
71 | 'entityListenerResolver' => Kdyby\Doctrine\Mapping\EntityListenerResolver::class, |
||
72 | 'proxyDir' => '%tempDir%/proxies', |
||
73 | 'proxyNamespace' => self::DEFAULT_PROXY_NAMESPACE, |
||
74 | 'dql' => ['string' => [], 'numeric' => [], 'datetime' => [], 'hints' => []], |
||
75 | 'hydrators' => [], |
||
76 | 'metadata' => [], |
||
77 | 'filters' => [], |
||
78 | 'namespaceAlias' => [], |
||
79 | 'targetEntityMappings' => [], |
||
80 | ]; |
||
81 | |||
82 | /** |
||
83 | * @var array |
||
84 | */ |
||
85 | public $connectionDefaults = [ |
||
86 | 'dbname' => NULL, |
||
87 | 'host' => '127.0.0.1', |
||
88 | 'port' => NULL, |
||
89 | 'user' => NULL, |
||
90 | 'password' => NULL, |
||
91 | 'charset' => 'UTF8', |
||
92 | 'driver' => 'pdo_mysql', |
||
93 | 'driverClass' => NULL, |
||
94 | 'options' => NULL, |
||
95 | 'path' => NULL, |
||
96 | 'memory' => NULL, |
||
97 | 'unix_socket' => NULL, |
||
98 | 'logging' => '%debugMode%', |
||
99 | 'platformService' => NULL, |
||
100 | 'defaultTableOptions' => [], |
||
101 | 'resultCache' => 'default', |
||
102 | 'types' => [], |
||
103 | 'schemaFilter' => NULL, |
||
104 | ]; |
||
105 | |||
106 | /** |
||
107 | * @var array |
||
108 | */ |
||
109 | public $metadataDriverClasses = [ |
||
110 | self::ANNOTATION_DRIVER => Doctrine\ORM\Mapping\Driver\AnnotationDriver::class, |
||
111 | 'static' => Doctrine\Common\Persistence\Mapping\Driver\StaticPHPDriver::class, |
||
112 | 'yml' => Doctrine\ORM\Mapping\Driver\YamlDriver::class, |
||
113 | 'yaml' => Doctrine\ORM\Mapping\Driver\YamlDriver::class, |
||
114 | 'xml' => Doctrine\ORM\Mapping\Driver\XmlDriver::class, |
||
115 | 'db' => Doctrine\ORM\Mapping\Driver\DatabaseDriver::class, |
||
116 | ]; |
||
117 | |||
118 | /** |
||
119 | * @var array |
||
120 | */ |
||
121 | private $proxyAutoloaders = []; |
||
122 | |||
123 | /** |
||
124 | * @var array |
||
125 | */ |
||
126 | private $targetEntityMappings = []; |
||
127 | |||
128 | /** |
||
129 | * @var array |
||
130 | */ |
||
131 | private $configuredManagers = []; |
||
132 | |||
133 | /** |
||
134 | * @var array |
||
135 | */ |
||
136 | private $managerConfigs = []; |
||
137 | |||
138 | /** |
||
139 | * @var array |
||
140 | */ |
||
141 | private $configuredConnections = []; |
||
142 | |||
143 | |||
144 | |||
145 | public function loadConfiguration() |
||
146 | { |
||
147 | $this->proxyAutoloaders = |
||
148 | $this->targetEntityMappings = |
||
149 | $this->configuredConnections = |
||
150 | $this->managerConfigs = |
||
151 | $this->configuredManagers = []; |
||
152 | |||
153 | if (!$this->compiler->getExtensions(AnnotationsExtension::class)) { |
||
154 | throw new Nette\Utils\AssertionException(sprintf("You should register %s before %s.", AnnotationsExtension::class, get_class($this))); |
||
155 | } |
||
156 | |||
157 | $builder = $this->getContainerBuilder(); |
||
158 | $config = $this->getConfig(); |
||
159 | |||
160 | $builder->parameters[$this->prefix('debug')] = !empty($config['debug']); |
||
161 | if (isset($config['dbname']) || isset($config['driver']) || isset($config['connection'])) { |
||
162 | $config = ['default' => $config]; |
||
163 | $defaults = ['debug' => $builder->parameters['debugMode']]; |
||
164 | |||
165 | } else { |
||
166 | $defaults = array_intersect_key($config, $this->managerDefaults) |
||
167 | + array_intersect_key($config, $this->connectionDefaults) |
||
168 | + ['debug' => $builder->parameters['debugMode']]; |
||
169 | |||
170 | $config = array_diff_key($config, $defaults); |
||
171 | } |
||
172 | |||
173 | if (empty($config)) { |
||
174 | throw new Kdyby\Doctrine\UnexpectedValueException("Please configure the Doctrine extensions using the section '{$this->name}:' in your config file."); |
||
175 | } |
||
176 | |||
177 | foreach ($config as $name => $emConfig) { |
||
178 | if (!is_array($emConfig) || (empty($emConfig['dbname']) && empty($emConfig['driver']))) { |
||
179 | throw new Kdyby\Doctrine\UnexpectedValueException("Please configure the Doctrine extensions using the section '{$this->name}:' in your config file."); |
||
180 | } |
||
181 | |||
182 | /** @var mixed[] $emConfig */ |
||
183 | $emConfig = Nette\DI\Config\Helpers::merge($emConfig, $defaults); |
||
184 | $this->processEntityManager($name, $emConfig); |
||
185 | } |
||
186 | |||
187 | if ($this->targetEntityMappings) { |
||
|
|||
188 | if (!$this->isKdybyEventsPresent()) { |
||
189 | throw new Nette\Utils\AssertionException('The option \'targetEntityMappings\' requires \'Kdyby\Events\DI\EventsExtension\'.', E_USER_NOTICE); |
||
190 | } |
||
191 | |||
192 | $listener = $builder->addDefinition($this->prefix('resolveTargetEntityListener')) |
||
193 | ->setClass(Kdyby\Doctrine\Tools\ResolveTargetEntityListener::class) |
||
194 | ->addTag(Kdyby\Events\DI\EventsExtension::TAG_SUBSCRIBER); |
||
195 | |||
196 | foreach ($this->targetEntityMappings as $originalEntity => $mapping) { |
||
197 | $listener->addSetup('addResolveTargetEntity', [$originalEntity, $mapping['targetEntity'], $mapping]); |
||
198 | } |
||
199 | } |
||
200 | |||
201 | $this->loadConsole(); |
||
202 | |||
203 | $builder->addDefinition($this->prefix('registry')) |
||
204 | ->setClass(Kdyby\Doctrine\Registry::class, [ |
||
205 | $this->configuredConnections, |
||
206 | $this->configuredManagers, |
||
207 | $builder->parameters[$this->name]['dbal']['defaultConnection'], |
||
208 | $builder->parameters[$this->name]['orm']['defaultEntityManager'], |
||
209 | ]); |
||
210 | } |
||
211 | |||
212 | |||
213 | |||
214 | protected function loadConsole() |
||
231 | |||
232 | |||
233 | |||
234 | protected function processEntityManager($name, array $defaults) |
||
235 | { |
||
236 | $builder = $this->getContainerBuilder(); |
||
237 | $config = $this->resolveConfig($defaults, $this->managerDefaults, $this->connectionDefaults); |
||
238 | |||
239 | if ($isDefault = !isset($builder->parameters[$this->name]['orm']['defaultEntityManager'])) { |
||
240 | $builder->parameters[$this->name]['orm']['defaultEntityManager'] = $name; |
||
241 | } |
||
242 | |||
243 | $metadataDriver = $builder->addDefinition($this->prefix($name . '.metadataDriver')) |
||
244 | ->setClass(Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain::class) |
||
245 | ->setAutowired(FALSE); |
||
246 | /** @var \Nette\DI\ServiceDefinition $metadataDriver */ |
||
247 | |||
248 | Validators::assertField($config, 'metadata', 'array'); |
||
249 | Validators::assertField($config, 'targetEntityMappings', 'array'); |
||
250 | $config['targetEntityMappings'] = $this->normalizeTargetEntityMappings($config['targetEntityMappings']); |
||
251 | foreach ($this->compiler->getExtensions() as $extension) { |
||
252 | if ($extension instanceof IEntityProvider) { |
||
253 | $metadata = $extension->getEntityMappings(); |
||
254 | Validators::assert($metadata, 'array'); |
||
255 | foreach ($metadata as $namespace => $nsConfig) { |
||
256 | if (array_key_exists($namespace, $config['metadata'])) { |
||
257 | throw new Nette\Utils\AssertionException(sprintf('The namespace %s is already configured, provider cannot change it', $namespace)); |
||
258 | } |
||
259 | $config['metadata'][$namespace] = $nsConfig; |
||
260 | } |
||
261 | } |
||
262 | |||
263 | if ($extension instanceof ITargetEntityProvider) { |
||
264 | $targetEntities = $extension->getTargetEntityMappings(); |
||
265 | Validators::assert($targetEntities, 'array'); |
||
266 | $config['targetEntityMappings'] = Nette\Utils\Arrays::mergeTree($config['targetEntityMappings'], $this->normalizeTargetEntityMappings($targetEntities)); |
||
267 | } |
||
268 | |||
269 | if ($extension instanceof IDatabaseTypeProvider) { |
||
270 | $providedTypes = $extension->getDatabaseTypes(); |
||
271 | Validators::assert($providedTypes, 'array'); |
||
272 | |||
273 | if (!isset($defaults['types'])) { |
||
274 | $defaults['types'] = []; |
||
275 | } |
||
276 | |||
277 | $defaults['types'] = array_merge($defaults['types'], $providedTypes); |
||
278 | } |
||
279 | } |
||
280 | |||
281 | foreach (self::natSortKeys($config['metadata']) as $namespace => $driver) { |
||
282 | $this->processMetadataDriver($metadataDriver, $namespace, $driver, $name); |
||
283 | } |
||
284 | |||
285 | $this->processMetadataDriver($metadataDriver, self::KDYBY_METADATA_NAMESPACE, __DIR__ . '/../Entities', $name); |
||
286 | |||
287 | if (empty($config['metadata'])) { |
||
288 | $metadataDriver->addSetup('setDefaultDriver', [ |
||
289 | new Statement($this->metadataDriverClasses[self::ANNOTATION_DRIVER], [ |
||
290 | '@' . Doctrine\Common\Annotations\Reader::class, |
||
291 | [$builder->expand('%appDir%')] |
||
292 | ]) |
||
293 | ]); |
||
294 | } |
||
295 | |||
296 | if ($config['repositoryFactoryClassName'] === 'default') { |
||
297 | $config['repositoryFactoryClassName'] = DefaultRepositoryFactory::class; |
||
298 | } |
||
299 | $builder->addDefinition($this->prefix($name . '.repositoryFactory')) |
||
300 | ->setClass($config['repositoryFactoryClassName']) |
||
301 | ->setAutowired(FALSE); |
||
302 | |||
303 | Validators::assertField($config, 'namespaceAlias', 'array'); |
||
304 | Validators::assertField($config, 'hydrators', 'array'); |
||
305 | Validators::assertField($config, 'dql', 'array'); |
||
306 | Validators::assertField($config['dql'], 'string', 'array'); |
||
307 | Validators::assertField($config['dql'], 'numeric', 'array'); |
||
308 | Validators::assertField($config['dql'], 'datetime', 'array'); |
||
309 | Validators::assertField($config['dql'], 'hints', 'array'); |
||
310 | |||
311 | $autoGenerateProxyClasses = is_bool($config['autoGenerateProxyClasses']) |
||
312 | ? ($config['autoGenerateProxyClasses'] ? AbstractProxyFactory::AUTOGENERATE_ALWAYS : AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS) |
||
313 | : $config['autoGenerateProxyClasses']; |
||
314 | |||
315 | $configuration = $builder->addDefinition($this->prefix($name . '.ormConfiguration')) |
||
316 | ->setClass(Kdyby\Doctrine\Configuration::class) |
||
317 | ->addSetup('setMetadataCacheImpl', [$this->processCache($config['metadataCache'], $name . '.metadata')]) |
||
318 | ->addSetup('setQueryCacheImpl', [$this->processCache($config['queryCache'], $name . '.query')]) |
||
319 | ->addSetup('setResultCacheImpl', [$this->processCache($config['resultCache'], $name . '.ormResult')]) |
||
320 | ->addSetup('setHydrationCacheImpl', [$this->processCache($config['hydrationCache'], $name . '.hydration')]) |
||
321 | ->addSetup('setMetadataDriverImpl', [$this->prefix('@' . $name . '.metadataDriver')]) |
||
322 | ->addSetup('setClassMetadataFactoryName', [$config['classMetadataFactory']]) |
||
323 | ->addSetup('setDefaultRepositoryClassName', [$config['defaultRepositoryClassName']]) |
||
324 | ->addSetup('setQueryBuilderClassName', [$config['queryBuilderClassName']]) |
||
325 | ->addSetup('setRepositoryFactory', [$this->prefix('@' . $name . '.repositoryFactory')]) |
||
326 | ->addSetup('setProxyDir', [$config['proxyDir']]) |
||
327 | ->addSetup('setProxyNamespace', [$config['proxyNamespace']]) |
||
328 | ->addSetup('setAutoGenerateProxyClasses', [$autoGenerateProxyClasses]) |
||
329 | ->addSetup('setEntityNamespaces', [$config['namespaceAlias']]) |
||
330 | ->addSetup('setCustomHydrationModes', [$config['hydrators']]) |
||
331 | ->addSetup('setCustomStringFunctions', [$config['dql']['string']]) |
||
332 | ->addSetup('setCustomNumericFunctions', [$config['dql']['numeric']]) |
||
333 | ->addSetup('setCustomDatetimeFunctions', [$config['dql']['datetime']]) |
||
334 | ->addSetup('setDefaultQueryHints', [$config['dql']['hints']]) |
||
335 | ->addSetup('setNamingStrategy', CacheHelpers::filterArgs($config['namingStrategy'])) |
||
336 | ->addSetup('setQuoteStrategy', CacheHelpers::filterArgs($config['quoteStrategy'])) |
||
337 | ->addSetup('setEntityListenerResolver', CacheHelpers::filterArgs($config['entityListenerResolver'])) |
||
338 | ->setAutowired(FALSE); |
||
339 | /** @var Nette\DI\ServiceDefinition $configuration */ |
||
340 | |||
341 | $this->proxyAutoloaders[$config['proxyNamespace']] = $config['proxyDir']; |
||
342 | |||
343 | $this->processSecondLevelCache($name, $config['secondLevelCache'], $isDefault); |
||
344 | |||
345 | Validators::assertField($config, 'filters', 'array'); |
||
346 | foreach ($config['filters'] as $filterName => $filterClass) { |
||
347 | $configuration->addSetup('addFilter', [$filterName, $filterClass]); |
||
348 | } |
||
349 | |||
350 | if ($config['targetEntityMappings']) { |
||
351 | $configuration->addSetup('setTargetEntityMap', [array_map(function ($mapping) { |
||
352 | return $mapping['targetEntity']; |
||
353 | }, $config['targetEntityMappings'])]); |
||
354 | $this->targetEntityMappings = Nette\Utils\Arrays::mergeTree($this->targetEntityMappings, $config['targetEntityMappings']); |
||
355 | } |
||
356 | |||
357 | if ($this->isKdybyEventsPresent()) { |
||
358 | $builder->addDefinition($this->prefix($name . '.evm')) |
||
359 | ->setClass(Kdyby\Events\NamespacedEventManager::class, [Kdyby\Doctrine\Events::NS . '::']) |
||
360 | ->addSetup('$dispatchGlobalEvents', [TRUE]) // for BC |
||
361 | ->setAutowired(FALSE); |
||
362 | |||
363 | } else { |
||
364 | $builder->addDefinition($this->prefix($name . '.evm')) |
||
365 | ->setClass('Doctrine\Common\EventManager') |
||
366 | ->setAutowired(FALSE); |
||
367 | } |
||
368 | |||
369 | // entity manager |
||
370 | $entityManager = $builder->addDefinition($managerServiceId = $this->prefix($name . '.entityManager')) |
||
371 | ->setClass(Kdyby\Doctrine\EntityManager::class) |
||
372 | ->setFactory(Kdyby\Doctrine\EntityManager::class . '::create', [ |
||
373 | $connectionService = $this->processConnection($name, $defaults, $isDefault), |
||
374 | $this->prefix('@' . $name . '.ormConfiguration'), |
||
375 | $this->prefix('@' . $name . '.evm'), |
||
376 | ]) |
||
377 | ->addTag(self::TAG_ENTITY_MANAGER) |
||
378 | ->addTag('kdyby.doctrine.entityManager') |
||
379 | ->setAutowired($isDefault); |
||
380 | |||
381 | if ($this->isTracyPresent()) { |
||
382 | $entityManager->addSetup('?->bindEntityManager(?)', [$this->prefix('@' . $name . '.diagnosticsPanel'), '@self']); |
||
383 | } |
||
384 | |||
385 | $builder->addDefinition($this->prefix('repositoryFactory.' . $name . '.defaultRepositoryFactory')) |
||
386 | ->setClass($config['defaultRepositoryClassName']) |
||
387 | ->setImplement(IRepositoryFactory::class) |
||
388 | ->setArguments([new Code\PhpLiteral('$entityManager'), new Code\PhpLiteral('$classMetadata')]) |
||
389 | ->setParameters([EntityManagerInterface::class . ' entityManager', Doctrine\ORM\Mapping\ClassMetadata::class . ' classMetadata']) |
||
390 | ->setAutowired(FALSE); |
||
391 | |||
392 | $builder->addDefinition($this->prefix($name . '.schemaValidator')) |
||
393 | ->setClass(Doctrine\ORM\Tools\SchemaValidator::class, ['@' . $managerServiceId]) |
||
394 | ->setAutowired($isDefault); |
||
395 | |||
396 | $builder->addDefinition($this->prefix($name . '.schemaTool')) |
||
397 | ->setClass(Doctrine\ORM\Tools\SchemaTool::class, ['@' . $managerServiceId]) |
||
398 | ->setAutowired($isDefault); |
||
399 | |||
400 | $cacheCleaner = $builder->addDefinition($this->prefix($name . '.cacheCleaner')) |
||
401 | ->setClass(Kdyby\Doctrine\Tools\CacheCleaner::class, ['@' . $managerServiceId]) |
||
402 | ->setAutowired($isDefault); |
||
403 | |||
404 | $builder->addDefinition($this->prefix($name . '.schemaManager')) |
||
405 | ->setClass(AbstractSchemaManager::class) |
||
406 | ->setFactory('@' . Kdyby\Doctrine\Connection::class . '::getSchemaManager') |
||
407 | ->setAutowired($isDefault); |
||
408 | |||
409 | foreach ($this->compiler->getExtensions(AnnotationsExtension::class) as $extension) { |
||
410 | /** @var AnnotationsExtension $extension */ |
||
411 | $cacheCleaner->addSetup('addCacheStorage', [$extension->prefix('@cache.annotations')]); |
||
412 | } |
||
413 | |||
414 | if ($isDefault) { |
||
415 | $builder->addDefinition($this->prefix('helper.entityManager')) |
||
416 | ->setClass(EntityManagerHelper::class, ['@' . $managerServiceId]) |
||
417 | ->addTag(Kdyby\Console\DI\ConsoleExtension::HELPER_TAG, 'em'); |
||
418 | |||
419 | $builder->addDefinition($this->prefix('helper.connection')) |
||
420 | ->setClass(ConnectionHelper::class, [$connectionService]) |
||
421 | ->addTag(Kdyby\Console\DI\ConsoleExtension::HELPER_TAG, 'db'); |
||
422 | |||
423 | $builder->addAlias($this->prefix('schemaValidator'), $this->prefix($name . '.schemaValidator')); |
||
424 | $builder->addAlias($this->prefix('schemaTool'), $this->prefix($name . '.schemaTool')); |
||
425 | $builder->addAlias($this->prefix('cacheCleaner'), $this->prefix($name . '.cacheCleaner')); |
||
426 | $builder->addAlias($this->prefix('schemaManager'), $this->prefix($name . '.schemaManager')); |
||
427 | } |
||
428 | |||
429 | $this->configuredManagers[$name] = $managerServiceId; |
||
430 | $this->managerConfigs[$name] = $config; |
||
431 | } |
||
432 | |||
433 | |||
434 | |||
435 | protected function processSecondLevelCache($name, array $config, $isDefault) |
||
486 | |||
487 | |||
488 | |||
489 | protected function processConnection($name, array $defaults, $isDefault = FALSE) |
||
490 | { |
||
491 | $builder = $this->getContainerBuilder(); |
||
492 | $config = $this->resolveConfig($defaults, $this->connectionDefaults, $this->managerDefaults); |
||
493 | |||
494 | if ($isDefault) { |
||
495 | $builder->parameters[$this->name]['dbal']['defaultConnection'] = $name; |
||
496 | } |
||
497 | |||
498 | if (isset($defaults['connection'])) { |
||
499 | return $this->prefix('@' . $defaults['connection'] . '.connection'); |
||
500 | } |
||
501 | |||
502 | // config |
||
503 | $configuration = $builder->addDefinition($this->prefix($name . '.dbalConfiguration')) |
||
504 | ->setClass(Doctrine\DBAL\Configuration::class) |
||
505 | ->addSetup('setResultCacheImpl', [$this->processCache($config['resultCache'], $name . '.dbalResult')]) |
||
506 | ->addSetup('setSQLLogger', [new Statement(Doctrine\DBAL\Logging\LoggerChain::class)]) |
||
507 | ->addSetup('setFilterSchemaAssetsExpression', [$config['schemaFilter']]) |
||
508 | ->setAutowired(FALSE); |
||
509 | |||
510 | // types |
||
511 | Validators::assertField($config, 'types', 'array'); |
||
512 | $schemaTypes = $dbalTypes = []; |
||
513 | foreach ($config['types'] as $dbType => $className) { |
||
514 | /** @var Doctrine\DBAL\Types\Type $typeInst */ |
||
515 | $typeInst = Code\Helpers::createObject($className, []); |
||
516 | $dbalTypes[$typeInst->getName()] = $className; |
||
517 | $schemaTypes[$dbType] = $typeInst->getName(); |
||
518 | } |
||
519 | |||
520 | // tracy panel |
||
521 | if ($this->isTracyPresent()) { |
||
522 | $builder->addDefinition($this->prefix($name . '.diagnosticsPanel')) |
||
523 | ->setClass(Kdyby\Doctrine\Diagnostics\Panel::class) |
||
524 | ->setAutowired(FALSE); |
||
525 | } |
||
526 | |||
527 | // connection |
||
528 | $options = array_diff_key($config, array_flip(['types', 'resultCache', 'connection', 'logging'])); |
||
529 | $connection = $builder->addDefinition($connectionServiceId = $this->prefix($name . '.connection')) |
||
530 | ->setClass(Kdyby\Doctrine\Connection::class) |
||
531 | ->setFactory(Kdyby\Doctrine\Connection::class . '::create', [ |
||
532 | $options, |
||
533 | $this->prefix('@' . $name . '.dbalConfiguration'), |
||
534 | $this->prefix('@' . $name . '.evm'), |
||
535 | ]) |
||
536 | ->addSetup('setSchemaTypes', [$schemaTypes]) |
||
537 | ->addSetup('setDbalTypes', [$dbalTypes]) |
||
538 | ->addTag(self::TAG_CONNECTION) |
||
539 | ->addTag('kdyby.doctrine.connection') |
||
540 | ->setAutowired($isDefault); |
||
541 | |||
542 | if ($this->isTracyPresent()) { |
||
543 | $connection->addSetup('$panel = ?->bindConnection(?)', [$this->prefix('@' . $name . '.diagnosticsPanel'), '@self']); |
||
544 | } |
||
545 | |||
546 | /** @var Nette\DI\ServiceDefinition $connection */ |
||
547 | |||
548 | $this->configuredConnections[$name] = $connectionServiceId; |
||
549 | |||
550 | if (!is_bool($config['logging'])) { |
||
551 | $fileLogger = new Statement(Kdyby\Doctrine\Diagnostics\FileLogger::class, [$builder->expand($config['logging'])]); |
||
552 | $configuration->addSetup('$service->getSQLLogger()->addLogger(?)', [$fileLogger]); |
||
553 | |||
554 | } elseif ($config['logging']) { |
||
555 | $connection->addSetup('?->enableLogging()', [new Code\PhpLiteral('$panel')]); |
||
556 | } |
||
557 | |||
558 | return $this->prefix('@' . $name . '.connection'); |
||
559 | } |
||
560 | |||
561 | |||
562 | |||
563 | /** |
||
564 | * @param \Nette\DI\ServiceDefinition $metadataDriver |
||
565 | * @param string $namespace |
||
566 | * @param string|array|\stdClass $driver |
||
567 | * @param string $prefix |
||
568 | * @throws \Nette\Utils\AssertionException |
||
569 | * @return string |
||
570 | */ |
||
571 | protected function processMetadataDriver(Nette\DI\ServiceDefinition $metadataDriver, $namespace, $driver, $prefix) |
||
572 | { |
||
573 | if (!is_string($namespace) || !Strings::match($namespace, '#^' . self::PHP_NAMESPACE . '\z#')) { |
||
574 | throw new Nette\Utils\AssertionException("The metadata namespace expects to be valid namespace, $namespace given."); |
||
575 | } |
||
576 | $namespace = ltrim($namespace, '\\'); |
||
577 | |||
578 | if (is_string($driver) && strpos($driver, '@') === 0) { // service reference |
||
579 | $metadataDriver->addSetup('addDriver', [$driver, $namespace]); |
||
580 | return $driver; |
||
581 | } |
||
582 | |||
583 | if (is_string($driver) || is_array($driver)) { |
||
584 | $paths = is_array($driver) ? $driver : [$driver]; |
||
585 | foreach ($paths as $path) { |
||
586 | if (!file_exists($path)) { |
||
587 | throw new Nette\Utils\AssertionException("The metadata path expects to be an existing directory, $path given."); |
||
588 | } |
||
589 | } |
||
590 | |||
591 | $driver = new Statement(self::ANNOTATION_DRIVER, is_array($paths) ? $paths : [$paths]); |
||
592 | } |
||
593 | |||
594 | $impl = $driver instanceof \stdClass ? $driver->value : ($driver instanceof Statement ? $driver->getEntity() : (string) $driver); |
||
595 | list($driver) = CacheHelpers::filterArgs($driver); |
||
596 | /** @var Statement $driver */ |
||
597 | |||
598 | /** @var string $impl */ |
||
599 | if (isset($this->metadataDriverClasses[$impl])) { |
||
600 | $driver = new Statement($this->metadataDriverClasses[$impl], $driver->arguments); |
||
601 | } |
||
602 | |||
603 | if (is_string($driver->getEntity()) && substr($driver->getEntity(), 0, 1) === '@') { |
||
604 | $metadataDriver->addSetup('addDriver', [$driver->getEntity(), $namespace]); |
||
605 | return $driver->getEntity(); |
||
606 | } |
||
607 | |||
608 | if ($impl === self::ANNOTATION_DRIVER) { |
||
609 | $driver->arguments = [ |
||
610 | '@' . Doctrine\Common\Annotations\Reader::class, |
||
611 | Nette\Utils\Arrays::flatten($driver->arguments) |
||
612 | ]; |
||
613 | } |
||
614 | |||
615 | $serviceName = $this->prefix($prefix . '.driver.' . str_replace('\\', '_', $namespace) . '.' . str_replace('\\', '_', $impl) . 'Impl'); |
||
616 | |||
617 | $this->getContainerBuilder()->addDefinition($serviceName) |
||
618 | ->setClass(Doctrine\Common\Persistence\Mapping\Driver\MappingDriver::class) |
||
619 | ->setFactory($driver->getEntity(), $driver->arguments) |
||
620 | ->setAutowired(FALSE); |
||
621 | |||
622 | $metadataDriver->addSetup('addDriver', ['@' . $serviceName, $namespace]); |
||
623 | return '@' . $serviceName; |
||
624 | } |
||
625 | |||
626 | |||
627 | |||
628 | /** |
||
629 | * @param string|\stdClass $cache |
||
630 | * @param string $suffix |
||
631 | * @return string |
||
632 | */ |
||
633 | protected function processCache($cache, $suffix) |
||
637 | |||
638 | |||
639 | |||
640 | public function beforeCompile() |
||
645 | |||
646 | |||
647 | |||
648 | protected function processRepositories() |
||
649 | { |
||
650 | $builder = $this->getContainerBuilder(); |
||
651 | |||
652 | $disabled = TRUE; |
||
653 | foreach ($this->configuredManagers as $managerName => $_) { |
||
654 | $factoryClassName = $builder->getDefinition($this->prefix($managerName . '.repositoryFactory'))->getClass(); |
||
655 | if ($factoryClassName === Kdyby\Doctrine\RepositoryFactory::class || in_array(Kdyby\Doctrine\RepositoryFactory::class, class_parents($factoryClassName), TRUE)) { |
||
656 | $disabled = FALSE; |
||
657 | } |
||
658 | } |
||
659 | |||
660 | if ($disabled) { |
||
661 | return; |
||
662 | } |
||
663 | |||
664 | if (!method_exists($builder, 'findByType')) { |
||
665 | foreach ($this->configuredManagers as $managerName => $_) { |
||
666 | $builder->getDefinition($this->prefix($managerName . '.repositoryFactory')) |
||
667 | ->addSetup('setServiceIdsMap', [[], $this->prefix('repositoryFactory.' . $managerName . '.defaultRepositoryFactory')]); |
||
668 | } |
||
669 | |||
670 | return; |
||
671 | } |
||
672 | |||
673 | $serviceMap = array_fill_keys(array_keys($this->configuredManagers), []); |
||
674 | foreach ($builder->findByType(Doctrine\ORM\EntityRepository::class) as $originalServiceName => $originalDef) { |
||
675 | if (strpos($originalServiceName, $this->name . '.') === 0) { |
||
676 | continue; // ignore |
||
677 | } |
||
678 | |||
679 | $originalDefFactory = $originalDef->getFactory(); |
||
680 | $factory = ($originalDefFactory !== NULL) ? $originalDefFactory->getEntity() : $originalDef->getClass(); |
||
681 | if (stripos($factory, '::getRepository') !== FALSE) { |
||
682 | continue; // ignore |
||
683 | } |
||
684 | |||
685 | $factoryServiceName = $this->prefix('repositoryFactory.' . $originalServiceName); |
||
686 | $factoryDef = $builder->addDefinition($factoryServiceName, $originalDef) |
||
687 | ->setImplement(IRepositoryFactory::class) |
||
688 | ->setParameters([Doctrine\ORM\EntityManagerInterface::class . ' entityManager', Doctrine\ORM\Mapping\ClassMetadata::class . ' classMetadata']) |
||
689 | ->setAutowired(FALSE); |
||
690 | $factoryStatement = $factoryDef->getFactory() ?: new Statement($factoryDef->getClass()); |
||
691 | $factoryStatement->arguments[0] = new Code\PhpLiteral('$entityManager'); |
||
692 | $factoryStatement->arguments[1] = new Code\PhpLiteral('$classMetadata'); |
||
693 | $factoryDef->setArguments($factoryStatement->arguments); |
||
694 | |||
695 | $boundManagers = $this->getServiceBoundManagers($originalDef); |
||
696 | Validators::assert($boundManagers, 'list:1', 'bound manager'); |
||
697 | |||
698 | if ($boundEntity = $originalDef->getTag(self::TAG_REPOSITORY_ENTITY)) { |
||
699 | if (!is_string($boundEntity) || !class_exists($boundEntity)) { |
||
700 | throw new Nette\Utils\AssertionException(sprintf('The entity "%s" for repository "%s" cannot be autoloaded.', $boundEntity, $originalDef->getClass())); |
||
701 | } |
||
702 | $entityArgument = $boundEntity; |
||
703 | |||
704 | } else { |
||
705 | throw new Nette\Utils\AssertionException(sprintf( |
||
706 | 'The magic auto-detection of entity for repository %s for %s was removed from Kdyby.' . |
||
707 | 'You have to specify %s tag with classname of the related entity in order to use this feature.', |
||
708 | $originalDef->getClass(), |
||
709 | IRepositoryFactory::class, |
||
710 | self::TAG_REPOSITORY_ENTITY |
||
711 | )); |
||
712 | } |
||
713 | |||
714 | $builder->removeDefinition($originalServiceName); |
||
715 | $builder->addDefinition($originalServiceName) |
||
716 | ->setClass($originalDef->getClass()) |
||
717 | ->setFactory(sprintf('@%s::getRepository', $this->configuredManagers[$boundManagers[0]]), [$entityArgument]); |
||
718 | |||
719 | $serviceMap[$boundManagers[0]][$originalDef->getClass()] = $factoryServiceName; |
||
720 | } |
||
721 | |||
722 | foreach ($this->configuredManagers as $managerName => $_) { |
||
723 | $builder->getDefinition($this->prefix($managerName . '.repositoryFactory')) |
||
724 | ->addSetup('setServiceIdsMap', [ |
||
725 | $serviceMap[$managerName], |
||
726 | $this->prefix('repositoryFactory.' . $managerName . '.defaultRepositoryFactory') |
||
727 | ]); |
||
728 | } |
||
729 | } |
||
730 | |||
731 | |||
732 | |||
733 | protected function processEventManagers() |
||
745 | |||
746 | |||
747 | |||
748 | /** |
||
749 | * @param Nette\DI\ServiceDefinition $def |
||
750 | * @return string[] |
||
751 | */ |
||
752 | protected function getServiceBoundManagers(Nette\DI\ServiceDefinition $def) |
||
759 | |||
760 | |||
761 | |||
762 | public function afterCompile(Code\ClassType $class) |
||
777 | |||
778 | |||
779 | |||
780 | /** |
||
781 | * @param array $provided |
||
782 | * @param array $defaults |
||
783 | * @param array $diff |
||
784 | * @return array |
||
785 | */ |
||
786 | private function resolveConfig(array $provided, array $defaults, array $diff = []) |
||
793 | |||
794 | |||
795 | /** |
||
796 | * @param array $targetEntityMappings |
||
797 | * @return array |
||
798 | */ |
||
799 | private function normalizeTargetEntityMappings(array $targetEntityMappings) |
||
820 | |||
821 | |||
822 | |||
823 | /** |
||
824 | * @return bool |
||
825 | */ |
||
826 | private function isTracyPresent() |
||
830 | |||
831 | |||
832 | |||
833 | /** |
||
834 | * @return bool |
||
835 | */ |
||
836 | private function isKdybyEventsPresent() |
||
840 | |||
841 | |||
842 | |||
843 | private function addCollapsePathsToTracy(Method $init) |
||
854 | |||
855 | |||
856 | |||
857 | /** |
||
858 | * @param \Nette\Configurator $configurator |
||
859 | */ |
||
860 | public static function register(Nette\Configurator $configurator) |
||
866 | |||
867 | |||
868 | |||
869 | /** |
||
870 | * @param array $array |
||
871 | */ |
||
872 | private static function natSortKeys(array &$array) |
||
880 | |||
881 | } |
||
882 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.