]> BookStack Code Mirror - bookstack/blob - app/Entities/Tools/SearchOptions.php
6c03c57a6105ce7a36e4c311e2b2f75ba3b13149
[bookstack] / app / Entities / Tools / SearchOptions.php
1 <?php namespace BookStack\Entities\Tools;
2
3 use Illuminate\Http\Request;
4
5 class SearchOptions
6 {
7
8     /**
9      * @var array
10      */
11     public $searches = [];
12
13     /**
14      * @var array
15      */
16     public $exacts = [];
17
18     /**
19      * @var array
20      */
21     public $tags = [];
22
23     /**
24      * @var array
25      */
26     public $filters = [];
27
28     /**
29      * Create a new instance from a search string.
30      */
31     public static function fromString(string $search): SearchOptions
32     {
33         $decoded = static::decode($search);
34         $instance = new static();
35         foreach ($decoded as $type => $value) {
36             $instance->$type = $value;
37         }
38         return $instance;
39     }
40
41     /**
42      * Create a new instance from a request.
43      * Will look for a classic string term and use that
44      * Otherwise we'll use the details from an advanced search form.
45      */
46     public static function fromRequest(Request $request): SearchOptions
47     {
48         if (!$request->has('search') && !$request->has('term')) {
49             return static::fromString('');
50         }
51
52         if ($request->has('term')) {
53             return static::fromString($request->get('term'));
54         }
55
56         $instance = new static();
57         $inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags']);
58         $instance->searches = explode(' ', $inputs['search'] ?? []);
59         $instance->exacts = array_filter($inputs['exact'] ?? []);
60         $instance->tags = array_filter($inputs['tags'] ?? []);
61         foreach (($inputs['filters'] ?? []) as $filterKey => $filterVal) {
62             if (empty($filterVal)) {
63                 continue;
64             }
65             $instance->filters[$filterKey] = $filterVal === 'true' ? '' : $filterVal;
66         }
67         if (isset($inputs['types']) && count($inputs['types']) < 4) {
68             $instance->filters['type'] = implode('|', $inputs['types']);
69         }
70         return $instance;
71     }
72
73     /**
74      * Decode a search string into an array of terms.
75      */
76     protected static function decode(string $searchString): array
77     {
78         $terms = [
79             'searches' => [],
80             'exacts' => [],
81             'tags' => [],
82             'filters' => []
83         ];
84
85         $patterns = [
86             'exacts' => '/"(.*?)"/',
87             'tags' => '/\[(.*?)\]/',
88             'filters' => '/\{(.*?)\}/'
89         ];
90
91         // Parse special terms
92         foreach ($patterns as $termType => $pattern) {
93             $matches = [];
94             preg_match_all($pattern, $searchString, $matches);
95             if (count($matches) > 0) {
96                 $terms[$termType] = $matches[1];
97                 $searchString = preg_replace($pattern, '', $searchString);
98             }
99         }
100
101         // Parse standard terms
102         foreach (explode(' ', trim($searchString)) as $searchTerm) {
103             if ($searchTerm !== '') {
104                 $terms['searches'][] = $searchTerm;
105             }
106         }
107
108         // Split filter values out
109         $splitFilters = [];
110         foreach ($terms['filters'] as $filter) {
111             $explodedFilter = explode(':', $filter, 2);
112             $splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
113         }
114         $terms['filters'] = $splitFilters;
115
116         return $terms;
117     }
118
119     /**
120      * Encode this instance to a search string.
121      */
122     public function toString(): string
123     {
124         $string = implode(' ', $this->searches ?? []);
125
126         foreach ($this->exacts as $term) {
127             $string .= ' "' . $term . '"';
128         }
129
130         foreach ($this->tags as $term) {
131             $string .= " [{$term}]";
132         }
133
134         foreach ($this->filters as $filterName => $filterVal) {
135             $string .= ' {' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}';
136         }
137
138         return $string;
139     }
140 }