- 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>
45 lines
1.4 KiB
PHP
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();
|
|
}
|
|
}
|