henzeb/query-filter-builder

View on GitHub
src/Illuminate/Builders/Builder.php

Summary

Maintainability
B
6 hrs
Test Coverage
A
100%
<?php

namespace Henzeb\Query\Illuminate\Builders;

use DB;
use DateTime;
use TypeError;
use Henzeb\Query\Filters\Contracts\Filter;
use Henzeb\Query\Filters\Contracts\QueryFilter;
use Henzeb\Query\Builders\Contracts\QueryBuilder;
use Illuminate\Database\Query\Builder as IlluminateBuilder;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Henzeb\Query\Illuminate\Filters\Contracts\Filter as IlluminateFilter;

class Builder implements QueryBuilder
{
    public function __construct(private IlluminateBuilder|EloquentBuilder $builder)
    {
    }

    protected function getBuilder(): IlluminateBuilder|EloquentBuilder
    {
        return $this->builder;
    }

    public function is(string $key, float|bool|int|string $value): void
    {
        $this->getBuilder()->where($key, $value);
    }

    public function orIs(string $key, float|bool|int|string $value): void
    {
        $this->getBuilder()->orWhere($key, $value);
    }

    public function not(string $key, float|bool|int|string $value): void
    {
        $this->getBuilder()->where($key, '!=', $value);
    }

    public function orNot(string $key, float|bool|int|string $value): void
    {
        $this->getBuilder()->orWhere($key, '!=', $value);
    }

    public function empty(string $key): void
    {
        $this->builder->whereNull($key);
    }

    public function orEmpty(string $key): void
    {
        $this->builder->orWhereNull($key);
    }

    public function notEmpty(string $key): void
    {
        $this->builder->whereNotNull($key);
    }

    public function orNotEmpty(string $key): void
    {
        $this->builder->orWhereNotNull($key);
    }

    public function in(string $key, string|float|int|bool $in, string|float|int|bool ...$moreIn): void
    {
        $this->getBuilder()->whereIn($key, [$in, ...$moreIn]);
    }

    public function orIn(string $key, string|float|int|bool $in, string|float|int|bool ...$moreIn): void
    {
        $this->getBuilder()->orWhereIn($key, [$in, ...$moreIn]);
    }

    public function notIn(string $key, string|float|int|bool $notIn, string|float|int|bool ...$moreNotIn): void
    {
        $this->getBuilder()->whereNotIn($key, [$notIn, ...$moreNotIn]);
    }

    public function orNotIn(string $key, string|float|int|bool $notIn, string|float|int|bool ...$moreNotIn): void
    {
        $this->getBuilder()->orWhereNotIn($key, [$notIn, ...$moreNotIn]);
    }

    public function like(string $key, string $like): void
    {
        $this->getBuilder()->where($key, 'like', $like);
    }

    public function orLike(string $key, string $like): void
    {
        $this->getBuilder()->orWhere($key, 'like', $like);
    }

    public function notLike(string $key, string $notLike): void
    {
        $this->getBuilder()->where($key, 'not like', $notLike);
    }

    public function orNotLike(string $key, string $notLike): void
    {
        $this->getBuilder()->orWhere($key, 'not like', $notLike);
    }

    public function less(string $key, float|DateTime|int $less): void
    {
        $this->getBuilder()->where($key, '<', $less);
    }

    public function orLess(string $key, float|DateTime|int $less): void
    {
        $this->getBuilder()->orWhere($key, '<', $less);
    }

    public function greater(string $key, float|DateTime|int $greater): void
    {
        $this->getBuilder()->where($key, '>', $greater);
    }

    public function orGreater(string $key, float|DateTime|int $greater): void
    {
        $this->getBuilder()->orWhere($key, '>', $greater);
    }

    public function lessOrEqual(string $key, float|DateTime|int $lessOrEqual): void
    {
        $this->getBuilder()->where($key, '<=', $lessOrEqual);
    }

    public function orLessOrEqual(string $key, float|DateTime|int $lessOrEqual): void
    {
        $this->getBuilder()->orWhere($key, '<=', $lessOrEqual);
    }

    public function greaterOrEqual(string $key, float|DateTime|int $greaterOrEqual): void
    {
        $this->getBuilder()->where($key, '>=', $greaterOrEqual);
    }

    public function orGreaterOrEqual(string $key, float|DateTime|int $greaterOrEqual): void
    {
        $this->getBuilder()->orWhere($key, '>=', $greaterOrEqual);
    }

    public function between(string $key, float|int $low, float|int $high): void
    {
        $this->getBuilder()->whereBetween($key, [$low, $high]);
    }

    public function orBetween(string $key, float|int $low, float|int $high): void
    {
        $this->getBuilder()->orWhereBetween($key, [$low, $high]);
    }

    public function notBetween(string $key, float|int $low, float|int $high): void
    {
        $this->getBuilder()->whereNotBetween($key, [$low, $high]);
    }

    public function orNotBetween(string $key, float|int $low, float|int $high): void
    {
        $this->getBuilder()->orWhereNotBetween($key, [$low, $high]);
    }

    public function dateBetween(string $key, DateTime $low, DateTime $high): void
    {
        $this->getBuilder()->whereBetween($key, [$low, $high]);
    }

    public function orDateBetween(string $key, DateTime $low, DateTime $high): void
    {
        $this->getBuilder()->orWhereBetween($key, [$low, $high]);
    }

    public function dateNotBetween(string $key, DateTime $low, DateTime $high): void
    {
        $this->getBuilder()->whereNotBetween($key, [$low, $high]);
    }

    public function orDateNotBetween(string $key, DateTime $low, DateTime $high): void
    {
        $this->getBuilder()->orWhereNotBetween($key, [$low, $high]);
    }

    public function limit(int $limit): void
    {
        $this->getBuilder()->limit($limit);
    }

    public function offset(int $offset): void
    {
        $this->getBuilder()->offset($offset);
    }

    public function asc(string $key): void
    {
        $this->getBuilder()->orderBy($key);
    }

    public function desc(string $key): void
    {
        $this->getBuilder()->orderByDesc($key);
    }

    public function nest(QueryFilter $query): void
    {
        $this->getBuilder()->where(
            $this->getNestingClosure($query)
        );
    }

    public function orNest(QueryFilter $query): void
    {
        $this->getBuilder()->orWhere(
            $this->getNestingClosure($query)
        );
    }

    public function filter(Filter $filter): void
    {
        $this->globalFilters($filter);

        $this->getBuilder()->where(
            $this->getNestingClosure(
                $this->filterProxy($filter)
            )
        );
    }

    public function orFilter(Filter $filter): void
    {
        $this->globalFilters($filter);

        $this->getBuilder()->orWhere(
            $this->getNestingClosure(
                $this->filterProxy($filter)
            )
        );
    }

    private function getNestingClosure(QueryFilter|IlluminateFilter $query): callable
    {
        return function (IlluminateBuilder|EloquentBuilder $queryBuilder) use ($query) {
            $query->build($query instanceof QueryFilter ? new self($queryBuilder) : $queryBuilder);
            return $queryBuilder;
        };
    }

    public function filterProxy(Filter $filter): IlluminateFilter
    {
        if ($filter instanceof IlluminateFilter) {
            return $filter;
        }

        throw new TypeError(
            '`' . self::class . ' expects `' . IlluminateFilter::class . '` but got `' . $filter::class . '`'
        );
    }

    private function globalFilters(Filter $filter)
    {
        $query = DB::query();
        $this->filterProxy($filter)->build($query);

        if ($query->joins) {

            $this->getBuilder()->joins = array_merge(
                $this->getBuilder()->joins ?? [],
                $query->joins ?? []
            );

            $this->getBuilder()->bindings['join'] = array_merge(
                $this->getBuilder()->bindings['join'],
                $query->bindings['join']
            );
        }
    }
}