1 | <?php |
||
2 | |||
3 | namespace Arbory\Base\Services; |
||
4 | |||
5 | use Arbory\Base\Admin\Admin; |
||
6 | use Arbory\Base\Admin\Module; |
||
7 | use Arbory\Base\Exceptions\GlobalSearchException; |
||
8 | use Illuminate\Database\Eloquent\Builder; |
||
9 | use Illuminate\Database\Eloquent\Model; |
||
10 | use Illuminate\Support\Collection; |
||
11 | use Illuminate\Support\Str; |
||
12 | |||
13 | class GlobalSearchService |
||
14 | { |
||
15 | protected const ALLOWED_RELATIONSHIP_NESTING_LEVEL = 2; |
||
16 | |||
17 | public function __construct(protected Admin $admin) |
||
18 | { |
||
19 | } |
||
20 | |||
21 | /** |
||
22 | * @throws GlobalSearchException |
||
23 | */ |
||
24 | public function search(string $term): array |
||
25 | { |
||
26 | $searchableModules = $this->admin |
||
27 | ->modules() |
||
28 | ->all() |
||
29 | ->filter(function ($module) { |
||
30 | /** |
||
31 | * @var Module $module |
||
32 | */ |
||
33 | $configuration = $module->getConfiguration(); |
||
34 | $controller = app($configuration->getControllerClass()); |
||
35 | |||
36 | return property_exists($controller, 'module') && |
||
37 | $module->isAuthorized() && |
||
38 | property_exists($controller, 'searchBy') && |
||
39 | ! empty($controller->searchBy); |
||
40 | }); |
||
41 | |||
42 | return $searchableModules->mapWithKeys(function ($module) use ($term) { |
||
43 | $configuration = $module->getConfiguration(); |
||
44 | $controller = app($configuration->getControllerClass()); |
||
45 | $searchFields = $controller->searchBy; |
||
46 | $model = $controller->resource(); |
||
0 ignored issues
–
show
|
|||
47 | |||
48 | $formattedResults = $this->findResults($module, $model, $searchFields, $term); |
||
49 | if (! $formattedResults->count()) { |
||
50 | return []; |
||
51 | } |
||
52 | |||
53 | return [ucfirst($module->name()) => $formattedResults]; |
||
54 | })->toArray(); |
||
55 | } |
||
56 | |||
57 | protected function findResults(Module $module, Model $model, array $searchFields, string $term): Collection |
||
58 | { |
||
59 | $searchQuery = $model::query(); |
||
60 | |||
61 | foreach ($searchFields as $searchField) { |
||
62 | $searchField = Str::of($searchField)->explode('.'); |
||
63 | |||
64 | if ($searchField->count() > self::ALLOWED_RELATIONSHIP_NESTING_LEVEL) { |
||
65 | throw new GlobalSearchException('Only one level nested relationship search is allowed'); |
||
66 | } |
||
67 | |||
68 | if ($searchField->count() === self::ALLOWED_RELATIONSHIP_NESTING_LEVEL) { |
||
69 | $searchQuery->orWhereHas($searchField[0], function (Builder $query) use ($searchField, $term) { |
||
70 | return $query->where($searchField[1], 'LIKE', '%' . $term . '%'); |
||
71 | }); |
||
72 | } else { |
||
73 | $searchQuery = $searchQuery->orWhere($searchField[0], 'LIKE', '%' . $term . '%'); |
||
74 | } |
||
75 | } |
||
76 | |||
77 | $results = $searchQuery->take(config('arbory.search.results_count_per_module'))->get(); |
||
78 | |||
79 | return $this->formattedResults($module, $results); |
||
80 | } |
||
81 | |||
82 | protected function formattedResults(Module $module, Collection $results): Collection |
||
83 | { |
||
84 | return $results->map(function ($row) use ($module) { |
||
85 | return [ |
||
86 | 'title' => Str::of((string) $row) |
||
87 | ->limit(config('arbory.search.result_title_length')) |
||
88 | ->ucfirst(), |
||
89 | 'url' => $module->url('edit', $row->getKey()), |
||
90 | ]; |
||
91 | }); |
||
92 | } |
||
93 | } |
||
94 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.