companies: Links aus Firmennamen normalisieren — API-Eingang und Bestandsbefehl

38 oeffentliche Firmen tragen eine URL im Namen ("Firma - https://..."),
laufend ueber die API. Nicht ablehnen (traefe die Meldung), sondern
normalisieren: URL raus, Rest bleibt Name, URL ins leere Website-Feld;
besteht der Name nur aus der URL, wird der Host zum Namen. Am API-Eingang
vor der Zuordnung; im Bestand per companies:strip-links-from-names mit
Dry-Run, Slugs bleiben.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-17 09:07:49 +00:00
parent 5d8e379e85
commit 8ea5e52c84
5 changed files with 310 additions and 3 deletions

View file

@ -0,0 +1,80 @@
<?php
namespace App\Services\Company;
use Illuminate\Support\Str;
/**
* Zieht Links aus einem Firmennamen (17.09.2026).
*
* Über die API kommen Namen wie „Steinreinigung - https://www.bgn.at/…"
* oder nur „www.beispiel.de" — SEO-Gewohnheit der Einlieferer, auf der
* Newsroom-Seite aber ein Link im Namen. Ablehnen träfe die Meldung des
* Einlieferers, deshalb normalisieren: URL raus, Rest bleibt Name, die
* URL wandert ins Website-Feld, falls das leer ist. Bleibt vom Namen nichts
* übrig, wird der Host der URL zum Namen („ak-autos.de") — eine nackte
* Domain ist Marke, kein Link.
*/
class CompanyNameNormalizer
{
private const URL_PATTERN = '~(?:https?://|www\.)[^\s,;|()<>"\']+|\b[a-z0-9-]+(?:\.[a-z0-9-]+)*\.[a-z]{2,}/[^\s,;|()<>"\']*~iu';
/**
* @return array{name: string, website: ?string, removed: list<string>}
*/
public static function normalize(string $name, ?string $website = null): array
{
$removed = [];
$stripped = (string) preg_replace_callback(self::URL_PATTERN, static function (array $match) use (&$removed): string {
$removed[] = rtrim($match[0], '.,:;');
return ' ';
}, $name);
$clean = self::tidy($stripped);
if ($clean === '' && $removed !== []) {
$clean = self::host($removed[0]) ?? Str::squish($name);
}
if (blank($website) && $removed !== []) {
$website = $removed[0];
}
return [
'name' => $clean !== '' ? $clean : Str::squish($name),
'website' => filled($website) ? trim((string) $website) : null,
'removed' => $removed,
];
}
public static function containsUrl(string $name): bool
{
return preg_match(self::URL_PATTERN, $name) === 1;
}
/**
* Verwaiste Trenner nach dem Herausziehen der URL aufräumen:
* „Firma - “, „ - Firma", „Firma ( )", „Firma /".
*/
private static function tidy(string $value): string
{
$value = (string) preg_replace('~\(\s*\)|\[\s*\]~u', ' ', $value);
$value = Str::squish($value);
$value = (string) preg_replace('~(?:\s*[-–—|/,:]+\s*){2,}~u', ' - ', $value);
return trim($value, " \t\n\r\0\x0B-–—|/,:");
}
private static function host(string $url): ?string
{
$candidate = Str::startsWith(mb_strtolower($url), ['http://', 'https://']) ? $url : 'https://'.$url;
$host = parse_url($candidate, PHP_URL_HOST);
if (! is_string($host) || $host === '') {
return null;
}
return (string) preg_replace('~^www\.~i', '', mb_strtolower($host));
}
}