• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nextras / orm / 28897596352

07 Jul 2026 08:47PM UTC coverage: 92.123% (-0.04%) from 92.166%
28897596352

push

github

web-flow
Merge pull request #796 from nextras/js/orm-configurator-extension

add Extension extensibility entry point

44 of 50 new or added lines in 8 files covered. (88.0%)

4339 of 4710 relevant lines covered (92.12%)

5.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.0
/src/Bridges/NetteDI/OrmExtension.php
1
<?php declare(strict_types = 1);
2

3
namespace Nextras\Orm\Bridges\NetteDI;
4

5

6
use Nette\Caching\Cache;
7
use Nette\DI\CompilerExtension;
8
use Nette\DI\ContainerBuilder;
9
use Nette\DI\Definitions\Statement;
10
use Nette\Schema\Expect;
11
use Nette\Schema\Schema;
12
use Nextras\Dbal\IConnection;
13
use Nextras\Orm\Entity\IEntity;
14
use Nextras\Orm\Entity\Reflection\MetadataParserFactory;
15
use Nextras\Orm\Exception\InvalidStateException;
16
use Nextras\Orm\Extension;
17
use Nextras\Orm\Mapper\Dbal\DbalMapperCoordinator;
18
use Nextras\Orm\Model\IModel;
19
use Nextras\Orm\Model\MetadataStorage;
20
use Nextras\Orm\Model\Model;
21
use Nextras\Orm\Repository\IRepository;
22
use stdClass;
23
use function is_subclass_of;
24

25

26
/**
27
 * @property-read stdClass $config
28
 */
29
class OrmExtension extends CompilerExtension
30
{
31
        protected ContainerBuilder $builder;
32

33
        protected IRepositoryFinder $repositoryFinder;
34

35
        /** @var class-string<IModel> */
36
        protected string $modelClass;
37

38

39
        public function getConfigSchema(): Schema
40
        {
41
                return Expect::structure([
6✔
42
                        'model' => Expect::string()->default(Model::class),
6✔
43
                        'extensions' => Expect::arrayOf('string|Nette\DI\Definitions\Statement')->default([]),
6✔
44
                        'repositoryFinder' => Expect::string()->default(PhpDocRepositoryFinder::class),
6✔
45
                        'initializeMetadata' => Expect::bool()->default(false),
6✔
46
                        'autowiredInternalServices' => Expect::bool()->default(true),
6✔
47
                        'connection' => Expect::string(),
6✔
48
                ]);
49
        }
50

51

52
        public function loadConfiguration(): void
53
        {
54
                $this->builder = $this->getContainerBuilder();
6✔
55
                $this->modelClass = $this->config->model;
6✔
56

57
                $extensions = [];
6✔
58
                foreach ($this->config->extensions as $extension) {
6✔
59
                        $extensions[] = is_string($extension) ? new Statement($extension) : $extension;
6✔
60
                }
61

62
                $repositoryFinderClass = $this->config->repositoryFinder;
6✔
63
                if (!is_subclass_of($repositoryFinderClass, IRepositoryFinder::class)) {
6✔
64
                        throw new InvalidStateException('Repository finder does not implement Nextras\Orm\Bridges\NetteDI\IRepositoryFinder interface.');
×
65
                }
66
                $this->repositoryFinder = new $repositoryFinderClass($this->modelClass, $extensions, $this->builder, $this);
6✔
67

68
                $repositories = $this->repositoryFinder->loadConfiguration();
6✔
69

70
                $this->setupCache();
6✔
71
                $this->setupDependencyProvider();
6✔
72
                $this->setupDbalMapperDependencies();
6✔
73
                $this->setupMetadataParserFactory($extensions);
6✔
74

75
                if ($repositories !== null) {
6✔
76
                        $repositoriesConfig = Model::getConfiguration($repositories);
6✔
77
                        $this->setupMetadataStorage($repositoriesConfig[2]);
6✔
78
                        $this->setupModel($this->modelClass, $repositoriesConfig, $extensions);
6✔
79
                }
80

81
                $this->initializeMetadata($this->config->initializeMetadata);
6✔
82
        }
6✔
83

84

85
        public function beforeCompile(): void
86
        {
87
                $repositories = $this->repositoryFinder->beforeCompile();
6✔
88

89
                if ($repositories !== null) {
6✔
90
                        $extensions = [];
6✔
91
                        foreach ($this->config->extensions as $extension) {
6✔
NEW
92
                                $extensions[] = is_string($extension) ? new Statement($extension) : $extension;
×
93
                        }
94

95
                        $repositoriesConfig = Model::getConfiguration($repositories);
6✔
96
                        $this->setupMetadataStorage($repositoriesConfig[2]);
6✔
97
                        $this->setupModel($this->modelClass, $repositoriesConfig, $extensions);
6✔
98
                }
99

100
                $this->setupDbalMapperDependencies();
6✔
101
        }
6✔
102

103

104
        protected function setupCache(): void
105
        {
106
                $cacheName = $this->prefix('cache');
6✔
107
                if ($this->builder->hasDefinition($cacheName)) {
6✔
108
                        return;
×
109
                }
110

111
                $this->builder->addDefinition($cacheName)
6✔
112
                        ->setType(Cache::class)
6✔
113
                        ->setArguments([
6✔
114
                                'namespace' => $this->name,
6✔
115
                        ])
116
                        ->setAutowired(false);
6✔
117
        }
6✔
118

119

120
        protected function setupDependencyProvider(): void
121
        {
122
                $providerName = $this->prefix('dependencyProvider');
6✔
123
                if ($this->builder->hasDefinition($providerName)) {
6✔
124
                        return;
×
125
                }
126

127
                $this->builder->addDefinition($providerName)
6✔
128
                        ->setType(DependencyProvider::class)
6✔
129
                        ->setAutowired($this->config->autowiredInternalServices);
6✔
130
        }
6✔
131

132

133
        protected function setupDbalMapperDependencies(): void
134
        {
135
                if (count($this->builder->findByType(IConnection::class)) === 0) {
6✔
136
                        return;
6✔
137
                }
138

139
                $name = $this->prefix('mapperCoordinator');
6✔
140
                if ($this->builder->hasDefinition($name)) {
6✔
141
                        return;
6✔
142
                }
143

144
                $this->builder->addDefinition($name)
6✔
145
                        ->setType(DbalMapperCoordinator::class)
6✔
146
                        ->setArguments(
6✔
147
                                $this->config->connection !== null ? ['connection' => $this->config->connection] : [],
6✔
148
                        )
149
                        ->setAutowired($this->config->autowiredInternalServices);
6✔
150
        }
6✔
151

152

153
        /**
154
         * @param list<Extension> $extensions
155
         */
156
        protected function setupMetadataParserFactory(array $extensions): void
157
        {
158
                $factoryName = $this->prefix('metadataParserFactory');
6✔
159
                if ($this->builder->hasDefinition($factoryName)) {
6✔
160
                        return;
×
161
                }
162

163
                $this->builder->addDefinition($factoryName)
6✔
164
                        ->setType(MetadataParserFactory::class)
6✔
165
                        ->setArgument('extensions', $extensions)
6✔
166
                        ->setAutowired($this->config->autowiredInternalServices);
6✔
167
        }
6✔
168

169

170
        /**
171
         * @param array<class-string<IEntity>, class-string<IRepository<IEntity>>> $entityClassMap
172
         */
173
        protected function setupMetadataStorage(array $entityClassMap): void
174
        {
175
                $metadataName = $this->prefix('metadataStorage');
6✔
176
                if ($this->builder->hasDefinition($metadataName)) {
6✔
177
                        return;
×
178
                }
179

180
                $this->builder->addDefinition($metadataName)
6✔
181
                        ->setType(MetadataStorage::class)
6✔
182
                        ->setArguments([
6✔
183
                                'entityClassesMap' => $entityClassMap,
6✔
184
                                'cache' => $this->prefix('@cache'),
6✔
185
                                'metadataParserFactory' => $this->prefix('@metadataParserFactory'),
6✔
186
                                'repositoryLoader' => $this->prefix('@repositoryLoader'),
6✔
187
                        ])
188
                        ->setAutowired($this->config->autowiredInternalServices);
6✔
189
        }
6✔
190

191

192
        /**
193
         * @param class-string<IModel> $modelClass
194
         * @param array{
195
         *     array<class-string<IRepository<IEntity>>, true>,
196
         *     array<string, class-string<IRepository<IEntity>>>,
197
         *     array<class-string<IEntity>, class-string<IRepository<IEntity>>>
198
         *     } $repositoriesConfig
199
         * @param list<Statement> $extensions
200
         */
201
        protected function setupModel(string $modelClass, array $repositoriesConfig, array $extensions): void
202
        {
203
                $modelName = $this->prefix('model');
6✔
204
                if ($this->builder->hasDefinition($modelName)) {
6✔
205
                        return;
×
206
                }
207

208
                $this->builder->addDefinition($modelName)
6✔
209
                        ->setType($modelClass)
6✔
210
                        ->setArguments([
6✔
211
                                'configuration' => $repositoriesConfig,
6✔
212
                                'repositoryLoader' => $this->prefix('@repositoryLoader'),
6✔
213
                                'metadataStorage' => $this->prefix('@metadataStorage'),
6✔
214
                        ])
215
                        ->addSetup('foreach (? as $e) { $e->configureModel($service); }', [$extensions]);
6✔
216
        }
6✔
217

218

219
        protected function initializeMetadata(bool $init): void
220
        {
221
                if (!$init) {
6✔
222
                        return;
6✔
223
                }
224

225
                // getMetadata() to force initialization when Nette DI's lazy proxies are enabled.
226
                $this->initialization->addBody('$this->getService(?)->getLoadedMetadata();', [
×
227
                        $this->prefix('metadataStorage'),
×
228
                ]);
229
        }
×
230
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc