Laragear/Surreal

View on GitHub
src/Query/Builder.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Laragear\Surreal\Query;

use Carbon\CarbonInterval;
use Closure;
use DateInterval;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Support\Collection;
use function array_key_last;
use function func_get_args;
use function func_num_args;
use function is_array;
use function is_int;
use function is_string;
use function ksort;
use function reset;

/**
 * @internal
 *
 * @mixin \Illuminate\Database\Query\Builder
 * @property-read \Laragear\Surreal\Query\SurrealGrammar $grammar
 * @property-read \Laragear\Surreal\SurrealConnection $connection
 */
class Builder
{
    /**
     * Creates a record in SurrealDB.
     *
     * @return \Closure
     */
    public function create(): Closure
    {
        return function (iterable $values = []): Collection {
            if (is_array(reset($values))) {
                ksort($values);
            }

            $this->applyBeforeQueryCallbacks();

            return $this->connection->create(
                $this->grammar->compileCreate($this, $values), $values,
            );
        };
    }

    /**
     * Relates two records through and edge and data.
     *
     * @return \Closure
     */
    public function relate(): Closure
    {
        return function ($id, $edge, $relatedId, $values) {
            if (is_array(reset($values))) {
                ksort($values);
            }

            $this->applyBeforeQueryCallbacks();

            return $this->connection->create(
                $this->grammar->compileRelate($this, $edge, $relatedId, $values), $values,
            );
        };
    }

    /**
     * Set the return to a given type.
     *
     * @return \Closure
     */
    public function return(): Closure
    {
        return function (ReturnType|string|array $type): QueryBuilder {
            // This spaghetti code does the following:
            //   1. If the type is a return type string, cast it to the enum.               ("none")
            //   2. If is the enum itself, set it.                                          (ReturnType:Default)
            //   3. If there are more arguments, set them all.                              ("foo", "bar"...)
            //   4. The default is a string or array, set them "all" as list.               ("foo")
            $this->joins['return'] = match (true) {
                is_string($type) && ReturnType::tryFrom($type) => ReturnType::from($type),
                $type instanceof ReturnType => $type,
                func_num_args() > 1 => func_get_args(),
                default => (array) $type,
            };

            return $this;
        };
    }

    /**
     * Set the return to none.
     *
     * @return \Closure
     */
    public function returnNone(): Closure
    {
        return function (): QueryBuilder {
            /** @var $this \Illuminate\Database\Query\Builder */
            return $this->return(ReturnType::None);
        };
    }

    /**
     * Sets a timeout for the query.
     *
     * @return \Closure
     */
    public function timeout(): Closure
    {
        return function (DateInterval|CarbonInterval|int $duration): QueryBuilder {
            $this->joins['timeout'] = match (true) {
                is_int($duration) => CarbonInterval::create(0, seconds: $duration),
                $duration instanceof DateInterval => CarbonInterval::instance($duration),
                default => $duration
            };

            return $this;
        };
    }

    /**
     * Sets the FETCH and relations to be retrieved in parallel.
     *
     * @return \Closure
     */
    public function parallel(): Closure
    {
        return function (): QueryBuilder {
            $this->joins['parallel'] = true;

            return $this;
        };
    }

    /**
     * Split the results by each value in an array or nested array.
     *
     * @return \Closure
     */
    public function split(): Closure
    {
        return function ($key): QueryBuilder {
            $this->joins['split'] = func_num_args() > 1 ? func_get_args() : (array) $key;

            return $this;
        };
    }

    /**
     * Fetches related parent records from the attributes.
     *
     * @return \Closure
     */
    public function fetch(): Closure
    {
        return function ($attributes): QueryBuilder {
            $this->joins['fetch'] = func_num_args() > 1 ? func_get_args() : (array) $attributes;

            return $this;
        };
    }

    /**
     * Order the results by collation.
     *
     * @return \Closure
     */
    public function orderByCollate(): Closure
    {
        return function ($field, $direction = 'asc') {
            $this->orderBy($field, $direction);

            $last = array_key_last($this->orders);

            $this->orders[$last]['type'] = 'collation';

            return $this;
        };
    }

    /**
     * Order the results by numeric.
     *
     * @return \Closure
     */
    public function orderByNumeric(): Closure
    {
        return function ($field, $direction = 'asc') {
            $this->orderBy($field, $direction);

            $last = array_key_last($this->orders);

            $this->orders[$last]['type'] = 'numeric';

            return $this;
        };
    }

    /**
     * Starts a relate operation.
     *
     * @return \Closure
     */
    public function relateTo(): Closure
    {
        return function ($related): RelateTo {
            return new RelateTo($this, $related);
        };
    }

    /**
     * Adds related records to fetch in a query.
     *
     * @return \Closure
     */
    public function related(): Closure
    {
        return function (string|array ...$related): QueryBuilder|Related {
            if (empty($related)) {
                return new Related($this);
            }

            foreach ($related as $relation) {
                $this->joins['edges'][] = (array) $relation;
            }

            return $this;
        };
    }
}