Files
VITEC-website/packages/vitec/Classes/Middleware/SuccessStoryPathRewrite.php
khaccount eae40f4286 Success-story migration + new plugins + JSON/UX polish
- Success Stories: full migration from old site (48/48, audited),
  markets n:n, quotation content block, columns content block,
  pretty SEO detail URLs via SuccessStoryPathRewrite middleware,
  list/detail split (list page 4 / story page), detailUrl/backUrl
- New plugins: VITEC Locations (grid/list/map + RTE map text),
  VITEC Customer Logos (color/bw logic, only-show-selected),
  VITEC Card (one plugin for product/story/market/solution
  with reloading FlexForm + custom backend preview renderer)
- Eventlist: layout dropdown (list/grid/teaserbar) in settings
- Hero section CB: Images/Video tabs, background video + overlay
- Product JSON: full category rootline (parents), fixed missing
  ConnectionPool import (all-products crash), category tree map
- Backend preview CSS: container-query responsive (narrow columns)
- Docs: ISO architecture spec, root + extension READMEs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 09:53:33 +02:00

80 lines
2.9 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace Evomedien\Vitec\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Pretty detail URLs for Success Stories.
*
* Public URL: /success-stories/<slug> (old-site SEO URLs)
* Real page: /success-stories/story/<slug> (subpage holding ONLY the
* usecaseshow plugin)
*
* TYPO3's page router always resolves /success-stories/<slug> to the LIST
* page (longest page-slug prefix), so the detail subpage could never answer
* that URL. This middleware rewrites the request path internally — before
* page resolution — whenever <slug> matches an existing (visible) success
* story. Real subpages of /success-stories are unaffected: no story match,
* no rewrite. The browser URL never changes.
*
* Exception-safe: any failure leaves the request untouched.
*/
final class SuccessStoryPathRewrite implements MiddlewareInterface
{
private const LIST_PATH = '/success-stories';
private const DETAIL_SEGMENT = 'story';
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$debug = 'no-match';
try {
$path = $request->getUri()->getPath();
if (preg_match('#^' . self::LIST_PATH . '/([a-zA-Z0-9\-]+)/?$#', $path, $m) === 1
&& $m[1] !== self::DETAIL_SEGMENT
) {
if ($this->isStorySlug($m[1])) {
$uri = $request->getUri()->withPath(
self::LIST_PATH . '/' . self::DETAIL_SEGMENT . '/' . $m[1]
);
$request = $request->withUri($uri);
$debug = 'rewritten';
} else {
$debug = 'slug-not-found:' . $m[1];
}
}
} catch (\Throwable $e) {
$debug = 'exception:' . substr($e->getMessage(), 0, 60);
}
// TEMP-DIAG header (remove after verification)
return $handler->handle($request)->withHeader('X-Vitec-Story-Rewrite', $debug);
}
private function isStorySlug(string $slug): bool
{
$qb = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_vitec_domain_model_usecase');
$row = $qb
->select('uid')
->from('tx_vitec_domain_model_usecase')
->where(
$qb->expr()->eq('slug', $qb->createNamedParameter($slug)),
$qb->expr()->eq('deleted', 0),
$qb->expr()->eq('hidden', 0)
)
->setMaxResults(1)
->executeQuery()
->fetchAssociative();
return $row !== false;
}
}