presseportale/app/Http/Controllers/Web/ReleaseShareTrackingController.php
Kevin Adametz 8f5970436b PM-Tracking: Ereignis-Log fuer Aufrufe + Shares (press_release_events)
- press_release_events (type view/share, channel, ohne Personenbezug):
  Model/Enums/Factory, Cascade-Delete an der PM
- Detailaufruf loggt View-Event zusaetzlich zum Legacy-hits-Zaehler
  (bleibt bewusst einfacher Page-Request-Zaehler, User-Vorgabe 10.07.)
- POST press-release/{slug}/share (throttle 30/min, nur archiv-sichtbare
  PMs des Portals) loggt Teilen-Klicks je Kanal (LinkedIn/X/E-Mail/Copy)
- Kundenpanel-PM-Seite: neue Kachel Geteilt mit Kanal-Aufschluesselung
- lang: web.detail.views (de/en)
- Tests: Web/PressReleaseTrackingTest (7)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:56:42 +00:00

45 lines
1.4 KiB
PHP

<?php
namespace App\Http\Controllers\Web;
use App\Enums\PressReleaseEventType;
use App\Enums\PressReleaseShareChannel;
use App\Http\Controllers\Controller;
use App\Models\PressRelease;
use App\Scopes\PortalScope;
use App\Support\WebPortalContext;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Validation\Rule;
/**
* Nimmt Share-Klicks der öffentlichen PM-Detailseite entgegen und loggt
* sie als Ereignis (Kanal: LinkedIn/X/E-Mail/Link kopiert) — ohne
* Personenbezug. Nur für im Archiv sichtbare PMs des aufrufenden Portals.
*/
class ReleaseShareTrackingController extends Controller
{
public function __invoke(Request $request, string $edition, string $slug): Response
{
$validated = $request->validate([
'channel' => ['required', Rule::enum(PressReleaseShareChannel::class)],
]);
$context = WebPortalContext::fromRequest();
$release = PressRelease::query()
->withoutGlobalScope(PortalScope::class)
->forPortalValues($context->portalValues)
->forLanguage($context->language)
->visibleInArchive()
->where('slug', $slug)
->firstOrFail();
$release->events()->create([
'type' => PressReleaseEventType::Share,
'channel' => $validated['channel'],
]);
return response()->noContent();
}
}