WS-1: Canonical- & OpenGraph-Meta zentral im Web-Layout
Ergänzt web-master.blade.php um <link rel="canonical">, OpenGraph- und Twitter-Card-Tags. Self-Canonical-Default wird portal-getrennt aus der Portal-URL ($domainUrl) + Request-Pfad gebaut – bewusst nicht aus url()->current(), da URL::forceRootUrl sonst alle Portale fälschlich auf pressekonto.test kanonisieren würde. Seiten können canonical/meta_description/ og_type/og_image per @section überschreiben; release-detail als article ausgezeichnet. Erfüllt die Canonical-Hygiene aus dem Duplicate-Content-Update §5. Tests: tests/Feature/Web/CanonicalMetaTest.php (inkl. Cross-Portal-Negativtest). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
ec4aa1dc3d
commit
c804f3bfc3
3 changed files with 63 additions and 2 deletions
|
|
@ -5,9 +5,38 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
<meta name="description" content="@yield('meta_description', $domainName ?? config('app.name', 'Laravel'))">
|
|
||||||
|
|
||||||
<title>@yield('title', $domainName ?? config('app.name', 'Laravel'))</title>
|
@php
|
||||||
|
$siteName = $domainName ?? config('app.name', 'Laravel');
|
||||||
|
$pageTitle = trim($__env->yieldContent('title')) ?: $siteName;
|
||||||
|
$pageDescription = trim($__env->yieldContent('meta_description')) ?: $siteName;
|
||||||
|
// Self-Canonical als Default aus der Portal-URL + Request-Pfad (ohne Query):
|
||||||
|
// pro Portal/Domain getrennt und unabhängig von URL::forceRootUrl – verhindert Cross-Portal-Duplicate.
|
||||||
|
$portalBase = rtrim($domainUrl ?? config('app.url'), '/');
|
||||||
|
$canonicalUrl = trim($__env->yieldContent('canonical')) ?: ($portalBase.request()->getPathInfo());
|
||||||
|
$ogType = trim($__env->yieldContent('og_type')) ?: 'website';
|
||||||
|
$ogImage = trim($__env->yieldContent('og_image'));
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<meta name="description" content="{{ $pageDescription }}">
|
||||||
|
|
||||||
|
<title>{{ $pageTitle }}</title>
|
||||||
|
|
||||||
|
{{-- Kanonische URL --}}
|
||||||
|
<link rel="canonical" href="{{ $canonicalUrl }}">
|
||||||
|
|
||||||
|
{{-- OpenGraph / Social --}}
|
||||||
|
<meta property="og:type" content="{{ $ogType }}">
|
||||||
|
<meta property="og:site_name" content="{{ $siteName }}">
|
||||||
|
<meta property="og:title" content="{{ $pageTitle }}">
|
||||||
|
<meta property="og:description" content="{{ $pageDescription }}">
|
||||||
|
<meta property="og:url" content="{{ $canonicalUrl }}">
|
||||||
|
@if ($ogImage)
|
||||||
|
<meta property="og:image" content="{{ $ogImage }}">
|
||||||
|
@endif
|
||||||
|
<meta name="twitter:card" content="{{ $ogImage ? 'summary_large_image' : 'summary' }}">
|
||||||
|
<meta name="twitter:title" content="{{ $pageTitle }}">
|
||||||
|
<meta name="twitter:description" content="{{ $pageDescription }}">
|
||||||
|
|
||||||
<!-- Domain-spezifisches Favicon -->
|
<!-- Domain-spezifisches Favicon -->
|
||||||
<link rel="icon" href="{{ asset(\App\Helpers\ThemeHelper::getFaviconPath()) }}">
|
<link rel="icon" href="{{ asset(\App\Helpers\ThemeHelper::getFaviconPath()) }}">
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
@extends('web.layouts.web-master')
|
@extends('web.layouts.web-master')
|
||||||
|
|
||||||
@section('title', 'KI-Revolution in Deutschland - Business Portal 24')
|
@section('title', 'KI-Revolution in Deutschland - Business Portal 24')
|
||||||
|
@section('meta_description', 'Neue Studie zeigt massive Investitionen deutscher Unternehmen in künstliche Intelligenz für 2025.')
|
||||||
|
@section('og_type', 'article')
|
||||||
|
@section('canonical', route('release.detail', ['slug' => $releaseSlug]))
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
|
|
|
||||||
29
tests/Feature/Web/CanonicalMetaTest.php
Normal file
29
tests/Feature/Web/CanonicalMetaTest.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
test('the release detail page emits an article canonical from its slug', function () {
|
||||||
|
/** @var TestCase $this */
|
||||||
|
$this->get('https://businessportal24.test/release/ki-revolution')
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSee('<link rel="canonical" href="', false)
|
||||||
|
->assertSee('/release/ki-revolution', false)
|
||||||
|
->assertSee('<meta property="og:type" content="article">', false)
|
||||||
|
->assertSee('<meta property="og:url" content="', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('a portal homepage emits a self-referencing canonical for its own domain', function () {
|
||||||
|
/** @var TestCase $this */
|
||||||
|
$this->get('https://businessportal24.test/')
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSee('<link rel="canonical" href="https://businessportal24.test', false)
|
||||||
|
->assertSee('<meta property="og:site_name" content="', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('portals stay self-canonical on their own host, never cross-portal', function () {
|
||||||
|
/** @var TestCase $this */
|
||||||
|
$this->get('https://presseecho.test/')
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSee('<link rel="canonical" href="https://presseecho.test', false)
|
||||||
|
->assertDontSee('businessportal24.test', false);
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue