Perf: Status-Counts der PM-Uebersicht via UNION statt OR-GROUP-BY
Die nach Status gruppierte Zaehlung lief ueber accessibleBy() (user_id OR company_id IN sub). Der OR-Ausdruck zwingt MySQL zu Full-Scans (EXPLAIN: companies type=ALL, press_releases Full-Index-Scan ueber 124k Zeilen) und war mit ~500-950ms der groesste Einzelposten der Seite. Neue PressRelease::accessibleStatusCountsFor(User, ?Closure): kombiniert Autor- und Firmen-Zweig als indexfreundliche UNION-Subquery (per PK dedupliziert) und gruppiert darueber. Im Benchmark ~2,5x schneller bei identischer Verteilung. Der Firmen-Filter der Uebersicht wird als geteilte Closure auf Counts UND Liste angewandt (garantiert deckungsgleiche Filterung). Gesamtmessung /admin/me/press-releases (User 14016, 26.772 Firmen / 20.830 PMs): Query-Pfad ~2850ms -> ~740ms warm; hydratisierte Company-Models 24. Tests: accessibleStatusCountsFor zaehlt Autor+Firma je einmal (UNION-Dedup) und wendet Zusatz-Constraints auf beide Zweige an. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6a406bf577
commit
7d6b97f3fb
3 changed files with 99 additions and 14 deletions
|
|
@ -111,26 +111,32 @@ new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class
|
|||
public function with(): array
|
||||
{
|
||||
$context = app(CustomerCompanyContext::class);
|
||||
$selectedCompanyId = $context->selectedCompanyId(auth()->user());
|
||||
$user = auth()->user();
|
||||
$selectedCompanyId = $context->selectedCompanyId($user);
|
||||
|
||||
// Firmen-Scope: eigene PMs (Autor) ODER PMs der zugeordneten Firmen.
|
||||
$base = PressRelease::accessibleBy(auth()->user())
|
||||
->when($selectedCompanyId !== null, fn($q) => $q->where('company_id', $selectedCompanyId))
|
||||
->when($selectedCompanyId === null && $this->companyFilter === 'assigned', fn($q) => $q->whereNotNull('company_id'))
|
||||
->when($selectedCompanyId === null && $this->companyFilter === 'unassigned', fn($q) => $q->whereNull('company_id'));
|
||||
// Firmen-Scope der Übersicht: auf eine gewählte Firma einschränken bzw.
|
||||
// (im globalen Kontext) auf zugeordnete/unzugeordnete PMs. Wird sowohl
|
||||
// auf die Status-Counts als auch die Liste angewandt, damit beide
|
||||
// deckungsgleich filtern.
|
||||
$applyCompanyFilter = function ($query) use ($selectedCompanyId): void {
|
||||
$query->when($selectedCompanyId !== null, fn($q) => $q->where('company_id', $selectedCompanyId))
|
||||
->when($selectedCompanyId === null && $this->companyFilter === 'assigned', fn($q) => $q->whereNotNull('company_id'))
|
||||
->when($selectedCompanyId === null && $this->companyFilter === 'unassigned', fn($q) => $q->whereNull('company_id'));
|
||||
};
|
||||
|
||||
$statusCountsRaw = (clone $base)->toBase()->selectRaw('status, COUNT(*) as aggregate')->groupBy('status')->pluck('aggregate', 'status');
|
||||
$statusCountsRaw = PressRelease::accessibleStatusCountsFor($user, $applyCompanyFilter);
|
||||
|
||||
$statusCounts = [
|
||||
'all' => (int) $statusCountsRaw->sum(),
|
||||
PressReleaseStatus::Published->value => (int) ($statusCountsRaw[PressReleaseStatus::Published->value] ?? 0),
|
||||
PressReleaseStatus::Draft->value => (int) ($statusCountsRaw[PressReleaseStatus::Draft->value] ?? 0),
|
||||
PressReleaseStatus::Review->value => (int) ($statusCountsRaw[PressReleaseStatus::Review->value] ?? 0),
|
||||
PressReleaseStatus::Rejected->value => (int) ($statusCountsRaw[PressReleaseStatus::Rejected->value] ?? 0),
|
||||
PressReleaseStatus::Archived->value => (int) ($statusCountsRaw[PressReleaseStatus::Archived->value] ?? 0),
|
||||
'all' => array_sum($statusCountsRaw),
|
||||
PressReleaseStatus::Published->value => $statusCountsRaw[PressReleaseStatus::Published->value] ?? 0,
|
||||
PressReleaseStatus::Draft->value => $statusCountsRaw[PressReleaseStatus::Draft->value] ?? 0,
|
||||
PressReleaseStatus::Review->value => $statusCountsRaw[PressReleaseStatus::Review->value] ?? 0,
|
||||
PressReleaseStatus::Rejected->value => $statusCountsRaw[PressReleaseStatus::Rejected->value] ?? 0,
|
||||
PressReleaseStatus::Archived->value => $statusCountsRaw[PressReleaseStatus::Archived->value] ?? 0,
|
||||
];
|
||||
|
||||
$prs = (clone $base)
|
||||
$prs = PressRelease::accessibleBy($user)
|
||||
->tap($applyCompanyFilter)
|
||||
->with('company:id,name')
|
||||
->when(filled($this->search), function ($q): void {
|
||||
$term = $this->search;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue