301 fuer die /products-Gruppenordner auf /products (Entscheidung Harry 11.11.)

Die 11 Gruppenseiten des Produktbaums wurden am 10.11. im Backend zu
Ordnern (doktype 254) und antworteten seitdem 404 (Seobility-Crawl 3:
8 'technische Probleme'). Harry: Absicht -> Migration
folder_pages_redirects_20261111 (angewendet) legt 301 auf /products an
(mit und ohne Slash) und biegt aeltere Redirects, die auf einen dieser
Ordner zeigten, ebenfalls auf /products um. Inhalte auf den Ordnern
bleiben unangetastet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
orascheandClaude Fable 5.1 committed 2026-11-11 10:56:43 +01:00
1 parent 67ded6e502
commit 1b72e65a1d
1 file changed
+71
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
/**
* The product group pages (two middle levels of the /products tree) were
* turned into folders by the editor on 2026-11-10 (decision confirmed by
* Harry 2026-11-11: intentional). Their former URLs answered 404 since
* then (Seobility crawl 3: 8 "technische Probleme"). Every folder under
* /products gets a 301 to /products; the content elements on the folders
* stay untouched.
*
* Dry run: php migrations/folder_pages_redirects_20261111.php
* Write: php migrations/folder_pages_redirects_20261111.php --apply
*/
error_reporting(E_ALL);
ini_set('display_errors', '1');
$apply = in_array('--apply', $argv, true);
$root = null;
$dir = __DIR__;
for ($i = 0; $i < 5; $i++) {
if (is_file($dir . '/config/system/settings.php')) { $root = $dir; break; }
$dir = dirname($dir);
}
if ($root === null) { exit("no project root\n"); }
$settings = require $root . '/config/system/settings.php';
$db = $settings['DB']['Connections']['Default'];
$pdo = new PDO(
sprintf('mysql:host=%s;port=%s;dbname=%s;charset=utf8mb4', $db['host'] ?? 'localhost', $db['port'] ?? 3306, $db['dbname'] ?? ''),
$db['user'] ?? '', $db['password'] ?? '',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
echo $apply ? "== APPLY ==\n" : "== DRY RUN (add --apply to write) ==\n";
$hasRedirect = $pdo->prepare("SELECT uid, target FROM sys_redirect WHERE deleted = 0 AND source_path = ? LIMIT 1");
$insertRedirect = $pdo->prepare(
"INSERT INTO sys_redirect (pid, createdon, updatedon, source_host, source_path, target, target_statuscode)
VALUES (0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '*', ?, ?, 301)"
);
$target = '/products';
$paths = [];
$n = 0;
foreach ($pdo->query("SELECT uid, slug, title, doktype FROM pages WHERE deleted = 0 AND hidden = 0 AND doktype = 254 AND slug LIKE '/products/%' ORDER BY slug") as $p) {
$ce = (int)$pdo->query("SELECT COUNT(*) FROM tt_content WHERE deleted = 0 AND hidden = 0 AND pid = {$p['uid']}")->fetchColumn();
foreach ([$p['slug'], $p['slug'] . '/'] as $source) {
$hasRedirect->execute([$source]);
$existing = $hasRedirect->fetch();
if ($existing) {
printf(" #%-4d %-55s (%2d CEs) %s: Redirect existiert -> %s\n", $p['uid'], $source, $ce, $p['title'], $existing['target']);
continue;
}
printf(" #%-4d %-55s (%2d CEs) %s -> %s\n", $p['uid'], $source, $ce, $p['title'], $target);
$n++;
if ($apply) { $insertRedirect->execute([$source, $target]); }
}
$paths[] = $p['slug'];
// Older redirects that point AT this folder (t3://page?uid=… or its slug)
// would now end in a 404 chain: send them to /products as well.
$st = $pdo->prepare("SELECT uid, source_path, target FROM sys_redirect WHERE deleted = 0 AND (target = ? OR target = ? OR target LIKE ?)");
$st->execute(['t3://page?uid=' . $p['uid'], $p['slug'], 't3://page?uid=' . $p['uid'] . '&%']);
foreach ($st->fetchAll() as $r) {
printf(" redirect #%d %s: Ziel %s -> %s\n", $r['uid'], $r['source_path'], $r['target'], $target);
if ($apply) { $pdo->prepare("UPDATE sys_redirect SET target = ?, updatedon = UNIX_TIMESTAMP() WHERE uid = ?")->execute([$target, (int)$r['uid']]); }
}
}
echo "\n$n Redirect(s) " . ($apply ? 'angelegt' : 'wuerden angelegt') . ".\n";
echo "SSR-Pfade:\n " . implode("\n ", $paths) . "\n";