Skip to content

Instantly share code, notes, and snippets.

@ambengers
Last active November 16, 2022 09:24
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 ambengers/473dbba54ae94c96df998b8f01b6860d to your computer and use it in GitHub Desktop.
Save ambengers/473dbba54ae94c96df998b8f01b6860d to your computer and use it in GitHub Desktop.
A trait for performing search on Eloquent model columns and related tables.
<?php
namespace App\Filters\Concerns;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphTo;
trait SearchesEloquent
{
protected $query;
/**
* Apply the scope to a given Eloquent query query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string|null $value
* @return void
*/
public function apply(Builder $query, $value = null) : void
{
$this->query = $query;
$this->search($value);
}
/**
* Perform a search from query string.
*
* @param string $text
* @return Illuminate\Database\Eloquent\Builder
*/
protected function search($text = null)
{
if (! $text || ! $this->columns) {
return $this->query;
}
$this->query->where(function ($query) use ($text) {
// Since we have a search filter, let's spin
// through our list of searchable columns
$this->performSearch($query, $text);
});
return $this->query;
}
/**
* Iterate through searchable columns.
*
* @param Illuminate\Database\Eloquent\Builder $query
* @param string $text
* @return void
*/
protected function performSearch(Builder $builder, $text)
{
foreach ($this->columns as $attribute => $value) {
// If the value is an array, that means we want to search through a relationship.
// We need to make sure that we send through the closure's query instance so we
// can have an 'AND' query with nested queries wrapped within a parenthesis.
is_array($value)
? $this->performRelationSearch($builder, $attribute, $value, $text)
: $this->performColumnSearch($builder, $value, $text);
}
return $builder;
}
/**
* Peform search on model columns
*
* @param Illuminate\Contracts\Database\Eloquent\Builder $builder
* @param string|array $column
* @param string $text
* @return Illuminate\Contracts\Database\Eloquent\Builder
*/
protected function performColumnSearch(Builder $builder, $column, $text)
{
$builder->orWhere(function ($query) use ($column, $text) {
foreach (explode(' ', $text) as $word) {
$query->where($column, 'like', "{$word}%");
}
});
return $builder;
}
/**
* Search through related tables.
*
* @param Illuminate\Database\Eloquent\Builder $builder
* @param string $related
* @param array|string $columns
* @param string $text
* @return Illuminate\Database\Eloquent\Builder
*/
protected function performRelationSearch(Builder $builder, $related, $columns, $text)
{
$columns = is_array($columns) ? $columns : [$columns];
$callback = function ($query) use ($columns, $text) {
// Here, we want to make sure that we are grouping our orWhere
// statement inside a where statement if incase the
// relatonship is also running query scopes
$query->where(function ($query) use ($columns, $text) {
foreach ($columns as $attribute => $value) {
is_array($value)
? $this->performRelationSearch($query, $attribute, $value, $text)
: $this->performColumnSearch($query, $value, $text);
}
});
};
return ($builder->getModel()->$related() instanceof MorphTo)
? $builder->orWhereHasMorph($related, '*', $callback)
: $builder->orWhereHas($related, $callback);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment