3 namespace BookStack\Api;
5 use Illuminate\Database\Eloquent\Builder;
6 use Illuminate\Database\Eloquent\Collection;
7 use Illuminate\Http\Request;
9 class ListingResponseBuilder
14 protected $hiddenFields;
16 protected $filterOperators = [
27 * ListingResponseBuilder constructor.
29 public function __construct(Builder $query, Request $request, array $fields, array $hiddenFields )
31 $this->query = $query;
32 $this->request = $request;
33 $this->fields = $fields;
34 $this->hiddenFields = $hiddenFields;
38 * Get the response from this builder.
40 public function toResponse()
42 $filteredQuery = $this->filterQuery($this->query);
44 $total = $filteredQuery->count();
45 $data = $this->fetchData($filteredQuery);
46 $data = $data->makeVisible($this->hiddenFields);
48 return response()->json([
55 * Fetch the data to return in the response.
57 protected function fetchData(Builder $query): Collection
59 $query = $this->countAndOffsetQuery($query);
60 $query = $this->sortQuery($query);
62 return $query->get($this->fields);
66 * Apply any filtering operations found in the request.
68 protected function filterQuery(Builder $query): Builder
70 $query = clone $query;
71 $requestFilters = $this->request->get('filter', []);
72 if (!is_array($requestFilters)) {
76 $queryFilters = collect($requestFilters)->map(function ($value, $key) {
77 return $this->requestFilterToQueryFilter($key, $value);
78 })->filter(function ($value) {
79 return !is_null($value);
80 })->values()->toArray();
82 return $query->where($queryFilters);
86 * Convert a request filter query key/value pair into a [field, op, value] where condition.
88 protected function requestFilterToQueryFilter($fieldKey, $value): ?array
90 $splitKey = explode(':', $fieldKey);
91 $field = $splitKey[0];
92 $filterOperator = $splitKey[1] ?? 'eq';
94 if (!in_array($field, $this->fields)) {
98 if (!in_array($filterOperator, array_keys($this->filterOperators))) {
99 $filterOperator = 'eq';
102 $queryOperator = $this->filterOperators[$filterOperator];
104 return [$field, $queryOperator, $value];
108 * Apply sorting operations to the query from given parameters
109 * otherwise falling back to the first given field, ascending.
111 protected function sortQuery(Builder $query): Builder
113 $query = clone $query;
114 $defaultSortName = $this->fields[0];
117 $sort = $this->request->get('sort', '');
118 if (strpos($sort, '-') === 0) {
122 $sortName = ltrim($sort, '+- ');
123 if (!in_array($sortName, $this->fields)) {
124 $sortName = $defaultSortName;
127 return $query->orderBy($sortName, $direction);
131 * Apply count and offset for paging, based on params from the request while falling
132 * back to system defined default, taking the max limit into account.
134 protected function countAndOffsetQuery(Builder $query): Builder
136 $query = clone $query;
137 $offset = max(0, $this->request->get('offset', 0));
138 $maxCount = config('api.max_item_count');
139 $count = $this->request->get('count', config('api.default_item_count'));
140 $count = max(min($maxCount, $count), 1);
142 return $query->skip($offset)->take($count);