mirror of
https://github.com/flarum/framework.git
synced 2025-03-28 02:05:15 +08:00
Refactor sticky order clause into a subquery
Based on some limited testing, using a subquery seems to outperform a join in this case (the join was invoking a temporary table, which is always a bad sign). This also adds logic to fix a bug where sticky discussions would remain at the top even when marked as read using the "mark all as read" button. I thought we had an open issue for this somewhere, but I can't seem to find one. ref #988 #1003
This commit is contained in:
parent
86cc5fe9c8
commit
7ff57e1ba4
@ -45,10 +45,17 @@ class PinStickiedDiscussionsToTop
|
||||
$search = $event->search;
|
||||
$query = $search->getQuery();
|
||||
|
||||
// TODO: ideally we might like to consider an event in core that is
|
||||
// fired before the sort criteria is applied to the query (ie.
|
||||
// immediately after gambits are applied) so that we can add the
|
||||
// following order logic to the start without using array_unshift.
|
||||
|
||||
if (! is_array($query->orders)) {
|
||||
$query->orders = [];
|
||||
}
|
||||
|
||||
// If we are viewing a specific tag, then pin all stickied
|
||||
// discussions to the top no matter what.
|
||||
foreach ($search->getActiveGambits() as $gambit) {
|
||||
if ($gambit instanceof TagGambit) {
|
||||
array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']);
|
||||
@ -57,21 +64,28 @@ class PinStickiedDiscussionsToTop
|
||||
}
|
||||
}
|
||||
|
||||
$query->leftJoin('users_discussions', function ($join) use ($search) {
|
||||
$join->on('users_discussions.discussion_id', '=', 'discussions.id')
|
||||
->where('discussions.is_sticky', '=', true)
|
||||
->where('users_discussions.user_id', '=', $search->getActor()->id);
|
||||
});
|
||||
|
||||
// TODO: Might be quicker to do a subquery in the order clause than a join?
|
||||
$grammar = $query->getGrammar();
|
||||
$readNumber = $grammar->wrap('users_discussions.read_number');
|
||||
$lastPostNumber = $grammar->wrap('discussions.last_post_number');
|
||||
// Otherwise, if we are viewing "all discussions" or similar, only
|
||||
// pin stickied discussions to the top if they are unread. To do
|
||||
// this we construct an order clause containing a subquery which
|
||||
// determines whether or not the discussion is unread.
|
||||
$subquery = $query->newQuery()
|
||||
->selectRaw(1)
|
||||
->from('users_discussions as sticky')
|
||||
->whereRaw('sticky.discussion_id = discussions.id')
|
||||
->where('sticky.user_id', '=', $search->getActor()->id)
|
||||
->where(function ($query) {
|
||||
$query->whereNull('sticky.read_number')
|
||||
->orWhereRaw('discussions.last_post_number > sticky.read_number');
|
||||
})
|
||||
->where('discussions.last_time', '>', $search->getActor()->read_time ?: 0);
|
||||
|
||||
array_unshift($query->orders, [
|
||||
'type' => 'raw',
|
||||
'sql' => "(is_sticky AND ($readNumber IS NULL OR $lastPostNumber > $readNumber)) desc"
|
||||
'sql' => "(is_sticky and exists ({$subquery->toSql()})) desc"
|
||||
]);
|
||||
|
||||
$orderBindings = $query->getRawBindings()['order'];
|
||||
$query->setBindings(array_merge($subquery->getBindings(), $orderBindings), 'order');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user