Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions app/Models/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ protected function hierarchy(): Attribute
CommentHierarchy
SQL;

return new Attribute(
$attribute = new Attribute(
get: fn ($value) => Arr::first(
DB::select($query, ['id' => $this->id])
)
)->shouldCache();
);

return $attribute->shouldCache();
}
}
6 changes: 4 additions & 2 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ public function notifyNewComment(NewComment $instance): void

protected function gravatarUrl(): Attribute
{
return new Attribute(
$attribute = new Attribute(
get: fn ($value) => get_gravatar(email: $this->email, size: 512)
)->shouldCache();
);

return $attribute->shouldCache();
}

public function passkeys(): MorphMany
Expand Down
8 changes: 6 additions & 2 deletions resources/views/components/layouts/⚑header.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public function logout(Logout $logout): void
</script>
@endscript

@php
$hasUnreadNotifications = auth()->check() && auth()->user()->unreadNotifications()->exists();
@endphp

<header
class="z-20 mb-6"
id="header"
Expand Down Expand Up @@ -158,7 +162,7 @@ class="flex justify-center items-center text-xl rounded-lg transition duration-1
<x-icons.bell class="size-6" />
</a>

@if (auth()->user()->unreadNotifications->count() > 0)
@if ($hasUnreadNotifications)
<span class="flex absolute top-2 right-2 -mt-1 -mr-1 w-3 h-3">
<span class="inline-flex absolute w-full h-full bg-red-400 rounded-full opacity-75 animate-ping"></span>
<span class="inline-flex relative w-3 h-3 bg-red-500 rounded-full"></span>
Expand Down Expand Up @@ -315,7 +319,7 @@ class="rounded-full text-zinc-500 dark:text-zinc-400 dark:hover:text-zinc-50 hov
<x-icons.bell class="w-5" />
</a>

@if (auth()->user()->unreadNotifications->count() > 0)
@if ($hasUnreadNotifications)
<span class="flex absolute top-2 right-2 -mt-1 -mr-1 w-3 h-3">
<span
class="inline-flex absolute w-full h-full bg-red-400 rounded-full opacity-75 animate-ping"></span>
Expand Down
58 changes: 58 additions & 0 deletions tests/Feature/HeaderPerformanceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

use App\Models\User;
use Illuminate\Notifications\DatabaseNotification;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Livewire\Livewire;

test('header performance benchmark', function () {
// Create a user
$user = User::factory()->create();

// Create 50 unread notifications
for ($i = 0; $i < 50; $i++) {
DatabaseNotification::create([
'id' => Str::uuid()->toString(),
'type' => 'App\Notifications\TestNotification',
'notifiable_type' => User::class,
'notifiable_id' => $user->id,
'data' => ['message' => 'test'],
'read_at' => null,
'created_at' => now(),
'updated_at' => now(),
]);
}

$this->actingAs($user);

DB::enableQueryLog();
DB::flushQueryLog();

$start = microtime(true);

Livewire::test('layouts.header');

$end = microtime(true);
$duration = $end - $start;

$queries = DB::getQueryLog();
$queryCount = count($queries);

$hasFullSelect = collect($queries)->contains(function ($query) {
// SQLite uses double quotes, MySQL uses backticks.
// We just check for "select * from" and "notifications"
return str_contains(strtolower($query['query']), 'select * from')
&& str_contains(strtolower($query['query']), 'notifications');
});

$hasExists = collect($queries)->contains(function ($query) {
return str_contains(strtolower($query['query']), 'exists');
});

// Verify results
expect($queryCount)->toBeLessThanOrEqual(3);
expect($hasExists)->toBeTrue();
// We can't strict check "Has Full Select" because exists() contains the string.
// But we can check that we used the optimized path.
});
Loading