koch / koch-staticroute
Package info
bitbucket.org/kochkommunikation/koch-staticroute/
Type:typo3-cms-extension
pkg:composer/koch/koch-staticroute
Requires
- symfony/routing: ^6.4 || ^7.0
- typo3/cms-core: ^13.4|^12.4
This package is auto-updated.
Last update: 2026-04-23 08:27:53 UTC
README
Lightweight TYPO3 extension for registering static route controllers that bypass the page tree. Routes are resolved via a PSR-15 frontend middleware using Symfony Routing. No TypoScript, no page record, no Extbase.
Requirements
- PHP >= 8.2
- TYPO3 v13.4+
symfony/routing^7.0
Installation
composer require koch/koch-staticroute
Architecture
A PSR-15 middleware intercepts requests prefixed with /route-static after site-resolver and before page-resolver. It matches the path against a Symfony RouteCollection built from all services implementing StaticControllerInterface. On match, route parameters are merged into $request->getQueryParams() and the controller's handle() method is called. On no match, the request falls through to TYPO3's page resolver.
Writing a Controller
Implement StaticControllerInterface and add two attributes:
#[AsTaggedItem(index: '...')]— unique route name, used as the service locator key#[StaticRoute(...)]— route pattern, requirements, defaults, allowed HTTP methods
<?php
declare(strict_types=1);
namespace Vendor\MyExt\Controller;
use Koch\StaticRoute\Attribute\StaticRoute;
use Koch\StaticRoute\Controller\StaticControllerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;
use TYPO3\CMS\Core\Http\JsonResponse;
#[AsTaggedItem(index: 'webhook-stripe')]
#[StaticRoute(
pattern: '/webhook/stripe/{action}',
requirements: ['action' => 'charge|refund|dispute'],
defaults: ['action' => 'charge'],
methods: ['POST'],
)]
final class StripeWebhookController implements StaticControllerInterface
{
public function handle(ServerRequestInterface $request): ResponseInterface
{
$action = $request->getQueryParams()['action'] ?? 'charge';
return new JsonResponse(['status' => 'ok', 'action' => $action]);
}
}
No manual service registration required — autoconfigure: true in your Services.yaml is sufficient.
Route Parameters
Matched route parameters are merged into query params. Given the pattern /webhook/stripe/{action}, a request to POST /route-static/webhook/stripe/refund results in:
$request->getQueryParams()['action']; // 'refund'
Existing query parameters are preserved. Route parameters take precedence on key collision.
Generating URIs
Inject StaticRouteUriBuilder to generate URLs for registered routes:
use Koch\StaticRoute\Service\StaticRouteUriBuilder;
final readonly class MyService
{
public function __construct(
private StaticRouteUriBuilder $uriBuilder,
) {}
public function getWebhookUrl(): string
{
return $this->uriBuilder->buildUri('webhook-stripe', ['action' => 'refund']);
// → /route-static/webhook/stripe/refund
}
}
Pass a ServerRequestInterface and absolute: true for fully qualified URLs:
$this->uriBuilder->buildUri('webhook-stripe', ['action' => 'refund'], $request, absolute: true);
// → https://example.com/route-static/webhook/stripe/refund
Attribute Reference
#[AsTaggedItem(index: string)]
Symfony DI attribute. The index value is the unique route name used to identify the controller in the service locator and for URI generation. Duplicate indexes within the same tag are rejected at container compile time.
#[StaticRoute]
| Parameter | Type | Default | Description |
|---|---|---|---|
pattern | string | — | Route pattern after /route-static, e.g. /api/{id} |
requirements | array<string,string> | [] | Symfony route requirements, e.g. ['id' => '\d+'] |
defaults | array<string,mixed> | [] | Default values for optional parameters |
methods | list<string> | [] | Allowed HTTP methods, empty = all |
File Structure
Classes/
Attribute/StaticRoute.php
Controller/StaticControllerInterface.php
Frontend/Middleware/StaticRouteResolveMiddleware.php
Routing/StaticRouteCollectionFactory.php
Service/StaticRouteUriBuilder.php
Configuration/
RequestMiddlewares.php
Services.yaml
composer.json