3 namespace BookStack\Api;
5 use Illuminate\Database\Eloquent\Builder;
6 use Illuminate\Database\Eloquent\Collection;
7 use Illuminate\Http\Request;
9 class ListingResponseBuilder
15 protected $filterOperators = [
26 * ListingResponseBuilder constructor.
28 public function __construct(Builder $query, Request $request, array $fields)
30 $this->query = $query;
31 $this->request = $request;
32 $this->fields = $fields;
36 * Get the response from this builder.
38 public function toResponse()
40 $filteredQuery = $this->filterQuery($this->query);
42 $total = $filteredQuery->count();
43 $data = $this->fetchData($filteredQuery);
45 return response()->json([
52 * Fetch the data to return in the response.
54 protected function fetchData(Builder $query): Collection
56 $query = $this->countAndOffsetQuery($query);
57 $query = $this->sortQuery($query);
59 return $query->get($this->fields);
63 * Apply any filtering operations found in the request.
65 protected function filterQuery(Builder $query): Builder
67 $query = clone $query;
68 $requestFilters = $this->request->get('filter', []);
69 if (!is_array($requestFilters)) {
73 $queryFilters = collect($requestFilters)->map(function ($value, $key) {
74 return $this->requestFilterToQueryFilter($key, $value);
75 })->filter(function ($value) {
76 return !is_null($value);
77 })->values()->toArray();
79 return $query->where($queryFilters);
83 * Convert a request filter query key/value pair into a [field, op, value] where condition.
85 protected function requestFilterToQueryFilter($fieldKey, $value): ?array
87 $splitKey = explode(':', $fieldKey);
88 $field = $splitKey[0];
89 $filterOperator = $splitKey[1] ?? 'eq';
91 if (!in_array($field, $this->fields)) {
95 if (!in_array($filterOperator, array_keys($this->filterOperators))) {
96 $filterOperator = 'eq';
99 $queryOperator = $this->filterOperators[$filterOperator];
101 return [$field, $queryOperator, $value];
105 * Apply sorting operations to the query from given parameters
106 * otherwise falling back to the first given field, ascending.
108 protected function sortQuery(Builder $query): Builder
110 $query = clone $query;
111 $defaultSortName = $this->fields[0];
114 $sort = $this->request->get('sort', '');
115 if (strpos($sort, '-') === 0) {
119 $sortName = ltrim($sort, '+- ');
120 if (!in_array($sortName, $this->fields)) {
121 $sortName = $defaultSortName;
124 return $query->orderBy($sortName, $direction);
128 * Apply count and offset for paging, based on params from the request while falling
129 * back to system defined default, taking the max limit into account.
131 protected function countAndOffsetQuery(Builder $query): Builder
133 $query = clone $query;
134 $offset = max(0, $this->request->get('offset', 0));
135 $maxCount = config('api.max_item_count');
136 $count = $this->request->get('count', config('api.default_item_count'));
137 $count = max(min($maxCount, $count), 1);
139 return $query->skip($offset)->take($count);