Simplify multi-column wildcard searches across model columns, relationships, and raw SQL expressions using a powerful whereLike macro on the Eloquent Builder.
Searching across multiple attributes frequently leads to verbose and repetitive orWhere query chains in controller code.
By registering a custom whereLike macro on Eloquent's Builder, you can search across model columns, dot-notation relationships (user.name), and custom database expressions (DB::raw()) in a single readable call.
Registering the Macro in AppServiceProvider
namespace App\Providers;
use Illuminate\Contracts\Database\Query\Expression;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Builder::macro('whereLike', function ($attributes, string $searchTerm) {
return $this->where(function (Builder $query) use ($attributes, $searchTerm) {
foreach (Arr::wrap($attributes) as $attribute) {
$query->when(
// Check if attribute is a relationship dot-notation string (e.g. 'user.name')
! ($attribute instanceof Expression) && str_contains((string) $attribute, '.'),
function (Builder $query) use ($attribute, $searchTerm) {
[$relation, $relatedAttribute] = explode('.', (string) $attribute);
$query->orWhereHas($relation, function (Builder $query) use ($relatedAttribute, $searchTerm) {
$query->where($relatedAttribute, 'LIKE', "%{$searchTerm}%");
});
},
function (Builder $query) use ($attribute, $searchTerm) {
// Search on local column or DB::raw expression
$query->orWhere($attribute, 'LIKE', "%{$searchTerm}%");
}
);
}
});
});
}
}
Usage Example
You can pass single columns, related model fields using dot notation, and formatted raw SQL expressions:
use App\Models\Post;
use Illuminate\Support\Facades\DB;
$search = request('search');
$posts = Post::query()
->whereLike([
'title',
'description',
'user.name',
'user.email',
DB::raw('CONCAT(user.first_name, " ", user.last_name)'),
DB::raw('DATE_FORMAT(created_at, "%d/%m/%Y")'),
], $search)
->with('user')
->paginate(15);
How It Works
- Encapsulated Scope: Wraps the entire search clause in a single
$this->where(function ($query) ...)closure so that booleanAND/ORoperator precedence is respected when chained with other query filters. - Relationship Detection: Automatically detects dot notation (
'user.name') and appliesorWhereHassubqueries against the related model. - Expression Compatibility: Supports
DB::raw()expressions without throwing type errors, enabling searches on computed columns and SQL date formats.
Summary
- Use
Builder::macro('whereLike')to consolidate multi-attribute wildcard searches into a single expressive method. - Automatically handles both local table columns and related model attributes via
orWhereHas. - Respects SQL operator grouping and supports raw database expressions.
Related Tips
View all tips →Reusable Migration Fields with Custom Blueprint Macros
Extend Laravel's Blueprint class with custom macros to standardize and reuse common schema columns across database migrations.
Define Custom Macros on the Eloquent Builder
Register reusable query methods directly on the Builder so every model gains access without repeating logic.