perf: fix N+1 queries; fix ComposeController lint and test namespace

Performance:
- TrendingHashtagService: batch-load hashtags with whereIn/keyBy instead
  of Hashtag::find() per trending row.
- DirectMessageController: eager-load status.media and read the in-memory
  collection instead of firstMedia() issuing a query per DM message.
- GroupsSearchController: batch Profile/Follower/GroupInvitation lookups
  with whereIn instead of per-invitee queries.

Lint/tests:
- ComposeController: whitespace formatting (Pint).
- ComposeControllerTest: correct App\User to App\Models\User, fixing the
  larastan class.notFound error and import ordering.

Full suite: 547 passed. Pint and PHPStan clean.
pull/6976/head
Your Name 3 weeks ago
parent d9817840c1
commit b52c3d7659

@ -396,7 +396,7 @@ class ComposeController extends Controller
];
});
});
$wildcard = config('database.default') === 'pgsql' ? 'ilike' : 'like';
$q = '%'.$raw.'%';

@ -307,13 +307,15 @@ class DirectMessageController extends Controller
$messages = $res->filter(function ($message) {
return $message && $message->status;
})->map(function ($message) use ($uid) {
$firstMedia = $message->status->media->sortBy('order')->first();
return [
'id' => (string) $message->id,
'hidden' => (bool) $message->is_hidden,
'isAuthor' => $uid == $message->from_id,
'type' => $message->type,
'text' => $message->status->caption,
'media' => $message->status->firstMedia() ? $message->status->firstMedia()->url() : null,
'media' => $firstMedia ? $firstMedia->url() : null,
'carousel' => MediaService::get($message->status_id),
'created_at' => $message->created_at->format('c'),
'timeAgo' => $message->created_at->diffForHumans(null, null, true),

@ -42,24 +42,27 @@ class GroupsSearchController extends Controller
'Invite limit reached'
);
$profiles = collect($uid)
->map(function ($u) {
return Profile::find($u);
})
->filter(function ($u) use ($pid) {
return $u &&
$u->id != $pid &&
isset($u->id) &&
Follower::whereFollowingId($pid)
->whereProfileId($u->id)
->exists();
})
->filter(function ($u) use ($group, $pid) {
return GroupInvitation::whereGroupId($group->id)
->whereFromProfileId($pid)
->whereToProfileId($u->id)
->exists() == false;
})
$candidateIds = collect($uid)
->filter(fn ($u) => $u != $pid)
->unique()
->values();
$profiles = Profile::whereIn('id', $candidateIds)->get();
$followedIds = Follower::whereFollowingId($pid)
->whereIn('profile_id', $profiles->pluck('id'))
->pluck('profile_id')
->all();
$alreadyInvitedIds = GroupInvitation::whereGroupId($group->id)
->whereFromProfileId($pid)
->whereIn('to_profile_id', $profiles->pluck('id'))
->pluck('to_profile_id')
->all();
$profiles
->filter(fn ($u) => in_array($u->id, $followedIds))
->filter(fn ($u) => ! in_array($u->id, $alreadyInvitedIds))
->each(function ($u) use ($gid, $pid) {
$gi = new GroupInvitation;
$gi->group_id = $gid;

@ -63,27 +63,32 @@ class TrendingHashtagService
$skipIds = array_merge(self::getBannedHashtags(), self::getNonTrendingHashtags(), self::getNsfwHashtags());
return Cache::remember(self::CACHE_KEY, config('trending.hashtags.ttl'), function () use ($minId, $skipIds) {
return StatusHashtag::select('hashtag_id', DB::raw('count(*) as total'))
$trending = StatusHashtag::select('hashtag_id', DB::raw('count(*) as total'))
->whereNotIn('hashtag_id', $skipIds)
->where('id', '>', $minId)
->groupBy('hashtag_id')
->orderBy('total', 'desc')
->take(config('trending.hashtags.limit'))
->get();
$hashtags = Hashtag::whereIn('id', $trending->pluck('hashtag_id'))
->get()
->map(function ($h) {
$hashtag = Hashtag::find($h->hashtag_id);
if (! $hashtag) {
return;
}
return [
'id' => $h->hashtag_id,
'total' => $h->total,
'name' => '#'.$hashtag->name,
'hashtag' => $hashtag->name,
'url' => $hashtag->url(),
];
})
->keyBy('id');
return $trending->map(function ($h) use ($hashtags) {
$hashtag = $hashtags->get($h->hashtag_id);
if (! $hashtag) {
return;
}
return [
'id' => $h->hashtag_id,
'total' => $h->total,
'name' => '#'.$hashtag->name,
'hashtag' => $hashtag->name,
'url' => $hashtag->url(),
];
})
->filter()
->values();
});

@ -2,10 +2,10 @@
namespace Tests\Feature;
use Illuminate\Support\Facades\DB;
use App\Models\User;
use Database\Factories\ProfileFactory;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
@ -71,4 +71,4 @@ class ComposeControllerTest extends TestCase
'country' => 'Philippines',
]);
}
}
}

Loading…
Cancel
Save