ConfigurationFactory::getRepositoryFactory()   B
last analyzed

Complexity

Conditions 7
Paths 8

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
dl 0
loc 26
rs 8.8333
cc 7
nc 8
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arp\LaminasDoctrine\Factory\Configuration;
6
7
use Arp\LaminasDoctrine\Config\DoctrineConfigInterface;
8
use Arp\LaminasFactory\AbstractFactory;
9
use Doctrine\Common\Cache\Cache;
10
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
11
use Doctrine\DBAL\Exception as DBALException;
12
use Doctrine\DBAL\Types\Type;
13
use Doctrine\ORM\Configuration;
14
use Doctrine\ORM\Repository\DefaultRepositoryFactory;
15
use Doctrine\ORM\Repository\RepositoryFactory;
16
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
17
use Laminas\ServiceManager\Exception\ServiceNotCreatedException;
18
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
19
use Laminas\ServiceManager\ServiceLocatorInterface;
20
use Psr\Cache\CacheItemPoolInterface;
21
use Psr\Container\ContainerExceptionInterface;
22
use Psr\Container\ContainerInterface;
23
24
final class ConfigurationFactory extends AbstractFactory
25
{
26
    /**
27
     * @var array<string, mixed>
28
     */
29
    private array $defaultOptions = [
30
        'repository_factory' => DefaultRepositoryFactory::class,
31
    ];
32
33
    /**
34
     * @param ContainerInterface&ServiceLocatorInterface $container
35
     * @param array<mixed>|null                          $options
36
     *
37
     * @throws ServiceNotCreatedException
38
     * @throws ServiceNotFoundException
39
     * @throws ContainerExceptionInterface
40
     */
41
    public function __invoke(
42
        ContainerInterface $container,
43
        string $requestedName,
44
        array $options = null
45
    ): Configuration {
46
        $options = $this->getOptions($container, $requestedName, $options);
47
48
        $configuration = new Configuration();
49
50
        if (!array_key_exists('proxy_dir', $options)) {
51
            throw new ServiceNotCreatedException(
52
                sprintf(
53
                    'The required \'proxy_dir\' configuration option is missing for service \'%s\'',
54
                    $requestedName
55
                )
56
            );
57
        }
58
59
        if (!array_key_exists('proxy_namespace', $options)) {
60
            throw new ServiceNotCreatedException(
61
                sprintf(
62
                    'The required \'proxy_namespace\' configuration option is missing for service \'%s\'',
63
                    $requestedName
64
                )
65
            );
66
        }
67
68
        if (!array_key_exists('driver', $options)) {
69
            throw new ServiceNotCreatedException(
70
                sprintf(
71
                    'The required \'driver\' configuration option is missing for service \'%s\'',
72
                    $requestedName
73
                )
74
            );
75
        }
76
77
        $configuration->setEntityNamespaces($options['entity_namespaces'] ?? []);
78
        $configuration->setMetadataDriverImpl(
79
            $this->getMappingDriver($container, $options['driver'], $requestedName)
80
        );
81
82
        $configuration->setProxyDir($options['proxy_dir']);
83
        $configuration->setAutoGenerateProxyClasses($options['generate_proxies']);
84
        $configuration->setProxyNamespace($options['proxy_namespace']);
85
86
        if (isset($options['metadata_cache'])) {
87
            $configuration->setMetadataCache($this->getCache($container, $options['metadata_cache'], $requestedName));
88
        }
89
90
        if (isset($options['query_cache'])) {
91
            $configuration->setQueryCache($this->getCache($container, $options['query_cache'], $requestedName));
92
        }
93
94
        if (isset($options['result_cache'])) {
95
            $configuration->setResultCache($this->getCache($container, $options['result_cache'], $requestedName));
96
        }
97
98
        if (isset($options['hydration_cache'])) {
99
            $configuration->setHydrationCache($this->getCache($container, $options['hydration_cache'], $requestedName));
100
        }
101
102
        if (!empty($options['repository_factory'])) {
103
            $configuration->setRepositoryFactory(
104
                $this->getRepositoryFactory($container, $options['repository_factory'], $requestedName)
105
            );
106
        }
107
108
        if (!empty($options['type'])) {
109
            $this->registerCustomTypes($options['type']);
110
        }
111
112
        // @todo EntityResolver
113
        // @todo setNamingStrategy() and setQuoteStrategy()
114
        // @todo 2nd Level Cache
115
        // @todo setSQLLogger()
116
117
        return $configuration;
118
    }
119
120
    /**
121
     * @param string|array<mixed>|MappingDriver $driver
122
     *
123
     * @throws ServiceNotCreatedException
124
     * @throws ServiceNotFoundException
125
     * @throws ContainerExceptionInterface
126
     */
127
    private function getMappingDriver(
128
        ServiceLocatorInterface $container,
129
        string|array|MappingDriver $driver,
130
        string $serviceName
131
    ): MappingDriver {
132
        if (is_string($driver)) {
0 ignored issues
show
introduced by
The condition is_string($driver) is always false.
Loading history...
133
            /** @var DoctrineConfigInterface $doctrineConfig */
134
            $doctrineConfig = $this->getService($container, DoctrineConfigInterface::class, $serviceName);
135
136
            if (!$doctrineConfig->hasDriverConfig($driver)) {
137
                throw new ServiceNotCreatedException(
138
                    sprintf(
139
                        'The driver configuration \'%s\' could not be found for service \'%s\'',
140
                        $driver,
141
                        $serviceName
142
                    )
143
                );
144
            }
145
146
            $driver = $doctrineConfig->getDriverConfig($driver);
147
        }
148
149
        if (is_array($driver)) {
0 ignored issues
show
introduced by
The condition is_array($driver) is always true.
Loading history...
150
            $driver = $this->buildService($container, MappingDriver::class, $driver, $serviceName);
151
        }
152
153
        if (!$driver instanceof MappingDriver) {
154
            throw new ServiceNotCreatedException(
155
                sprintf('The \'driver\' configuration must be an object of type \'%s\'', MappingDriver::class)
156
            );
157
        }
158
159
        return $driver;
160
    }
161
162
    /**
163
     * @param string|array<mixed>|CacheItemPoolInterface $cache
164
     *
165
     * @throws ServiceNotCreatedException
166
     * @throws ServiceNotFoundException
167
     * @throws ContainerExceptionInterface
168
     */
169
    private function getCache(
170
        ServiceLocatorInterface $container,
171
        string|array|CacheItemPoolInterface $cache,
172
        string $serviceName
173
    ): CacheItemPoolInterface {
174
        if (is_string($cache)) {
0 ignored issues
show
introduced by
The condition is_string($cache) is always false.
Loading history...
175
            $cache = $this->buildService($container, Cache::class, ['name' => $cache], $serviceName);
176
        }
177
178
        if ($cache instanceof DoctrineProvider) {
0 ignored issues
show
introduced by
$cache is never a sub-type of Doctrine\Common\Cache\Psr6\DoctrineProvider.
Loading history...
179
            $cache = $cache->getPool();
180
        }
181
182
        if (!$cache instanceof CacheItemPoolInterface) {
0 ignored issues
show
introduced by
$cache is never a sub-type of Psr\Cache\CacheItemPoolInterface.
Loading history...
183
            throw new ServiceNotCreatedException(
184
                sprintf('The \'cache\' configuration must be an object of type \'%s\'', CacheItemPoolInterface::class)
185
            );
186
        }
187
188
        return $cache;
189
    }
190
191
    /**
192
     * @throws ServiceNotCreatedException
193
     * @throws ServiceNotFoundException
194
     * @throws ContainerExceptionInterface
195
     */
196
    private function getRepositoryFactory(
197
        ContainerInterface $container,
198
        string|RepositoryFactory $factory,
199
        string $serviceName
200
    ): RepositoryFactory {
201
        if (is_string($factory)) {
202
            if ($container->has($factory)) {
203
                $factory = $this->getService($container, $factory, $serviceName);
204
            } elseif (class_exists($factory) && is_a($factory, RepositoryFactory::class, true)) {
205
                $factory = new $factory();
206
            }
207
        }
208
209
        if (!$factory instanceof RepositoryFactory) {
210
            throw new ServiceNotCreatedException(
211
                sprintf(
212
                    'The \'repository_factory\' configuration must be an object of type \'%s\'; '
213
                    . '\'%s\' provided for service \'%s\'',
214
                    RepositoryFactory::class,
215
                    is_object($factory) ? get_class($factory) : gettype($factory),
216
                    $serviceName
217
                )
218
            );
219
        }
220
221
        return $factory;
222
    }
223
224
    /**
225
     * @param array<mixed>|null $options
226
     *
227
     * @return array<mixed>
228
     *
229
     * @throws ServiceNotCreatedException
230
     * @throws ServiceNotFoundException
231
     * @throws ContainerExceptionInterface
232
     */
233
    private function getOptions(ContainerInterface $container, string $serviceName, ?array $options): array
234
    {
235
        if (null === $options) {
0 ignored issues
show
introduced by
The condition null === $options is always false.
Loading history...
236
            /** @var DoctrineConfigInterface $doctrineConfig */
237
            $doctrineConfig = $this->getService($container, DoctrineConfigInterface::class, $serviceName);
238
239
            if (
240
                !$doctrineConfig instanceof DoctrineConfigInterface
241
                || !$doctrineConfig->hasConfigurationConfig($serviceName)
242
            ) {
243
                throw new ServiceNotCreatedException(
244
                    sprintf('Unable to find configuration for \'%s\'', $serviceName)
245
                );
246
            }
247
248
            $options = $doctrineConfig->getConfigurationConfig($serviceName);
249
        }
250
251
        return array_replace_recursive($this->defaultOptions, $options);
252
    }
253
254
    /**
255
     * @param array<string, class-string<Type>> $types
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, class-string<Type>> at position 4 could not be parsed: Unknown type name 'class-string' at position 4 in array<string, class-string<Type>>.
Loading history...
256
     *
257
     * @throws ServiceNotCreatedException
258
     */
259
    private function registerCustomTypes(array $types): void
260
    {
261
        foreach ($types as $typeName => $typeClassName) {
262
            try {
263
                if (Type::hasType($typeName)) {
264
                    Type::overrideType($typeName, $typeClassName);
265
                } else {
266
                    Type::addType($typeName, $typeClassName);
267
                }
268
            } catch (DBALException $e) {
269
                throw new ServiceNotCreatedException(
270
                    sprintf('The doctrine type \'%s\' could not be registered', $typeName),
271
                    $e->getCode(),
272
                    $e
273
                );
274
            }
275
        }
276
    }
277
}
278