/home/bdqbpbxa/api-uniferx.goodface.com.ua/vendor/laravel/nova/src/Query/Builder.php
<?php

namespace Laravel\Nova\Query;

use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Pagination\Paginator;
use Laravel\Nova\Contracts\QueryBuilder;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\TrashedStatus;
use Laravel\Scout\Builder as ScoutBuilder;
use Laravel\Scout\Contracts\PaginatesEloquentModels;
use RuntimeException;

class Builder implements QueryBuilder
{
    /**
     * The resource class.
     *
     * @var class-string<\Laravel\Nova\Resource>
     */
    protected $resourceClass;

    /**
     * The original query builder instance.
     *
     * @var \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation|null
     */
    protected $originalQueryBuilder;

    /**
     * The query builder instance.
     *
     * @var \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation|null
     */
    protected $queryBuilder;

    /**
     * Optional callbacks before model query execution.
     *
     * @var array<int, callable(\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation):void>
     */
    protected $queryCallbacks = [];

    /**
     * Determine query callbacks has been applied.
     *
     * @var bool
     */
    protected $appliedQueryCallbacks = false;

    /**
     * Construct a new query builder for a resource.
     *
     * @param  class-string<\Laravel\Nova\Resource>  $resourceClass
     * @return void
     */
    public function __construct($resourceClass)
    {
        $this->resourceClass = $resourceClass;
    }

    /**
     * Build a "whereKey" query for the given resource.
     *
     * @param  \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation  $query
     * @param  string  $key
     * @return $this
     */
    public function whereKey($query, $key)
    {
        $this->setOriginalQueryBuilder($this->queryBuilder = $query);

        $this->tap(function ($query) use ($key) {
            $query->whereKey($key);
        });

        return $this;
    }

    /**
     * Build a "search" query for the given resource.
     *
     * @param  \Laravel\Nova\Http\Requests\NovaRequest  $request
     * @param  \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation  $query
     * @param  string|null  $search
     * @param  array<int, \Laravel\Nova\Query\ApplyFilter>  $filters
     * @param  array<string, string>  $orderings
     * @param  string  $withTrashed
     * @return $this
     */
    public function search(NovaRequest $request, $query, $search = null,
        array $filters = [], array $orderings = [],
        $withTrashed = TrashedStatus::DEFAULT)
    {
        $this->setOriginalQueryBuilder($query);

        $hasSearchKeyword = ! empty(trim($search ?? ''));
        $hasOrderings = collect($orderings)->filter()->isNotEmpty();

        if ($this->resourceClass::usesScout()) {
            if ($hasSearchKeyword) {
                $this->queryBuilder = $this->resourceClass::buildIndexQueryUsingScout($request, $search, $withTrashed);
                $search = '';

                if ($query instanceof MorphToMany || $query instanceof BelongsToMany) {
                    $this->tap(function ($queryBuilder) use ($query) {
                        /** @var \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation $queryBuilder */
                        $queryBuilder->whereIn(
                            $this->resourceClass::newModel()->getQualifiedKeyName(),
                            $query->allRelatedIds()
                        );
                    });
                }
            }

            if (! $hasSearchKeyword && ! $hasOrderings) {
                $this->tap(function ($query) {
                    /** @var \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation $query */
                    $this->resourceClass::defaultOrderings($query);
                });
            }
        }

        if (! isset($this->queryBuilder)) {
            $this->queryBuilder = $query;
        }

        $this->tap(function ($query) use ($request, $search, $filters, $orderings, $withTrashed) {
            /** @var \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation $query */
            $this->resourceClass::buildIndexQuery(
                $request, $query, $search, $filters, $orderings, $withTrashed
            );
        });

        return $this;
    }

    /**
     * Pass the query to a given callback.
     *
     * @param  callable(\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation):void  $callback
     * @return $this
     */
    public function tap($callback)
    {
        $this->queryCallbacks[] = $callback;

        return $this;
    }

    /**
     * Set the "take" directly to Scout or Eloquent builder.
     *
     * @param  int  $limit
     * @return $this
     */
    public function take($limit)
    {
        $this->queryBuilder->take($limit);

        return $this;
    }

    /**
     * Defer setting a "limit" using query callback and only executed via Eloquent builder.
     *
     * @param  int  $limit
     * @return $this
     */
    public function limit($limit)
    {
        return $this->tap(function ($query) use ($limit) {
            /** @var \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation $query */
            $query->limit($limit);
        });
    }

    /**
     * Get the results of the search.
     *
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function get()
    {
        return $this->applyQueryCallbacks($this->queryBuilder)->get();
    }

    /**
     * Get a lazy collection for the given query by chunks of the given size.
     *
     * @param  int  $chunkSize
     * @return \Illuminate\Support\LazyCollection
     */
    public function lazy($chunkSize = 1000)
    {
        if (! method_exists($this->queryBuilder, 'lazy')) {
            return $this->cursor();
        }

        return $this->applyQueryCallbacks($this->queryBuilder)->lazy($chunkSize);
    }

    /**
     * Get a lazy collection for the given query.
     *
     * @return \Illuminate\Support\LazyCollection
     */
    public function cursor()
    {
        $queryBuilder = $this->applyQueryCallbacks($this->queryBuilder);

        if (
            method_exists($queryBuilder, 'cursor')
            && (! $queryBuilder instanceof ScoutBuilder && empty($queryBuilder->getEagerLoads()))
        ) {
            return $queryBuilder->cursor();
        }

        return $queryBuilder->get()->lazy();
    }

    /**
     * Get the paginated results of the query.
     *
     * @param  int  $perPage
     * @return array{0: \Illuminate\Contracts\Pagination\Paginator, 1: int|null, 2: bool}
     */
    public function paginate($perPage)
    {
        $queryBuilder = $this->applyQueryCallbacks($this->queryBuilder);

        if (! $queryBuilder instanceof ScoutBuilder) {
            return [
                $queryBuilder->simplePaginate($perPage),
                $this->getCountForPagination(),
                true,
            ];
        }

        return $this->paginateFromScout($queryBuilder, $perPage);
    }

    /**
     * Get the paginated results of the Scout query.
     *
     * @param  \Laravel\Scout\Builder  $queryBuilder
     * @param  int  $perPage
     * @return array{0: \Illuminate\Contracts\Pagination\Paginator, 1: int|null, 2: false}
     */
    protected function paginateFromScout(ScoutBuilder $queryBuilder, $perPage)
    {
        $originalQueryBuilder = clone $this->originalQueryBuilder;

        [$sql, $bindings] = [$originalQueryBuilder->toSql(), $originalQueryBuilder->getBindings()];

        $modelQueryBuilder = $this->handleQueryCallbacks($originalQueryBuilder);

        if ($sql === $modelQueryBuilder->toSql() && array_diff($bindings, $modelQueryBuilder->getBindings()) === []) {
            /** @var \Illuminate\Pagination\LengthAwarePaginator $paginated */
            $paginated = $queryBuilder->paginate($perPage);

            $items = $paginated->items();

            $hasMorePages = ($paginated->perPage() * $paginated->currentPage()) < $paginated->total();

            return [
                app()->makeWith(Paginator::class, [
                    'items' => $items,
                    'perPage' => $paginated->perPage(),
                    'currentPage' => $paginated->currentPage(),
                    'options' => $paginated->getOptions(),
                ])->hasMorePagesWhen($hasMorePages),
                $paginated->total(),
                false,
            ];
        }

        /** @var array<int, string|int> $scoutResultKeys */
        $scoutResultKeys = $queryBuilder->keys()->all();

        /** @var \Illuminate\Database\Eloquent\Model&\Laravel\Scout\Searchable $model */
        $model = $this->resourceClass::newModel();

        $paginated = tap($model->queryScoutModelsByIds(
            $queryBuilder, $scoutResultKeys
        ), function ($query) {
            /** @var \Illuminate\Database\Eloquent\Builder $query */
            $this->originalQueryBuilder = $query;
        })->simplePaginate($perPage);

        if (! $model->searchableUsing() instanceof PaginatesEloquentModels) {
            /** @var array<int|string, int> $objectIdPositions */
            $objectIdPositions = collect($scoutResultKeys)->values()->flip()->all();

            $paginated->setCollection(
                $paginated->getCollection()
                    ->sortBy(function ($model) use ($objectIdPositions) {
                        return $objectIdPositions[$model->getScoutKey()];
                    }, SORT_NUMERIC)->values()
            );
        }

        return [$paginated, $this->getCountForPagination(), false];
    }

    /**
     * Get the count of the total records for the paginator.
     *
     * @return int|null
     */
    public function getCountForPagination()
    {
        return $this->toBaseQueryBuilder()->getCountForPagination();
    }

    /**
     * Convert the query builder to an Eloquent query builder (skip using Scout).
     *
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation
     */
    public function toBase()
    {
        return $this->applyQueryCallbacks($this->originalQueryBuilder);
    }

    /**
     * Convert the query builder to an fluent query builder (skip using Scout).
     *
     * @return \Illuminate\Database\Query\Builder
     */
    public function toBaseQueryBuilder()
    {
        return $this->toBase()->toBase();
    }

    /**
     * Set original query builder instance.
     *
     * @param  \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation  $queryBuilder
     * @return void
     */
    protected function setOriginalQueryBuilder($queryBuilder)
    {
        if (isset($this->originalQueryBuilder)) {
            throw new RuntimeException('Unable to override $originalQueryBuilder, please create a new '.self::class);
        }

        $this->originalQueryBuilder = $queryBuilder;
    }

    /**
     * Apply any query callbacks to the query builder.
     *
     * @param  \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation  $queryBuilder
     * @return \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation
     */
    protected function applyQueryCallbacks($queryBuilder)
    {
        if (! $this->appliedQueryCallbacks) {
            $this->handleQueryCallbacks($queryBuilder);

            $this->appliedQueryCallbacks = true;
        }

        return $queryBuilder;
    }

    /**
     * Handle any query callbacks to the query builder.
     *
     * @param  \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation  $queryBuilder
     * @return \Laravel\Scout\Builder|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation
     */
    protected function handleQueryCallbacks($queryBuilder)
    {
        $callback = function ($query) {
            /** @var \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Relations\Relation $query */
            collect($this->queryCallbacks)
                ->filter()
                ->each(function ($callback) use ($query) {
                    call_user_func($callback, $query);
                });
        };

        if ($queryBuilder instanceof ScoutBuilder) {
            $queryBuilder->query($callback);
        } else {
            $queryBuilder->tap($callback);
        }

        return $queryBuilder;
    }
}