Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
272 / 272
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
BootstrapEngineFactory
100.00% covered (success)
100.00%
271 / 271
100.00% covered (success)
100.00%
6 / 6
6
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
1 / 1
1
 createCrudAndWorkflow
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
1 / 1
1
 createMetadataAndEngineApis
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
1 / 1
1
 createWidgetsAndMedia
100.00% covered (success)
100.00%
58 / 58
100.00% covered (success)
100.00%
1 / 1
1
 createProjectsAndDocuments
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
1
 createCommentsSubsystem
100.00% covered (success)
100.00%
55 / 55
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Core\Bootstrap\Configurator;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Engine\Application\Navigation\RecordNavigationService;
12use App\Core\Engine\Application\Persistence\UniversalPersistenceManager;
13use App\Core\Engine\Application\Query\RelationResolver;
14use App\Core\Engine\Application\Query\UniversalQueryBuilder;
15use App\Core\Engine\Application\Security\PermissionGuard;
16use App\Core\Engine\Application\Service\PrefixGeneratorService;
17use App\Core\Engine\Application\Service\UniversalCrudService;
18use App\Core\Engine\Application\Transformer\DefaultTransformersFactory;
19use App\Core\Engine\Application\Transformer\UiTypeTransformerPipeline;
20use App\Core\Engine\Application\Validator\UniversalValidationEngine;
21use App\Core\Engine\Domain\DataSource\DynamicDataProviderRegistry;
22use App\Core\Engine\Domain\Repository\MetadataRepositoryInterface;
23use App\Core\Engine\Infrastructure\Repository\SqlAuditRepository;
24use App\Core\Engine\Application\Service\HierarchyService;
25use App\Core\Engine\Application\Service\ImageUploadService;
26use App\Core\Engine\Application\Service\RecordFavoriteService;
27use App\Core\Engine\Application\Service\RecordPinService;
28use App\Core\Engine\Application\Service\RelationMmManager;
29use App\Core\Engine\Infrastructure\Repository\SqlPrefixRepository;
30use App\Core\Engine\Presentation\Api\CentralEngineApiController;
31use App\Core\Engine\Presentation\Api\GridWidgetApiController;
32use App\Core\Engine\Presentation\Api\GridWidgetManagementApiController;
33use App\Core\Engine\Presentation\Api\KanbanApiController;
34use App\Core\Engine\Presentation\Api\MediaUploadApiController;
35use App\Core\Engine\Presentation\Api\ModuleFieldsApiController;
36use App\Core\Engine\Presentation\Api\ModuleRelationsApiController;
37use App\Core\Engine\Presentation\Api\PicklistValuesApiController;
38use App\Core\Engine\Presentation\Api\RelationMmApiController;
39use App\Core\Translation\Presentation\Api\LanguageTranslationsApiController;
40use App\Core\Engine\Presentation\Web\CentralEngineWebController;
41use App\Core\Event\SystemEventDispatcher;
42use App\Core\Grid\Widget\ChartWidgetRenderer;
43use App\Core\Grid\Widget\DiagnosticsWidgetRenderer;
44use App\Core\Grid\Widget\ListWidgetRenderer;
45use App\Core\Grid\Widget\PhysicalTableWidgetRenderer;
46use App\Core\Grid\Widget\VirtualTableWidgetRenderer;
47use App\Core\Grid\Widget\WidgetRegistry;
48use App\Core\Grid\Widget\Application\WidgetAggregationDataService;
49use App\Core\Grid\Widget\Application\WidgetListQueryService;
50use App\Core\Preference\Infrastructure\Repository\SqlUserPreferenceRepository;
51use App\Modules\Dashboard\Application\Service\DashboardWidgetsService;
52use App\Modules\Dashboard\Infrastructure\Widget\CurrentEventsWidgetRenderer;
53use App\Modules\Dashboard\Infrastructure\Widget\OverdueEventsWidgetRenderer;
54use App\Core\Instance\Application\Service\InstanceContextManagerInterface;
55use App\Core\Instance\Application\Service\RemoteInstanceEngineGatewayInterface;
56use App\Modules\About\Application\Service\SystemDiagnosticsService;
57use App\Modules\About\Application\Service\SystemDiagnosticsServiceInterface;
58use App\Modules\About\Infrastructure\Repository\SqlSystemRequirementsRepository;
59use App\Modules\Automation\Application\Service\WorkflowDagEngine;
60use App\Modules\Automation\Infrastructure\Repository\SqlWorkflowRepository;
61use App\Modules\Automation\Presentation\Api\WorkflowApiController;
62use App\Core\Engine\Application\Security\PermissionContextFactory;
63use App\Modules\Comments\Application\Service\CommentAttachmentService;
64use App\Modules\Comments\Application\Service\CommentHierarchyRollupService;
65use App\Modules\Comments\Application\Service\CommentMentionParserService;
66use App\Modules\Comments\Application\Service\CommentService;
67use App\Modules\Comments\Application\Service\CommentMentionsQueryService;
68use App\Modules\Comments\Infrastructure\Repository\SqlCommentRepository;
69use App\Modules\Comments\Presentation\Api\CommentsApiController;
70use App\Modules\Comments\Presentation\Htmx\CommentsHtmxController;
71use App\Modules\Comments\Presentation\Htmx\MentionsHtmxController;
72use App\Modules\Comments\Presentation\Web\CommentAttachmentDownloadWebController;
73use App\Modules\Comments\Presentation\Web\MentionsWebController;
74use App\Modules\Documents\Application\Service\DocumentUploadService;
75use App\Modules\Documents\Application\Service\DocumentVersioningService;
76use App\Modules\Documents\Presentation\Api\DocumentApiController;
77use App\Modules\Documents\Presentation\Web\DocumentDownloadWebController;
78use App\Modules\Projects\Application\Service\ProjectGanttDataTransformer;
79use App\Modules\Projects\Application\Service\ProjectProgressCalculator;
80use App\Modules\Projects\Presentation\Api\ProjectGanttApiController;
81use Nyholm\Psr7\Factory\Psr17Factory;
82use PDO;
83use Twig\Environment as TwigEnvironment;
84use Yiisoft\Cache\CacheInterface;
85
86/**
87 * BootstrapEngineFactory.
88 *
89 * Configures and instantiates the Central Engine core services, transformer pipeline, and controllers.
90 *
91 * @package App\Core\Bootstrap\Configurator
92 */
93final readonly class BootstrapEngineFactory
94{
95    public UniversalCrudService $universalCrudService;
96    public CentralEngineApiController $centralEngineApiController;
97    public PicklistValuesApiController $picklistValuesApiController;
98    public LanguageTranslationsApiController $languageTranslationsApiController;
99    public ModuleFieldsApiController $moduleFieldsApiController;
100    public ModuleRelationsApiController $moduleRelationsApiController;
101    public RelationMmManager $relationMmManager;
102    public RelationMmApiController $relationMmApiController;
103    public GridWidgetApiController $gridWidgetApiController;
104    public GridWidgetManagementApiController $gridWidgetManagementApiController;
105    public CentralEngineWebController $centralEngineWebController;
106    public WorkflowApiController $workflowApiController;
107    public WorkflowDagEngine $workflowEngine;
108    public MediaUploadApiController $mediaUploadApiController;
109    public ProjectGanttApiController $projectGanttApiController;
110    public KanbanApiController $kanbanApiController;
111    public DocumentApiController $documentApiController;
112    public DocumentDownloadWebController $documentDownloadWebController;
113    public CommentService $commentService;
114    public CommentsApiController $commentsApiController;
115    public CommentsHtmxController $commentsHtmxController;
116    public CommentAttachmentDownloadWebController $commentAttachmentDownloadWebController;
117    public CommentMentionsQueryService $commentMentionsQueryService;
118    public MentionsWebController $mentionsWebController;
119    public MentionsHtmxController $mentionsHtmxController;
120    public MetadataRepositoryInterface $metadataRepo;
121
122    /**
123     * BootstrapEngineFactory constructor.
124     *
125     * @param BootstrapEngineContext $context Bootstrap engine configuration context.
126     */
127    public function __construct(BootstrapEngineContext $context)
128    {
129        $this->metadataRepo = $context->metadataRepo;
130        [
131            $this->workflowEngine,
132            $this->workflowApiController,
133            $this->universalCrudService,
134        ] = $this->createCrudAndWorkflow($context);
135
136        [
137            $this->picklistValuesApiController,
138            $this->moduleFieldsApiController,
139            $this->moduleRelationsApiController,
140            $this->relationMmManager,
141            $this->relationMmApiController,
142            $this->centralEngineApiController,
143            $this->languageTranslationsApiController,
144        ] = $this->createMetadataAndEngineApis($context);
145
146        [
147            $this->gridWidgetApiController,
148            $this->centralEngineWebController,
149            $this->mediaUploadApiController,
150            $this->gridWidgetManagementApiController,
151        ] = $this->createWidgetsAndMedia($context);
152
153        [
154            $this->projectGanttApiController,
155            $this->kanbanApiController,
156            $this->documentApiController,
157            $this->documentDownloadWebController,
158        ] = $this->createProjectsAndDocuments($context);
159
160        [
161            $this->commentService,
162            $this->commentMentionsQueryService,
163            $this->commentsApiController,
164            $this->commentsHtmxController,
165            $this->mentionsWebController,
166            $this->mentionsHtmxController,
167            $this->commentAttachmentDownloadWebController,
168        ] = $this->createCommentsSubsystem($context);
169    }
170
171    /**
172     * @return array{0: WorkflowDagEngine, 1: WorkflowApiController, 2: UniversalCrudService}
173     */
174    private function createCrudAndWorkflow(BootstrapEngineContext $context): array
175    {
176        $queryBuilder = new UniversalQueryBuilder($context->pdo, new RelationResolver($context->pdo));
177        $persistenceManager = new UniversalPersistenceManager(
178            $context->pdo,
179            $context->clientPdo,
180            $context->prefix
181        );
182        $permissionGuard = new PermissionGuard($context->accessRepo, $context->profileRepo);
183        $validationEngine = new UniversalValidationEngine($context->pdo);
184
185        $transformerPipeline = new UiTypeTransformerPipeline(
186            DefaultTransformersFactory::createDefaultTransformers()
187        );
188
189        $prefixRepo = new SqlPrefixRepository($context->pdo);
190        $prefixGenerator = new PrefixGeneratorService($prefixRepo);
191
192        $navigationService = new RecordNavigationService(
193            $context->metadataRepo,
194            $permissionGuard,
195            $queryBuilder,
196            $context->cache
197        );
198
199        $workflowRepo = new SqlWorkflowRepository($context->pdo, $context->prefix);
200        $workflowEngine = new WorkflowDagEngine($workflowRepo, $context->pdo, $context->prefix);
201        $workflowApiController = new WorkflowApiController(
202            $workflowRepo,
203            $workflowEngine,
204            $context->psr17Factory
205        );
206
207        $universalCrudService = new UniversalCrudService(
208            $context->metadataRepo,
209            $permissionGuard,
210            $queryBuilder,
211            $persistenceManager,
212            $transformerPipeline,
213            $validationEngine,
214            $context->eventDispatcher,
215            $navigationService,
216            $context->auditRepo,
217            $context->dataProviderRegistry,
218            $prefixGenerator,
219            $workflowEngine,
220            null,
221            $context->profileRepo
222        );
223
224        return [$workflowEngine, $workflowApiController, $universalCrudService];
225    }
226
227    /**
228     * @return array{
229     *     0: PicklistValuesApiController,
230     *     1: ModuleFieldsApiController,
231     *     2: ModuleRelationsApiController,
232     *     3: RelationMmManager,
233     *     4: RelationMmApiController,
234     *     5: CentralEngineApiController,
235     *     6: LanguageTranslationsApiController
236     * }
237     */
238    private function createMetadataAndEngineApis(BootstrapEngineContext $context): array
239    {
240        $picklistValuesApiController = new PicklistValuesApiController(
241            $context->pdo,
242            $context->psr17Factory
243        );
244        $moduleFieldsApiController = new ModuleFieldsApiController(
245            $context->pdo,
246            $context->psr17Factory,
247            $context->metadataRepo
248        );
249        $moduleRelationsApiController = new ModuleRelationsApiController(
250            $context->pdo,
251            $context->psr17Factory,
252            $context->metadataRepo
253        );
254        $relationMmManager = new RelationMmManager(
255            $context->metadataRepo,
256            $context->pdo
257        );
258        $relationMmApiController = new RelationMmApiController(
259            $relationMmManager,
260            $context->twig,
261            $context->psr17Factory
262        );
263
264        $centralEngineApiController = new CentralEngineApiController(
265            $this->universalCrudService,
266            $context->psr17Factory,
267            $context->instanceContextManager,
268            $context->remoteGateway,
269            new HierarchyService($context->pdo),
270            null,
271            null,
272            $context->pdo,
273            $context->prefix,
274            $relationMmManager,
275            new RecordFavoriteService($context->pdo),
276            new RecordPinService($context->pdo)
277        );
278
279        $languageTranslationsApiController = new LanguageTranslationsApiController(
280            $context->psr17Factory,
281            $context->pdo,
282            dirname(__DIR__, 4) . '/storage/cache/translations',
283            $context->prefix
284        );
285
286        return [
287            $picklistValuesApiController,
288            $moduleFieldsApiController,
289            $moduleRelationsApiController,
290            $relationMmManager,
291            $relationMmApiController,
292            $centralEngineApiController,
293            $languageTranslationsApiController,
294        ];
295    }
296
297    /**
298     * @return array{
299     *     0: GridWidgetApiController,
300     *     1: CentralEngineWebController,
301     *     2: MediaUploadApiController,
302     *     3: GridWidgetManagementApiController
303     * }
304     */
305    private function createWidgetsAndMedia(BootstrapEngineContext $context): array
306    {
307        $diagService = $context->diagnosticsService ?? new SystemDiagnosticsService(
308            new SqlSystemRequirementsRepository($context->pdo, $context->prefix),
309            $context->pdo,
310            dirname(__DIR__, 4)
311        );
312
313        $dashWidgetsService = new DashboardWidgetsService(
314            $context->pdo,
315            new SqlUserPreferenceRepository($context->pdo, $context->prefix),
316            $context->instanceContextManager,
317            null,
318            $context->prefix,
319            $context->appProfile
320        );
321
322        $queryBuilder = new UniversalQueryBuilder($context->pdo, new RelationResolver($context->pdo));
323        $aggregationService = new WidgetAggregationDataService($context->pdo, $context->metadataRepo);
324        $listQueryService = new WidgetListQueryService($context->metadataRepo, $queryBuilder);
325
326        $widgetRegistry = new WidgetRegistry([
327            new DiagnosticsWidgetRenderer($diagService, $context->twig),
328            new VirtualTableWidgetRenderer($this->universalCrudService, $context->twig),
329            new PhysicalTableWidgetRenderer($this->universalCrudService, $context->twig),
330            new CurrentEventsWidgetRenderer($dashWidgetsService, $context->twig),
331            new OverdueEventsWidgetRenderer($dashWidgetsService, $context->twig),
332            new ChartWidgetRenderer($aggregationService, $context->twig),
333            new ListWidgetRenderer($listQueryService, $context->twig),
334        ]);
335
336        $gridWidgetApiController = new GridWidgetApiController(
337            $context->metadataRepo,
338            $widgetRegistry,
339            $context->psr17Factory,
340            $context->pdo
341        );
342
343        $gridWidgetManagementApiController = new GridWidgetManagementApiController(
344            $context->metadataRepo,
345            $aggregationService,
346            $listQueryService,
347            $widgetRegistry,
348            $context->psr17Factory,
349            $context->pdo,
350            $context->appProfile,
351            $context->instanceContextManager,
352            $context->translator
353        );
354
355        $centralEngineWebController = new CentralEngineWebController(
356            $context->twig,
357            $context->psr17Factory,
358            $this->centralEngineApiController
359        );
360
361        $uploadService = new ImageUploadService(dirname(__DIR__, 4) . '/public');
362        $mediaUploadApiController = new MediaUploadApiController(
363            $uploadService,
364            $context->psr17Factory
365        );
366
367        return [
368            $gridWidgetApiController,
369            $centralEngineWebController,
370            $mediaUploadApiController,
371            $gridWidgetManagementApiController,
372        ];
373    }
374
375    /**
376     * @return array{
377     *     0: ProjectGanttApiController,
378     *     1: KanbanApiController,
379     *     2: DocumentApiController,
380     *     3: DocumentDownloadWebController
381     * }
382     */
383    private function createProjectsAndDocuments(BootstrapEngineContext $context): array
384    {
385        $projectGanttApiController = new ProjectGanttApiController(
386            $context->pdo,
387            new ProjectGanttDataTransformer(),
388            $context->psr17Factory
389        );
390
391        $kanbanApiController = new KanbanApiController(
392            $context->pdo,
393            new ProjectProgressCalculator(),
394            $context->psr17Factory
395        );
396
397        $docVersioningService = new DocumentVersioningService($context->pdo);
398        $docUploadService = new DocumentUploadService($context->pdo, $docVersioningService);
399        $documentApiController = new DocumentApiController(
400            $docUploadService,
401            $docVersioningService,
402            $context->psr17Factory
403        );
404        $documentDownloadWebController = new DocumentDownloadWebController(
405            $docUploadService,
406            $this->universalCrudService
407        );
408
409        return [
410            $projectGanttApiController,
411            $kanbanApiController,
412            $documentApiController,
413            $documentDownloadWebController,
414        ];
415    }
416
417    /**
418     * @return array{
419     *     0: CommentService,
420     *     1: CommentMentionsQueryService,
421     *     2: CommentsApiController,
422     *     3: CommentsHtmxController,
423     *     4: MentionsWebController,
424     *     5: MentionsHtmxController,
425     *     6: CommentAttachmentDownloadWebController
426     * }
427     */
428    private function createCommentsSubsystem(BootstrapEngineContext $context): array
429    {
430        $sqlCommentRepo = new SqlCommentRepository($context->pdo);
431        $commentRollupService = new CommentHierarchyRollupService($context->pdo);
432        $commentMentionParser = new CommentMentionParserService();
433        $commentAttachmentService = new CommentAttachmentService($sqlCommentRepo);
434        $commentService = new CommentService(
435            $sqlCommentRepo,
436            $commentRollupService,
437            $commentAttachmentService,
438            $commentMentionParser
439        );
440        $commentMentionsQueryService = new CommentMentionsQueryService(
441            $context->pdo,
442            $commentMentionParser,
443            null,
444            $context->prefix
445        );
446        $commentsApiController = new CommentsApiController(
447            $commentService,
448            $context->psr17Factory,
449            null,
450            null,
451            $commentMentionsQueryService,
452            $context->pdo
453        );
454        $permissionContextFactory = new PermissionContextFactory($context->session, $context->pdo);
455        $commentsHtmxController = new CommentsHtmxController(
456            $commentService,
457            $context->twig,
458            $context->psr17Factory,
459            $permissionContextFactory,
460            $commentsApiController
461        );
462        $mentionsWebController = new MentionsWebController(
463            $context->twig,
464            $commentMentionsQueryService,
465            $context->psr17Factory
466        );
467        $mentionsHtmxController = new MentionsHtmxController(
468            $context->twig,
469            $commentMentionsQueryService,
470            $context->psr17Factory
471        );
472        $commentAttachmentDownloadWebController = new CommentAttachmentDownloadWebController(
473            $commentService,
474            $commentAttachmentService
475        );
476
477        return [
478            $commentService,
479            $commentMentionsQueryService,
480            $commentsApiController,
481            $commentsHtmxController,
482            $mentionsWebController,
483            $mentionsHtmxController,
484            $commentAttachmentDownloadWebController,
485        ];
486    }
487}