| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Api; | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | use Illuminate\Database\Eloquent\Builder; | 
					
						
							|  |  |  | use Illuminate\Database\Eloquent\Collection; | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  | use Illuminate\Http\Request; | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ListingResponseBuilder | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     protected $query; | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |     protected $request; | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     protected $fields; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |     protected $filterOperators = [ | 
					
						
							|  |  |  |         'eq'   => '=', | 
					
						
							|  |  |  |         'ne'   => '!=', | 
					
						
							|  |  |  |         'gt'   => '>', | 
					
						
							|  |  |  |         'lt'   => '<', | 
					
						
							|  |  |  |         'gte'  => '>=', | 
					
						
							|  |  |  |         'lte'  => '<=', | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |         'like' => 'like', | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * ListingResponseBuilder constructor. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |     public function __construct(Builder $query, Request $request, array $fields) | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $this->query = $query; | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |         $this->request = $request; | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |         $this->fields = $fields; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get the response from this builder. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function toResponse() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         $filteredQuery = $this->filterQuery($this->query); | 
					
						
							| 
									
										
										
										
											2020-04-26 04:37:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         $total = $filteredQuery->count(); | 
					
						
							|  |  |  |         $data = $this->fetchData($filteredQuery); | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return response()->json([ | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |             'data'  => $data, | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |             'total' => $total, | 
					
						
							|  |  |  |         ]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Fetch the data to return in the response. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |     protected function fetchData(Builder $query): Collection | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         $query = $this->countAndOffsetQuery($query); | 
					
						
							|  |  |  |         $query = $this->sortQuery($query); | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         return $query->get($this->fields); | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Apply any filtering operations found in the request. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |     protected function filterQuery(Builder $query): Builder | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         $query = clone $query; | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |         $requestFilters = $this->request->get('filter', []); | 
					
						
							|  |  |  |         if (!is_array($requestFilters)) { | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |             return $query; | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $queryFilters = collect($requestFilters)->map(function ($value, $key) { | 
					
						
							|  |  |  |             return $this->requestFilterToQueryFilter($key, $value); | 
					
						
							|  |  |  |         })->filter(function ($value) { | 
					
						
							|  |  |  |             return !is_null($value); | 
					
						
							|  |  |  |         })->values()->toArray(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         return $query->where($queryFilters); | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Convert a request filter query key/value pair into a [field, op, value] where condition. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function requestFilterToQueryFilter($fieldKey, $value): ?array | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $splitKey = explode(':', $fieldKey); | 
					
						
							|  |  |  |         $field = $splitKey[0]; | 
					
						
							|  |  |  |         $filterOperator = $splitKey[1] ?? 'eq'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!in_array($field, $this->fields)) { | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!in_array($filterOperator, array_keys($this->filterOperators))) { | 
					
						
							|  |  |  |             $filterOperator = 'eq'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $queryOperator = $this->filterOperators[$filterOperator]; | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |         return [$field, $queryOperator, $value]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Apply sorting operations to the query from given parameters | 
					
						
							|  |  |  |      * otherwise falling back to the first given field, ascending. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |     protected function sortQuery(Builder $query): Builder | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         $query = clone $query; | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |         $defaultSortName = $this->fields[0]; | 
					
						
							|  |  |  |         $direction = 'asc'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |         $sort = $this->request->get('sort', ''); | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |         if (strpos($sort, '-') === 0) { | 
					
						
							|  |  |  |             $direction = 'desc'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $sortName = ltrim($sort, '+- '); | 
					
						
							|  |  |  |         if (!in_array($sortName, $this->fields)) { | 
					
						
							|  |  |  |             $sortName = $defaultSortName; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         return $query->orderBy($sortName, $direction); | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Apply count and offset for paging, based on params from the request while falling | 
					
						
							|  |  |  |      * back to system defined default, taking the max limit into account. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |     protected function countAndOffsetQuery(Builder $query): Builder | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         $query = clone $query; | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |         $offset = max(0, $this->request->get('offset', 0)); | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |         $maxCount = config('api.max_item_count'); | 
					
						
							| 
									
										
										
										
											2020-01-02 00:33:47 +08:00
										 |  |  |         $count = $this->request->get('count', config('api.default_item_count')); | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |         $count = max(min($maxCount, $count), 1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-26 05:15:59 +08:00
										 |  |  |         return $query->skip($offset)->take($count); | 
					
						
							| 
									
										
										
										
											2019-12-28 22:58:07 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | } |