diff --git a/extensions/sticky/src/Listener/PinStickiedDiscussionsToTop.php b/extensions/sticky/src/Listener/PinStickiedDiscussionsToTop.php index 74352c79e..4c284c36b 100755 --- a/extensions/sticky/src/Listener/PinStickiedDiscussionsToTop.php +++ b/extensions/sticky/src/Listener/PinStickiedDiscussionsToTop.php @@ -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'); } } }