2020-06-27 14:29:00 +02:00
|
|
|
<?php namespace BookStack\Entities;
|
|
|
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
|
|
|
class SearchOptions
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
public $searches = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
public $exacts = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
public $tags = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
public $filters = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new instance from a search string.
|
|
|
|
*/
|
|
|
|
public static function fromString(string $search): SearchOptions
|
|
|
|
{
|
|
|
|
$decoded = static::decode($search);
|
|
|
|
$instance = new static();
|
|
|
|
foreach ($decoded as $type => $value) {
|
|
|
|
$instance->$type = $value;
|
|
|
|
}
|
|
|
|
return $instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new instance from a request.
|
|
|
|
* Will look for a classic string term and use that
|
|
|
|
* Otherwise we'll use the details from an advanced search form.
|
|
|
|
*/
|
|
|
|
public static function fromRequest(Request $request): SearchOptions
|
|
|
|
{
|
2020-06-27 14:37:18 +02:00
|
|
|
if (!$request->has('search') && !$request->has('term')) {
|
|
|
|
return static::fromString('');
|
|
|
|
}
|
|
|
|
|
2020-06-27 14:29:00 +02:00
|
|
|
if ($request->has('term')) {
|
|
|
|
return static::fromString($request->get('term'));
|
|
|
|
}
|
|
|
|
|
|
|
|
$instance = new static();
|
|
|
|
$inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags']);
|
|
|
|
$instance->searches = explode(' ', $inputs['search'] ?? []);
|
|
|
|
$instance->exacts = array_filter($inputs['exact'] ?? []);
|
|
|
|
$instance->tags = array_filter($inputs['tags'] ?? []);
|
|
|
|
foreach (($inputs['filters'] ?? []) as $filterKey => $filterVal) {
|
|
|
|
if (empty($filterVal)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$instance->filters[$filterKey] = $filterVal === 'true' ? '' : $filterVal;
|
|
|
|
}
|
|
|
|
if (isset($inputs['types']) && count($inputs['types']) < 4) {
|
|
|
|
$instance->filters['type'] = implode('|', $inputs['types']);
|
|
|
|
}
|
|
|
|
return $instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decode a search string into an array of terms.
|
|
|
|
*/
|
|
|
|
protected static function decode(string $searchString): array
|
|
|
|
{
|
|
|
|
$terms = [
|
|
|
|
'searches' => [],
|
|
|
|
'exacts' => [],
|
|
|
|
'tags' => [],
|
|
|
|
'filters' => []
|
|
|
|
];
|
|
|
|
|
|
|
|
$patterns = [
|
|
|
|
'exacts' => '/"(.*?)"/',
|
|
|
|
'tags' => '/\[(.*?)\]/',
|
|
|
|
'filters' => '/\{(.*?)\}/'
|
|
|
|
];
|
|
|
|
|
|
|
|
// Parse special terms
|
|
|
|
foreach ($patterns as $termType => $pattern) {
|
|
|
|
$matches = [];
|
|
|
|
preg_match_all($pattern, $searchString, $matches);
|
|
|
|
if (count($matches) > 0) {
|
|
|
|
$terms[$termType] = $matches[1];
|
|
|
|
$searchString = preg_replace($pattern, '', $searchString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse standard terms
|
|
|
|
foreach (explode(' ', trim($searchString)) as $searchTerm) {
|
|
|
|
if ($searchTerm !== '') {
|
|
|
|
$terms['searches'][] = $searchTerm;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Split filter values out
|
|
|
|
$splitFilters = [];
|
|
|
|
foreach ($terms['filters'] as $filter) {
|
|
|
|
$explodedFilter = explode(':', $filter, 2);
|
|
|
|
$splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
|
|
|
|
}
|
|
|
|
$terms['filters'] = $splitFilters;
|
|
|
|
|
|
|
|
return $terms;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encode this instance to a search string.
|
|
|
|
*/
|
|
|
|
public function toString(): string
|
|
|
|
{
|
|
|
|
$string = implode(' ', $this->searches ?? []);
|
|
|
|
|
|
|
|
foreach ($this->exacts as $term) {
|
|
|
|
$string .= ' "' . $term . '"';
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->tags as $term) {
|
|
|
|
$string .= " [{$term}]";
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->filters as $filterName => $filterVal) {
|
|
|
|
$string .= ' {' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $string;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|