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