1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Models\Traits;
7
8use App\Libraries\DbCursorHelper;
9
10trait WithDbCursorHelper
11{
12 public static function makeDbCursorHelper(?string $sort = null)
13 {
14 return new DbCursorHelper(static::SORTS, static::DEFAULT_SORT, $sort);
15 }
16
17 private static function cursorSortExecOrder($query, array $sort)
18 {
19 foreach ($sort as $i => $sortItem) {
20 $orderMethod = $i === 0 ? 'reorderBy' : 'orderBy';
21 $query->$orderMethod($sortItem['column'], $sortItem['order']);
22 }
23
24 return $query;
25 }
26
27 private static function cursorSortExecWhere($query, ?array $preparedCursor)
28 {
29 if (empty($preparedCursor)) {
30 return $query;
31 }
32
33 $current = array_shift($preparedCursor);
34
35 $dir = $current['order'] === 'DESC' ? '<' : '>';
36
37 if (count($preparedCursor) === 0) {
38 $query->where($current['column'], $dir, $current['value']);
39 } else {
40 $query->where($current['column'], "{$dir}=", $current['value'])
41 ->where(function ($q) use ($current, $dir, $preparedCursor) {
42 return $q->where($current['column'], $dir, $current['value'])
43 ->orWhere(function ($qq) use ($preparedCursor) {
44 return static::cursorSortExecWhere($qq, $preparedCursor);
45 });
46 });
47 }
48
49 return $query;
50 }
51
52 /**
53 * Builds a cursor-based sort query.
54 *
55 * @param \Illuminate\Database\Eloquent\Builder $query Input query to be extended.
56 * @param string|DbCursorHelper $sortOrCursorHelper Either sort name to create DbCursorHelper or existing DbCursorHelper.
57 * @param array|static $cursorOrStatic Either an input cursor array or object instance to generate cursor array from.
58 *
59 * @return \Illuminate\Database\Eloquent\Builder
60 */
61 public function scopeCursorSort($query, $sortOrCursorHelper, $cursorOrStatic)
62 {
63 $cursorHelper = $sortOrCursorHelper instanceof DbCursorHelper
64 ? $sortOrCursorHelper
65 : static::makeDbCursorHelper(get_string($sortOrCursorHelper));
66
67 $query = static::cursorSortExecOrder($query, $cursorHelper->getSort());
68
69 $cursor = $cursorOrStatic instanceof static
70 ? $cursorHelper->itemToCursor($cursorOrStatic)
71 : $cursorOrStatic;
72
73 return static::cursorSortExecWhere($query, $cursorHelper->prepare($cursor));
74 }
75}