Sometimes, the existing methods inside Laravel's Query Builder are not enough to apply every rule you want if your main idea is to reuse a lot of these rules among classes inside your project.

Let's suppose you want to create a filter by name in a product listing. In this scenario, we would construct the Query Builder from its Model object like this:

$products = Product::query()->where('name', 'LIKE', '%' . $name . '%')->get();

Beyond product listing, the user could also filter by name in other places, like customers, suppliers, drivers, etc. In this way, the same structure of the Query Builder could be reused, making the developer to repeat the sentence elsewhere.

However, we could extend the Query Builder class, creating a version where we could create a reusable sentence for similar wheres. Like this:

<?php

namespace App\MyBuilder;

use Illuminate\Database\Query\Builder;

class MyBuilder extends Builder {

    public function whereLike($column, $value, $boolean = 'and') {
        $this->where($column, 'LIKE', '%' . $value . '%', $boolean);
        return $this;
    }

    public function orWhereLike($column, $value) {
        $this->whereLike($column, $value, 'or');
        return $this;
    }

}

After that, we must modify the newBaseQueryBuilder method inside the Model class, so our new class called MyBuilder could the instanciated instead of the default Query Builder.

protected function newBaseQueryBuilder() {
    $connection = $this->getConnection();
    return new MyBuilder(
        $connection, $connection->getQueryGrammar(), $connection->getPostProcessor()
    );
}

Lastly, we can replace the first sentence in order to use our new whereLike method, still producing the same result.

$products = Product::query()->whereLike('name', $name)->get();

Other useful functionalities

Removing accents and/or special characters in a string is always useful to improve the user experience and get more precise results. If your database has a particular functionality to do this operation, we can adapt the whereLike method use this feature. As an example, PostgreSQL has the UNACCENT function, which removes all accents from a string. In this way, the adaptation of the method would look like this:

<?php

namespace App\MyBuilder;

use Illuminate\Database\Query\Builder;

class MyBuilder extends Builder {

    public function whereLike($column, $value, $boolean = 'and') {
        $this->whereRaw('UNACCENT(' . $column . ') ILIKE UNACCENT(?)', [ '%' . $value . '%' ], $boolean);
        return $this;
    }

    public function orWhereLike($column, $value) {
        $this->whereLike($column, $value, 'or');
        return $this;
    }

}

Another interesting feature is the possibility to check whether or not a JOIN clause has already been inserted in a particular query. To do that, we could override the join method from Query Builder class to call a custom method to check it prior to join.

<?php

namespace App\MyBuilder;

use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Expression;

class MyBuilder extends Builder {

    public function isJoined($table) {
        if ($this->joins == null) {
            return false;
        }
        $key = ($table instanceof Expression ? $table->getValue() : $table);
        foreach ($this->joins as $join) {
            if (($join->table instanceof Expression && $join->table->getValue() === $key) || $join->table === $key) {
                return true;
            }
        }
        return false;
    }

    public function join($table, $first, $operator = null, $second = null, $type = 'inner', $where = false) {
        if ($this->isJoined($table)) {
            return $this;
        }
        return parent::join($table, $first, $operator, $second, $type, $where);
    }

}