Skip to content

Instantly share code, notes, and snippets.

@justsanjit
Last active April 19, 2022 22:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justsanjit/47bca2cb36aae32db808fc03927903dd to your computer and use it in GitHub Desktop.
Save justsanjit/47bca2cb36aae32db808fc03927903dd to your computer and use it in GitHub Desktop.
Fuzzy filter for spatie/laravel-query-builder
<?php
$query->allowedSorts('id', 'name')
->allowedFilters(
AllowedFilter::custom('search', new FuzzySearch('name', 'email', 'address.city'))
);
<?php
namespace App\Filters;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Spatie\QueryBuilder\Filters\Filter;
class FuzzySearch implements Filter
{
/** @var array */
private $columns;
/** @var array */
private $relationProperties = [];
/** @var array */
private $properties = [];
public function __construct(string ...$columns)
{
$this->columns = $columns;
}
/**
* @param string $value
*/
public function __invoke(Builder $query, $value, string $property): void
{
foreach (Arr::wrap($this->columns) as $column) {
if ($this->isRelationProperty($query, $column)) {
[$relation, $c] = $this->splitPropertyRelation($column);
$this->relationProperties[$relation][] = $c;
}
if ($this->isProperty($query, $column)) {
$this->properties[] = $column;
}
}
$this->applyFilters($query, $this->properties, $value);
$this->applyRelationFilters($query, $this->relationProperties, $value);
}
protected function applyFilters(Builder $query, array $attributes, string $value): void
{
$query->where(function (Builder $query) use ($attributes, $value) {
foreach (Arr::wrap($attributes) as $attribute) {
$query->orWhere(function ($query) use ($attribute, $value) {
foreach (explode(' ', $value) as $searchTerm) {
$query->orWhere($attribute, 'LIKE', "%{$searchTerm}%");
}
});
}
});
}
protected function applyRelationFilters(Builder $query, array $attributes, $value): void
{
foreach ($attributes as $relation => $columns) {
$this->applyRelationFilter($query, $relation, $columns, $value);
}
}
protected function applyRelationFilter(Builder $query, $relation, array $attributes, $value): void
{
$query->orWhereHas($relation, function (Builder $query) use ($attributes, $value) {
$this->applyFilters($query, $attributes, $value);
});
}
protected function splitPropertyRelation(string $column): array
{
return explode('.', $column);
}
protected function isRelationProperty(Builder $query, string $property): bool
{
if (! Str::contains($property, '.')) {
return false;
}
if (in_array($property, $this->relationProperties, true)) {
return false;
}
if (Str::startsWith($property, $query->getModel()->getTable().'.')) {
return false;
}
return true;
}
protected function isProperty(Builder $query, string $property): bool
{
if (in_array($property, $this->properties, true)) {
return false;
}
if (Str::contains($property, '.') && ! Str::startsWith($property, $query->getModel()->getTable().'.')) {
return false;
}
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment