Skip to content

Instantly share code, notes, and snippets.

@MrPunyapal
Last active April 12, 2024 03:32
Show Gist options
  • Star 34 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save MrPunyapal/31433fdd415518f8510385b86178ff1f to your computer and use it in GitHub Desktop.
Save MrPunyapal/31433fdd415518f8510385b86178ff1f to your computer and use it in GitHub Desktop.
Laravel Custom 'whereLike' Macro for Dynamic 'LIKE' Searches including relationships
<?php
namespace App\Providers;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
// Define the 'whereLike' macro
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 the attribute is not an expression and contains a dot (indicating a related model)
! ($attribute instanceof \Illuminate\Contracts\Database\Query\Expression) &&
str_contains((string) $attribute, '.'),
function (Builder $query) use ($attribute, $searchTerm) {
// Split the attribute into a relation and related attribute
[$relation, $relatedAttribute] = explode('.', (string) $attribute);
// Perform a 'LIKE' search on the related model's attribute
$query->orWhereHas($relation, function (Builder $query) use ($relatedAttribute, $searchTerm) {
$query->where($relatedAttribute, 'LIKE', "%{$searchTerm}%");
});
// if need more deep nesting then commonet above code and
// use below (which is not recommend)
// Split the attribute into a relation and related attribute
// $attrs = explode('.', (string) $attribute);
// $relatedAttribute = array_pop($attrs);
// $relation = implode('.', $attrs);
// Perform a 'LIKE' search on the related model's attribute
// $query->orWhereRelation($relation, $relatedAttribute, 'LIKE', "%{$searchTerm}%");
},
function (Builder $query) use ($attribute, $searchTerm) {
// Perform a 'LIKE' search on the current model's attribute
// also attribute can be an expression
$query->orWhere($attribute, 'LIKE', "%{$searchTerm}%");
}
);
}
});
});
}
}
// example of usage 👇
Post::query()
->whereLike([
'title',
// search in the current model's 'title' attribute
'description',
// search in the current model's 'description' attribute
'user.name',
// search in the related model's 'name' attribute
'user.email',
// search in the related model's 'email' attribute
DB::raw('DATE_FORMAT(created_at, "%d/%m/%Y")'),
// search in the formatted 'created_at' attribute
DB::raw('CONCAT(user.first_name, " ", user.last_name)'),
// search in the concatenated 'first_name' and 'last_name' attributes
], request()->search)
// search for the request's 'search' query parameter
->with('user')
->get();
@xavi7th
Copy link

xavi7th commented Oct 30, 2023

Awesome tip.

For Laravel 8 you need to return the query builder otherwise all whereLike queries return null.

... return $this->where(function (Builder $query) use ($attributes, $searchTerm) { ...

@MrPunyapal
Copy link
Author

Awesome tip.

For Laravel 8 you need to return the query builder otherwise all whereLike queries return null.

... return $this->where(function (Builder $query) use ($attributes, $searchTerm) { ...

thanks for sharing

@ezequidias
Copy link

ezequidias commented Nov 27, 2023

Awesome tip.

instead of using:

$query->orWhereHas($relation, function (Builder $query) use ($relatedAttribute, $searchTerm) {

  $query->where($relatedAttribute, 'LIKE', "%{$searchTerm}%");

 });

use:

$query->orWhereRelation('comments.user' /*relation*/, 'name' /*column*/, 'LIKE', "%{$searchTerm}%");

instead of using:

[$relation, $relatedAttribute] = explode('.', (string) $attribute);

use:

$attrs = explode('.', (string) $attribute);
$relatedAttribute = array_pop($attrs);
$relation = implode('.', $attrs);
Post::query()
    ->whereLike([
        'comments.user.name', // multiples relationships nested
        'comments.user.address.name', // multiples relationships nested
        'comments.user.address.city.name', // multiples relationships nested
  ...

Docs:
https://laravel.com/docs/10.x/eloquent-relationships#inline-relationship-existence-queries

@MrPunyapal
Copy link
Author

n

Awesome tip.

instead of using:

$query->orWhereHas($relation, function (Builder $query) use ($relatedAttribute, $searchTerm) {

  $query->where($relatedAttribute, 'LIKE', "%{$searchTerm}%");

 });

use:

$query->orWhereRelation('comments.user' /*relation*/, 'name' /*column*/, 'LIKE', "%{$searchTerm}%");

instead of using:

[$relation, $relatedAttribute] = explode('.', (string) $attribute);

use:

$attrs = explode('.', (string) $attribute);
$relatedAttribute = array_pop($attrs);
$relation = implode('.', $attrs);
Post::query()
    ->whereLike([
        'comments.user.name', // multiples relationships nested
        'comments.user.address.name', // multiples relationships nested
        'comments.user.address.city.name', // multiples relationships nested
  ...

Docs: https://laravel.com/docs/10.x/eloquent-relationships#inline-relationship-existence-queries

nice 🫡

@Danishkhan1234
Copy link

Danishkhan1234 commented Dec 13, 2023

public function render()
{
    return view('livewire.admin.category-list',[
        'categories' => Category::query()->whereLike(['name'], $this->search)
                            ->latest()                    
                            ->paginate($this->limit),
    ]);
}

I tried using the controller and livewire component, but neither of them worked and gave me an error.
Call to a member function latest() on null

@MrPunyapal
Copy link
Author

public function render()
{
    return view('livewire.admin.category-list',[
        'categories' => Category::query()->whereLike(['name'], $this->search)
                            ->latest()                    
                            ->paginate($this->limit),
    ]);
}

i used in livewire component ,its given me error 
 Call to a member function latest() on null

Laravel version? 🤔

@Danishkhan1234
Copy link

public function render()
{
    return view('livewire.admin.category-list',[
        'categories' => Category::query()->whereLike(['name'], $this->search)
                            ->latest()                    
                            ->paginate($this->limit),
    ]);
}

i used in livewire component ,its given me error 
 Call to a member function latest() on null

Laravel version? 🤔

    "laravel/framework": "^10.0",

@MrPunyapal
Copy link
Author

just add return before $this->where.....

@Danishkhan1234
Copy link

Thankyou so much bro ,now its working 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment