]> BookStack Code Mirror - bookstack/blob - app/Entities/SearchOptions.php
af9156953e6bf98f732a23c0e91b432e1adc33ee
[bookstack] / app / Entities / SearchOptions.php
1 <?php namespace BookStack\Entities;
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('term')) {
49             return static::fromString($request->get('term'));
50         }
51
52         $instance = new static();
53         $inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags']);
54         $instance->searches = explode(' ', $inputs['search'] ?? []);
55         $instance->exacts = array_filter($inputs['exact'] ?? []);
56         $instance->tags = array_filter($inputs['tags'] ?? []);
57         foreach (($inputs['filters'] ?? []) as $filterKey => $filterVal) {
58             if (empty($filterVal)) {
59                 continue;
60             }
61             $instance->filters[$filterKey] = $filterVal === 'true' ? '' : $filterVal;
62         }
63         if (isset($inputs['types']) && count($inputs['types']) < 4) {
64             $instance->filters['type'] = implode('|', $inputs['types']);
65         }
66         return $instance;
67     }
68
69     /**
70      * Decode a search string into an array of terms.
71      */
72     protected static function decode(string $searchString): array
73     {
74         $terms = [
75             'searches' => [],
76             'exacts' => [],
77             'tags' => [],
78             'filters' => []
79         ];
80
81         $patterns = [
82             'exacts' => '/"(.*?)"/',
83             'tags' => '/\[(.*?)\]/',
84             'filters' => '/\{(.*?)\}/'
85         ];
86
87         // Parse special terms
88         foreach ($patterns as $termType => $pattern) {
89             $matches = [];
90             preg_match_all($pattern, $searchString, $matches);
91             if (count($matches) > 0) {
92                 $terms[$termType] = $matches[1];
93                 $searchString = preg_replace($pattern, '', $searchString);
94             }
95         }
96
97         // Parse standard terms
98         foreach (explode(' ', trim($searchString)) as $searchTerm) {
99             if ($searchTerm !== '') {
100                 $terms['searches'][] = $searchTerm;
101             }
102         }
103
104         // Split filter values out
105         $splitFilters = [];
106         foreach ($terms['filters'] as $filter) {
107             $explodedFilter = explode(':', $filter, 2);
108             $splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
109         }
110         $terms['filters'] = $splitFilters;
111
112         return $terms;
113     }
114
115     /**
116      * Encode this instance to a search string.
117      */
118     public function toString(): string
119     {
120         $string = implode(' ', $this->searches ?? []);
121
122         foreach ($this->exacts as $term) {
123             $string .= ' "' . $term . '"';
124         }
125
126         foreach ($this->tags as $term) {
127             $string .= " [{$term}]";
128         }
129
130         foreach ($this->filters as $filterName => $filterVal) {
131             $string .= ' {' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}';
132         }
133
134         return $string;
135     }
136
137 }