Skip to content

Commit

Permalink
Make users searchable with scout
Browse files Browse the repository at this point in the history
  • Loading branch information
ttoino committed Oct 17, 2023
1 parent d6c1fdf commit 63ed17c
Show file tree
Hide file tree
Showing 10 changed files with 64 additions and 165 deletions.
39 changes: 7 additions & 32 deletions app/Http/Controllers/CRUDController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@ abstract class CRUDController extends Controller
*/
protected array $rules = [];

/**
* The columns to search in.
*
* @var array<int, string>
*/
protected array $search = [];

/**
* The validation rules for the store method.
*
Expand Down Expand Up @@ -76,29 +69,13 @@ protected function with(): array

public function index(Request $request)
{
$sort_by = $request->query('sort_by', 'id');
$sort_dir = $request->query('sort_dir', 'asc');
$query = $this->model::orderBy($sort_by, $sort_dir);

$filter_by = $request->query('filter_by');

if ($filter_by) {
foreach ($filter_by as $column => $values) {
$query->whereIn($column, $values);
}
}
$isSearchable = in_array(\Laravel\Scout\Searchable::class, class_uses($this->model));

$search = $request->query('query');

if ($search) {
$search = explode(' ', $search);
foreach ($search as $searchTerm) {
$query->where(function ($query) use ($searchTerm) {
foreach ($this->search as $column) {
$query->orWhere($column, 'ILIKE', "%{$searchTerm}%");
}
});
}
if ($isSearchable && $search !== null) {
$query = $this->model::search($search);
} else {
$query = $this->model::orderBy('id');
}

$filteredQuery = collect($request->query())
Expand All @@ -107,12 +84,10 @@ public function index(Request $request)

$with = $this->with();

return Inertia::render("CRUD/$this->view/Index", [
return Inertia::render("CRUD/{$this->view}/Index", [
'items' => $items,
'with' => $with,
'sortBy' => $sort_by,
'sortDir' => $sort_dir,
'filterBy' => $filter_by,
'isSearchable' => $isSearchable,
]);
}

Expand Down
5 changes: 5 additions & 0 deletions app/Models/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,9 @@ public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}

public function toSearchableArray(): array
{
return [];
}
}
8 changes: 8 additions & 0 deletions app/Models/Company.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,12 @@ public function participants(): HasManyThrough
]
)->distinct();
}

public function toSearchableArray(): array
{
return [
'description' => $this->description,
'social_media' => $this->socialMedia->toSearchableArray(),
];
}
}
7 changes: 7 additions & 0 deletions app/Models/Participant.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,11 @@ public function competitionTeams(): BelongsToMany
{
return $this->belongsToMany(CompetitionTeam::class);
}

public function toSearchableArray(): array
{
return [
'social_media' => $this->socialMedia->toSearchableArray(),
];
}
}
10 changes: 10 additions & 0 deletions app/Models/Speaker.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,14 @@ public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}

public function toSearchableArray(): array
{
return [
'title' => $this->title,
'description' => $this->description,
'organization' => $this->organization,
'social_media' => $this->socialMedia->toSearchableArray(),
];
}
}
13 changes: 13 additions & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
use Laravel\Scout\Searchable;

class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use Searchable;
use TwoFactorAuthenticatable;

/**
Expand Down Expand Up @@ -95,4 +97,15 @@ public function isSpeaker(): bool
{
return $this->usertype_type === Speaker::class;
}

public function toSearchableArray()
{
return [
'id' => (int) $this->id,
'name' => $this->name,
'email' => $this->email,
'type' => call_user_func('end', explode('\\', $this->usertype_type)),
'user' => $this->usertype->toSearchableArray(),
];
}
}
10 changes: 0 additions & 10 deletions config/scout.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,6 @@
'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
'key' => env('MEILISEARCH_KEY'),
'index-settings' => [
\App\Models\User::class => [
'filterableAttributes' => [
'type',
],
'sortableAttributes' => [
'id',
'name',
'email',
],
],
],
],

Expand Down
121 changes: 0 additions & 121 deletions resources/js/Components/CRUD/Header.vue
Original file line number Diff line number Diff line change
@@ -1,126 +1,5 @@
<script setup lang="ts">
import type MyPageProps from "@/Types/PageProps";
import { Link, usePage } from "@inertiajs/vue3";
import { OhVueIcon } from "oh-vue-icons";
import { computed, ref } from "vue";
import route, { type RouteParams } from "ziggy-js";
type Props =
| {
sortBy: string;
filterBy?: never;
filterValues?: never;
}
| {
filterBy: string;
filterValues: Record<string, string>;
sortBy?: never;
}
| {
sortBy?: never;
filterBy?: never;
filterValues?: never;
};
interface PageProps extends Record<string, unknown>, MyPageProps {
sortBy?: string;
sortDir?: "asc" | "desc";
filterBy?: Record<string, string[]>;
}
const props = defineProps<Props>();
const page = usePage<PageProps>();
const open = ref(false);
const sort = computed(() => {
if (!props.sortBy) return { url: "#", icon: "io-swap-vertical" };
const router = route();
return {
url: route(router.current() ?? "", {
...router.params,
sort_by: props.sortBy,
sort_dir:
page.props.sortBy === props.sortBy
? page.props.sortDir === "asc"
? "desc"
: "asc"
: "asc",
}),
icon:
page.props.sortBy === props.sortBy
? page.props.sortDir === "asc"
? "io-arrow-down"
: "io-arrow-up"
: "io-swap-vertical",
};
});
const filter = computed(() => {
if (!props.filterBy) return [];
const router = route();
return Object.entries(props.filterValues).map(([value, label]) => {
const oldValue = page.props.filterBy?.[props.filterBy] ?? [];
const selected = oldValue.includes(value);
const newValue = selected
? oldValue.filter((v) => v !== value)
: [...oldValue, value];
return {
label,
value,
selected: page.props.filterBy?.[props.filterBy].includes(value),
href: route(router.current() ?? "", {
...router.params,
filter_by: {
...(page.props.filterBy ?? {}),
[props.filterBy]: newValue,
},
} as unknown as RouteParams), // We need this because the types are wrong 🙃
};
});
});
</script>

<template>
<th class="px-4 py-2 text-start last:text-right">
<slot></slot>
<Link
v-if="sortBy"
:href="sort.url"
preserve-scroll
preserve-state
:only="['items', 'sortBy', 'sortDir']"
>
<OhVueIcon :name="sort.icon" class="ml-2" />
</Link>
<span v-if="filterBy" class="relative ml-2 w-min">
<button @click="open = !open">
<OhVueIcon name="io-filter" />
</button>

<div
v-show="open"
class="absolute right-0 flex max-h-[50vh] w-max flex-col gap-4 overflow-y-auto border border-black bg-2023-bg p-2 text-black"
>
<Link
v-for="f in filter"
:key="f.value"
:href="f.href"
preserve-scroll
preserve-state
:only="['items', 'filterBy']"
class="flex flex-row items-center gap-3"
>
<input type="checkbox" :checked="f.selected" />
{{ f.label }}
</Link>
</div>
</span>
</th>
</template>
8 changes: 7 additions & 1 deletion resources/js/Layouts/CRUDLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defineProps<{
items: Paginated<T>;
title: string;
name: string;
isSearchable?: boolean;
}>();
const query = useSearch("query", ["items"]);
Expand All @@ -28,7 +29,12 @@ const query = useSearch("query", ["items"]);
>
<h2 class="mr-auto text-2xl"><slot name="heading"></slot></h2>

<TextInput v-model="query" label="Pesquisar" type="search" />
<TextInput
v-if="isSearchable"
v-model="query"
label="Pesquisar"
type="search"
/>

<Link :href="route(`admin.${name}.create`)">Novo</Link>
</header>
Expand Down
8 changes: 7 additions & 1 deletion resources/js/Pages/CRUD/User/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Header from "@/Components/CRUD/Header.vue";
interface Props {
items: Paginated<User>;
isSearchable?: boolean;
}
defineProps<Props>();
Expand All @@ -22,7 +23,12 @@ const usertypeMap = {
</script>

<template>
<CRUDLayout title="User" :items="items" name="users">
<CRUDLayout
title="User"
:items="items"
name="users"
:is-searchable="isSearchable"
>
<template #heading>Utilizadores</template>

<template #header>
Expand Down

0 comments on commit 63ed17c

Please sign in to comment.