EntityRepositoryFactory   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 178
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 69
c 1
b 0
f 0
dl 0
loc 178
rs 10
wmc 11

5 Methods

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