Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.15% covered (success)
96.15%
25 / 26
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
RouteCollectionBuilder
96.15% covered (success)
96.15%
25 / 26
75.00% covered (warning)
75.00%
3 / 4
11
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
 build
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 loadAttributeRoutes
87.50% covered (success)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
3.02
 getControllerDirectories
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2
3/**
4 * Route collection builder
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2026-01-18
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Routing;
21
22use phpMyFAQ\Configuration;
23use Symfony\Component\Routing\RouteCollection;
24
25/**
26 * Class RouteCollectionBuilder
27 *
28 * Builds a RouteCollection from controller attributes using PHP 8+ Route attributes.
29 * All routes are defined using #[Route] attributes directly on controller methods.
30 */
31class RouteCollectionBuilder
32{
33    private AttributeRouteLoader $attributeLoader;
34
35    public function __construct(
36        private readonly ?Configuration $configuration = null,
37    ) {
38        $this->attributeLoader = new AttributeRouteLoader();
39    }
40
41    /**
42     * Build a RouteCollection for the specified context.
43     *
44     * @param string $context The routing context ('public', 'admin', 'admin-api', 'api')
45     * @param bool $attributesOnly Deprecated parameter kept for backward compatibility (always true now)
46     * @return RouteCollection The route collection loaded from controller attributes
47     */
48    public function build(string $context, bool $attributesOnly = true): RouteCollection
49    {
50        // Load routes from controller attributes
51        return $this->loadAttributeRoutes($context);
52    }
53
54    /**
55     * Load routes from controller attributes.
56     *
57     * @param string $context The routing context
58     * @return RouteCollection The attribute-based routes
59     */
60    private function loadAttributeRoutes(string $context): RouteCollection
61    {
62        $controllerDirs = $this->getControllerDirectories($context);
63        $routes = new RouteCollection();
64
65        foreach ($controllerDirs as $dir) {
66            if (!is_dir($dir)) {
67                continue;
68            }
69
70            $dirRoutes = $this->attributeLoader->load($dir, $context);
71            $routes->addCollection($dirRoutes);
72        }
73
74        return $routes;
75    }
76
77    /**
78     * Get controller directories for the specified context.
79     *
80     * @param string $context The routing context
81     * @return array<string> Array of controller directory paths
82     */
83    private function getControllerDirectories(string $context): array
84    {
85        $baseDir = PMF_SRC_DIR . '/phpMyFAQ/Controller';
86
87        return match ($context) {
88            'public' => [
89                $baseDir . '/Frontend',
90            ],
91            'admin' => [
92                $baseDir . '/Administration',
93            ],
94            'admin-api' => [
95                $baseDir . '/Administration/Api',
96            ],
97            'api' => [
98                $baseDir . '/Api',
99                $baseDir . '/Frontend/Api',
100            ],
101            default => [$baseDir],
102        };
103    }
104}