Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
67.31% covered (warning)
67.31%
35 / 52
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
BoundedContextHashmapFactory
67.31% covered (warning)
67.31%
35 / 52
66.67% covered (warning)
66.67%
2 / 3
18.91
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 create
57.50% covered (warning)
57.50%
23 / 40
0.00% covered (danger)
0.00%
0 / 1
8.76
 addSharedResources
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2namespace Apie\Common\Wrappers;
3
4use Apie\Common\Config\Configuration;
5use Apie\Common\Events\RegisterBoundedContexts;
6use Apie\Common\Other\AuditLog;
7use Apie\Common\ValueObjects\EntityNamespace;
8use Apie\Core\ApieLib;
9use Apie\Core\Attributes\Auditable;
10use Apie\Core\BackgroundProcess\SequentialBackgroundProcess;
11use Apie\Core\BoundedContext\BoundedContext;
12use Apie\Core\BoundedContext\BoundedContextHashmap;
13use Apie\Core\BoundedContext\BoundedContextId;
14use Apie\Core\Entities\EntityInterface;
15use Psr\EventDispatcher\EventDispatcherInterface;
16use Symfony\Component\Finder\Finder;
17
18/**
19 * Creates a BoundedContextHashmap instance from the ApieBundle configuration.
20 *
21 * @see Configuration in apie/apie-bundle for Symfony
22 * @see resources/apie.php in apie/laravel-apie for Laravel
23 */
24final class BoundedContextHashmapFactory
25{
26    /**
27     * @param array<string, mixed> $boundedContexts
28     * @param array<string, string> $scanBoundedContexts
29     */
30    public function __construct(
31        private readonly array $boundedContexts,
32        private readonly array $scanBoundedContexts,
33        private readonly EventDispatcherInterface $eventDispatcher
34    ) {
35    }
36
37    public function create(): BoundedContextHashmap
38    {
39        $result = [];
40        $entities = [];
41        foreach ($this->boundedContexts as $boundedContextId => $boundedContextConfig) {
42            $contextId = new BoundedContextId($boundedContextId);
43            $namespace = new EntityNamespace($boundedContextConfig['entities_namespace']);
44            $classes = $namespace->getClasses($boundedContextConfig['entities_folder']);
45            $entities = array_merge($entities, $classes->toStringArray());
46            $namespace = new EntityNamespace($boundedContextConfig['actions_namespace']);
47            $methods = $namespace->getMethods($boundedContextConfig['actions_folder']);
48            $result[$boundedContextId] = new BoundedContext(
49                $contextId,
50                $classes,
51                $methods
52            );
53        }
54        ApieLib::registerAlias(EntityInterface::class, implode('|', $entities));
55        if (!empty($this->scanBoundedContexts['search_path'])
56            && !empty($this->scanBoundedContexts['search_namespace'])
57            && is_dir($this->scanBoundedContexts['search_path'])
58        ) {
59            $paths = Finder::create()
60                ->in($this->scanBoundedContexts['search_path'])
61                ->depth(0)
62                ->directories();
63            $namespace = new EntityNamespace($this->scanBoundedContexts['search_namespace']);
64            foreach ($paths as $path) {
65                $contextId = new BoundedContextId(strtolower($path->getBasename()));
66                $pathNamespace = $namespace->getChildNamespace($path->getBasename());
67                $resourceNamespace = $pathNamespace->getChildNamespace('Resources');
68                $classes = $resourceNamespace->getClasses($path . '/Resources');
69                $methodNamespace = $pathNamespace->getChildNamespace('Actions');
70                $methods = $methodNamespace->getMethods($path . '/Actions');
71                $result[$contextId->toNative()] = new BoundedContext(
72                    $contextId,
73                    $classes,
74                    $methods
75                );
76            }
77        }
78        $map = new BoundedContextHashmap($result);
79        $event = new RegisterBoundedContexts($map);
80        $this->addSharedResources($event); // Laravel workaround, but in hindsight: the event should be deprecated
81        $this->eventDispatcher->dispatch($event);
82        
83        return $event->hashmap;
84    }
85
86    public function addSharedResources(RegisterBoundedContexts $registerBoundedContexts): void
87    {
88        foreach ($registerBoundedContexts->hashmap as $boundedContext) {
89            $resources = $boundedContext->resources;
90            /** @var BoundedContext $boundedContext */
91            $lists = $boundedContext->findRelatedClasses()->toStringArray();
92            if (in_array(SequentialBackgroundProcess::class, $lists)) {
93                $resources[] = new \ReflectionClass(SequentialBackgroundProcess::class);
94            }
95            $auditLogAdded = false;
96            foreach ($boundedContext->resources as $resource) {
97                foreach ($resource->getAttributes(Auditable::class) as $auditable) {
98                    $auditLogAdded = true;
99                }
100            }
101            if ($auditLogAdded) {
102                $resources[] = new \ReflectionClass(AuditLog::class);
103            }
104        }
105    }
106}