Passed
Pull Request — master (#4)
by Alex
02:54
created

EntityRepositoryFactory::resolveClassName()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
c 0
b 0
f 0
dl 0
loc 19
rs 9.6111
cc 5
nc 3
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arp\LaminasDoctrine\Factory\Repository;
6
7
use Arp\LaminasDoctrine\Repository\EntityRepository;
8
use Arp\LaminasDoctrine\Repository\EntityRepositoryInterface;
9
use Arp\LaminasDoctrine\Repository\Persistence\PersistService;
10
use Arp\LaminasDoctrine\Repository\Persistence\PersistServiceInterface;
11
use Arp\LaminasDoctrine\Repository\Query\QueryService;
12
use Arp\LaminasDoctrine\Repository\Query\QueryServiceInterface;
13
use Arp\LaminasDoctrine\Repository\Query\QueryServiceManager;
14
use Arp\LaminasFactory\AbstractFactory;
15
use Arp\LaminasMonolog\Factory\FactoryLoggerProviderTrait;
16
use Laminas\ServiceManager\Exception\ContainerModificationsNotAllowedException;
17
use Laminas\ServiceManager\Exception\InvalidServiceException;
18
use Laminas\ServiceManager\Exception\ServiceNotCreatedException;
19
use Laminas\ServiceManager\Exception\ServiceNotFoundException;
20
use Laminas\ServiceManager\ServiceLocatorInterface;
21
use Psr\Container\ContainerExceptionInterface;
22
use Psr\Container\ContainerInterface;
23
use Psr\Container\NotFoundExceptionInterface;
24
use Psr\Log\NullLogger;
25
26
/**
27
 * @author  Alex Patterson <[email protected]>
28
 * @package Arp\LaminasDoctrine\Factory\Repository
29
 */
30
final class EntityRepositoryFactory extends AbstractFactory
31
{
32
    use FactoryLoggerProviderTrait;
33
34
    /**
35
     * The default configuration for all entity repositories
36
     *
37
     * @var array<mixed>
38
     */
39
    private array $defaultOptions = [
40
        'logger' => null,
41
        'query_service' => [
42
            'service_name' => QueryService::class,
43
            'logger' => null,
44
        ],
45
        'persist_service' => [
46
            'service_name' => PersistService::class,
47
            'logger' => null,
48
        ],
49
    ];
50
51
    /**
52
     * @param ContainerInterface&ServiceLocatorInterface $container
53
     * @param string                                     $requestedName
54
     * @param array<string, mixed>|null                  $options
55
     *
56
     * @return EntityRepositoryInterface
57
     *
58
     * @throws ServiceNotCreatedException
59
     * @throws ServiceNotFoundException
60
     * @throws ContainerExceptionInterface
61
     * @throws NotFoundExceptionInterface
62
     */
63
    public function __invoke(
64
        ContainerInterface $container,
65
        string $requestedName,
66
        array $options = null
67
    ): EntityRepositoryInterface {
68
        $options = array_replace_recursive(
69
            $this->defaultOptions,
70
            $this->getServiceOptions($container, $requestedName, 'repositories'),
71
            $options ?? []
72
        );
73
74
        $entityName = $options['entity_name'] ?? $requestedName;
75
        if (empty($entityName)) {
76
            throw new ServiceNotCreatedException(
77
                sprintf(
78
                    'The required \'entity_name\' configuration option is missing for service \'%s\'',
79
                    $requestedName
80
                )
81
            );
82
        }
83
84
        $queryService = $this->getQueryService(
85
            $container,
86
            $entityName,
87
            $options['query_service'] ?? [],
88
            $requestedName
89
        );
90
91
        $persistService = $this->getPersistService(
92
            $container,
93
            $entityName,
94
            $options['persist_service'] ?? [],
95
            $requestedName
96
        );
97
98
        $className = $this->resolveClassName($entityName, $options);
99
100
        return new $className(
101
            $entityName,
102
            $queryService,
103
            $persistService,
104
            $this->getLogger($container, $options['logger'] ?? null, $requestedName)
105
        );
106
    }
107
108
    /**
109
     * @param string               $entityName
110
     * @param array<string, mixed> $options
111
     *
112
     * @return class-string<EntityRepositoryInterface>
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<EntityRepositoryInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<EntityRepositoryInterface>.
Loading history...
113
     */
114
    private function resolveClassName(string $entityName, array $options = []): string
115
    {
116
        $className = $options['class_name'] ?? EntityRepository::class;
117
        if (empty($options['class_name'])) {
118
            $generatedClassNames = [
119
                str_replace('Entity', 'Repository', $entityName) . 'Repository',
120
                str_replace('Entity', 'Entity\\Repository', $entityName) . 'Repository',
121
            ];
122
            foreach ($generatedClassNames as $generatedClassName) {
123
                if (
124
                    class_exists($generatedClassName, true)
125
                    && is_subclass_of($generatedClassName, EntityRepositoryInterface::class, true)
126
                ) {
127
                    return $generatedClassName;
128
                }
129
            }
130
        }
131
132
        return $className;
133
    }
134
135
    /**
136
     * @param ServiceLocatorInterface $container
137
     * @param string                  $entityName
138
     * @param array<string, mixed>    $options
139
     * @param string                  $serviceName
140
     *
141
     * @return PersistServiceInterface
142
     *
143
     * @throws ServiceNotCreatedException
144
     * @throws ServiceNotFoundException
145
     * @throws ContainerExceptionInterface
146
     */
147
    private function getPersistService(
148
        ServiceLocatorInterface $container,
149
        string $entityName,
150
        array $options,
151
        string $serviceName
152
    ): PersistServiceInterface {
153
        $options = array_replace_recursive(
154
            $this->getServiceOptions($container, PersistService::class),
155
            $options
156
        );
157
        $options['entity_name'] ??= $entityName;
158
159
        return $this->buildService(
160
            $container,
161
            $options['service_name'] ?? PersistService::class,
162
            $options,
163
            $serviceName
164
        );
165
    }
166
167
    /**
168
     * @param ServiceLocatorInterface $container
169
     * @param string                  $entityName
170
     * @param array<string, mixed>    $options
171
     * @param string                  $serviceName
172
     *
173
     * @return QueryServiceInterface
174
     *
175
     * @throws ContainerExceptionInterface
176
     * @throws ServiceNotCreatedException
177
     * @throws ServiceNotFoundException
178
     * @throws ContainerModificationsNotAllowedException
179
     * @throws InvalidServiceException
180
     */
181
    private function getQueryService(
182
        ServiceLocatorInterface $container,
183
        string $entityName,
184
        array $options,
185
        string $serviceName
186
    ): QueryServiceInterface {
187
        /** @var QueryServiceManager $queryServiceManager */
188
        $queryServiceManager = $this->getService($container, QueryServiceManager::class, $serviceName);
189
190
        if ($queryServiceManager->has($entityName)) {
191
            return $queryServiceManager->get($entityName);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $queryServiceManager->get($entityName) could return the type array which is incompatible with the type-hinted return Arp\LaminasDoctrine\Repo...y\QueryServiceInterface. Consider adding an additional type-check to rule them out.
Loading history...
192
        }
193
194
        $options = array_replace_recursive($this->getServiceOptions($container, QueryService::class), $options);
195
        $options['entity_name'] ??= $entityName;
196
197
        $queryService = $this->buildService(
198
            $container,
199
            $options['service_name'] ?? QueryService::class,
200
            $options,
201
            $serviceName
202
        );
203
204
        $queryServiceManager->setService($entityName, $queryService);
205
206
        return $queryService;
207
    }
208
209
    /**
210
     * @param array<mixed> $defaultOptions
211
     */
212
    public function setDefaultOptions(array $defaultOptions): void
213
    {
214
        $this->defaultOptions = $defaultOptions;
215
    }
216
}
217