Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
89.06% |
57 / 64 |
|
66.67% |
2 / 3 |
CRAP | |
0.00% |
0 / 1 |
ApieRouteLoader | |
89.06% |
57 / 64 |
|
66.67% |
2 / 3 |
17.38 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
load | |
88.71% |
55 / 62 |
|
0.00% |
0 / 1 |
15.32 | |||
supports | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | namespace Apie\ApieBundle\Routing; |
3 | |
4 | use Apie\Cms\RouteDefinitions\CmsRouteDefinitionProvider; |
5 | use Apie\Common\Interfaces\HasRouteDefinition; |
6 | use Apie\Common\Interfaces\RouteDefinitionProviderInterface; |
7 | use Apie\Common\Lists\UrlPrefixList; |
8 | use Apie\Common\RouteDefinitions\ActionHashmap; |
9 | use Apie\Common\RouteDefinitions\PossibleRoutePrefixProvider; |
10 | use Apie\Core\ApieLib; |
11 | use Apie\Core\Attributes\Route as AttributesRoute; |
12 | use Apie\Core\BoundedContext\BoundedContextHashmap; |
13 | use Apie\Core\ContextBuilders\ContextBuilderFactory; |
14 | use Apie\Core\Enums\RequestMethod; |
15 | use Apie\Core\ValueObjects\UrlRouteDefinition; |
16 | use Apie\RestApi\RouteDefinitions\RestApiRouteDefinitionProvider; |
17 | use Psr\Log\LoggerInterface; |
18 | use Psr\Log\NullLogger; |
19 | use ReflectionClass; |
20 | use Symfony\Component\Config\Loader\Loader; |
21 | use Symfony\Component\Config\Resource\DirectoryResource; |
22 | use Symfony\Component\Config\Resource\FileExistenceResource; |
23 | use Symfony\Component\Config\Resource\GlobResource; |
24 | use Symfony\Component\Config\Resource\ReflectionClassResource; |
25 | use Symfony\Component\Routing\Route; |
26 | use Symfony\Component\Routing\RouteCollection; |
27 | |
28 | /** |
29 | * Loads the Apie routing into the symfony loader system. |
30 | */ |
31 | final class ApieRouteLoader extends Loader |
32 | { |
33 | private bool $loaded = false; |
34 | |
35 | /** |
36 | * @param array<string, string> $scanBoundedContexts |
37 | */ |
38 | public function __construct( |
39 | private readonly RouteDefinitionProviderInterface $routeProvider, |
40 | private readonly BoundedContextHashmap $boundedContextHashmap, |
41 | private readonly PossibleRoutePrefixProvider $routePrefixProvider, |
42 | private readonly ContextBuilderFactory $contextBuilder, |
43 | private readonly LoggerInterface $logger = new NullLogger(), |
44 | private readonly array $scanBoundedContexts = [], |
45 | ) { |
46 | } |
47 | |
48 | /** |
49 | * @param mixed $resource |
50 | * @param mixed $type |
51 | */ |
52 | public function load($resource, $type = null): RouteCollection |
53 | { |
54 | if (true === $this->loaded) { |
55 | throw new \RuntimeException('Do not add the "apie" loader twice'); |
56 | } |
57 | |
58 | $routes = new RouteCollection(); |
59 | $classesForCaching = [ |
60 | __CLASS__, |
61 | $this->routeProvider, |
62 | $this->boundedContextHashmap, |
63 | $this->routePrefixProvider, |
64 | RestApiRouteDefinitionProvider::class, |
65 | CmsRouteDefinitionProvider::class, |
66 | RouteDefinitionProviderInterface::class, |
67 | HasRouteDefinition::class, |
68 | UrlRouteDefinition::class, |
69 | RequestMethod::class, |
70 | UrlPrefixList::class, |
71 | ActionHashmap::class, |
72 | ApieLib::class, |
73 | AttributesRoute::class, |
74 | ]; |
75 | if (!empty($this->scanBoundedContexts['search_path'])) { |
76 | if (!is_dir($this->scanBoundedContexts['search_path'])) { |
77 | if (!@mkdir($this->scanBoundedContexts['search_path'], recursive: true)) { |
78 | $this->logger->error('I could not create path: "' . $this->scanBoundedContexts['search_path'] . '"'); |
79 | } |
80 | } |
81 | if (is_dir($this->scanBoundedContexts['search_path'])) { |
82 | $routes->addResource(new GlobResource($this->scanBoundedContexts['search_path'], '*', true)); |
83 | } else { |
84 | $routes->addResource(new FileExistenceResource($this->scanBoundedContexts['search_path'])); |
85 | } |
86 | } |
87 | |
88 | foreach ($classesForCaching as $classForCaching) { |
89 | if (is_object($classForCaching) || class_exists($classForCaching)) { |
90 | $routes->addResource(new ReflectionClassResource(new ReflectionClass($classForCaching))); |
91 | } |
92 | } |
93 | $pathsHandled = []; |
94 | foreach ($this->boundedContextHashmap as $boundedContext) { |
95 | foreach ($boundedContext->resources as $resource) { |
96 | $routes->addResource(new ReflectionClassResource($resource)); |
97 | $path = dirname($resource->getFileName()); |
98 | if (!isset($pathsHandled[$path])) { |
99 | $pathsHandled[$path] = true; |
100 | $routes->addResource(new DirectoryResource($path)); |
101 | } |
102 | } |
103 | } |
104 | $apieContext = $this->contextBuilder->createGeneralContext([ |
105 | 'route-gen' => true, |
106 | ]); |
107 | foreach ($this->boundedContextHashmap as $boundedContextId => $boundedContext) { |
108 | foreach ($this->routeProvider->getActionsForBoundedContext($boundedContext, $apieContext) as $routeDefinition) { |
109 | $routes->addResource(new ReflectionClassResource(new ReflectionClass($routeDefinition))); |
110 | /** @var HasRouteDefinition $routeDefinition */ |
111 | $prefix = $this->routePrefixProvider->getPossiblePrefixes($routeDefinition); |
112 | |
113 | $requirements = $prefix->getRouteRequirements(); |
114 | $url = $routeDefinition->getUrl(); |
115 | $placeholders = $url->getPlaceholders(); |
116 | if (in_array('properties', $placeholders)) { |
117 | $requirements['properties'] = '[a-zA-Z0-9]+(/[a-zA-Z0-9]+)*'; |
118 | } |
119 | $path = $prefix . $boundedContextId . '/' . ltrim($url, '/'); |
120 | $method = $routeDefinition->getMethod(); |
121 | $defaults = $routeDefinition->getRouteAttributes() |
122 | + [ |
123 | '_controller' => $routeDefinition->getController(), |
124 | '_is_apie' => true, |
125 | ]; |
126 | $route = (new Route($path, $defaults, $requirements))->setMethods([$method->value]); |
127 | $routes->add( |
128 | 'apie.' . $boundedContextId . '.' . $routeDefinition->getOperationId(), |
129 | $route |
130 | ); |
131 | } |
132 | } |
133 | |
134 | return $routes; |
135 | } |
136 | |
137 | /** |
138 | * @param mixed $resource |
139 | * @param mixed $type |
140 | */ |
141 | public function supports($resource, $type = null): bool |
142 | { |
143 | return 'apie' === $type; |
144 | } |
145 | } |