Custom finder for distance.

Options:
- lat (required)
- lng (required)
- tableName
- distance
- sort

@param \Cake\ORM\Query $query Query.
@param array $options Array of options as described above
@return \Cake\ORM\Query
5760
public function findDistance(Query $query, array $options) {
		$options += ['tableName' => null, 'sort' => true];
		$sql = $this->distanceExpr($options['lat'], $options['lng'], null, null, $options['tableName']);

		if ($query->autoFields() === null) {
			$query->autoFields(true);
		}

		$query->select(['distance' => $query->newExpr($sql)]);
		if (isset($options['distance'])) {
			// Some SQL versions cannot reuse the select() distance field, so we better reuse the $sql snippet
			$query->where(function ($exp) use ($sql, $options) {
				return $exp->lt($sql, $options['distance']);
			});
		}

		if ($options['sort']) {
			$sort = $options['sort'] === true ? 'ASC' : $options['sort'];
			$query->order(['distance' => $sort]);
		}

		return $query;
	}
473412
protected function getPaginationData(Select $query, array $options)
    {
        // Get pagination options
        $page = (int) Arr::get($options, 'page');

        if ($page < 1) {
            $page = 1;
        }

        $resultsPerPage = Arr::get(
            $options,
            'resultsPerPage',
            $this->resultsPerPage
        );

        // Get total results
        $resultCount = $this->getQueryResultCount($query);
        $pageCount   = ceil($resultCount / $resultsPerPage);

        return new PaginationData([
            'page'             => $page,
            'page_count'       => $pageCount,
            'result_count'     => $resultCount,
            'results_per_page' => $resultsPerPage
        ]);
    }
512483
protected function _executeQuery(Query $query, array $options = [])
    {
        switch ($query->action()) {
            case Query::ACTION_CREATE:
                return $this->_executeCreateQuery($query, $options);
            case Query::ACTION_READ:
                return $this->_executeReadQuery($query, $options);
            case Query::ACTION_UPDATE:
                return $this->_executeUpdateQuery($query, $options);
            case Query::ACTION_DELETE:
                return $this->_executeDeleteQuery($query, $options);
        }

        return false;
    }
839358
public function findFailed(QueryInterface $query, array $options) : QueryInterface
    {
        $query->where([
            'import_id' => $options['import']->id,
            'status' => static::STATUS_FAIL
        ]);

        return $query;
    }
553973
public function scope(Query $query, $bundle = null)
    {
        $orderClause = $query->clause('order');
        if (!$orderClause) {
            return $query;
        }

        $class = new \ReflectionClass($orderClause);
        $property = $class->getProperty('_conditions');
        $property->setAccessible(true);
        $conditions = $property->getValue($orderClause);

        foreach ($conditions as $column => $direction) {
            if (empty($column) ||
                in_array($column, (array)$this->_table->schema()->columns()) || // ignore real columns
                !in_array($column, $this->_toolbox->getAttributeNames())
            ) {
                continue;
            }

            $conditions['(' . $this->_subQuery($column, $bundle) . ')'] = $direction;
            unset($conditions[$column]);
        }

        $property->setValue($orderClause, $conditions);

        return $query;
    }
415509
public function beforeFind(Event $event, Query $query, ArrayObject $options, $primary) {
		$query->formatResults(function (ResultSetInterface $results) {
			return $results->map(function ($row) {
				$this->processItems($row, 'output');
				return $row;
			});
		});
	}
575106
private function buildLocationQuery(Query $query, Location $parentLocation): LocationQuery
    {
        $locationQuery = new LocationQuery();

        $criteria = [
            new Criterion\Subtree($parentLocation->pathString),
            new Criterion\Visibility(Criterion\Visibility::VISIBLE),
            new Criterion\LogicalNot(new Criterion\LocationId($parentLocation->id)),
            $this->getMainLocationFilterCriteria($query),
            $this->getQueryTypeFilterCriteria($query, $parentLocation),
            $this->getContentTypeFilterCriteria($query),
            $this->getSectionFilterCriteria($query),
            $this->getObjectStateFilterCriteria($query),
        ];

        $criteria = array_filter(
            $criteria,
            static function ($criterion): bool {
                return $criterion instanceof Criterion;
            }
        );

        $locationQuery->filter = new Criterion\LogicalAnd($criteria);
        $locationQuery->sortClauses = $this->getSortClauses($query, $parentLocation);

        return $locationQuery;
    }
1345508
public function findCurrent(Query $query, array $options)
    {
        $query
            ->where(['Current.language_id' => Wasabi::contentLanguage()->id])
            ->order(['Current.modified' => 'DESC'])->limit(1);
        return $query;
    }
534593
public function queryTableGenerator($query, $options = [])
    {
        $statement = $this->_runQuery($query, $options);
        while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
            yield $row;
        }
    }
1307963
public function findDeleted($query, $options)
    {
        $query->where([
            $this->config('field') => $this->config('states.deleted'),
        ]);

        return $query;
    }
54460
public function findFilter(Query $query, array $options)
    {
        foreach ($this->filters()->collection() as $name => $filter) {
            $filter->apply($query, $options);
        }

        return $query;
    }
686080
public function findAuth($query, $options)
    {
        foreach ($options['columns'] as $column) {
            $query = $query->orWhere([$column => $options['username']]);
        }
        return $query;
    }
1199258
public function read($query, array $options = []) {
		$defaults = ['return' => 'resource', 'model' => $query->model()];
		$options += $defaults;
		$params = compact('query', 'options');

		return Filters::run($this, __FUNCTION__, $params, function($params) {
			$query = $params['query'];
			$params = $query->export($this);

			list($_path, $conditions) = (array) $params['conditions'];
			$model = $query->model();

			if (empty($_path)) {
				$_path = '_all_docs';
				$conditions['include_docs'] = 'true';
			}
			$path = "{$this->_config['database']}/{$_path}";
			$args = (array) $conditions + (array) $params['limit'] + (array) $params['order'];

			$result = $this->connection->get($path, $args);
			$result = is_string($result) ? json_decode($result, true) : $result;

			$data = $stats = [];

			if (isset($result['_id'])) {
				$data = [$result];
			} elseif (isset($result['rows'])) {
				$data = $result['rows'];
				unset($result['rows']);
				$stats = $result;
			}
			$stats += ['total_rows' => null, 'offset' => null];
			$opts = compact('stats') + [
				'class' => 'set', 'exists' => true, 'defaults' => false
			];

			return $this->item($model, $data, $opts);
		});
	}
702044
protected function doNear($near, array $query, array $options)
    {
        if ($near instanceof Point) {
            $near = $near->jsonSerialize();
        }

        list($commandOptions, $clientOptions) = isset($options['socketTimeoutMS']) || isset($options['timeout'])
            ? $this->splitCommandAndClientOptions($options)
            : [$options, []];

        $command = [];
        $command['geoNear'] = $this->mongoCollection->getName();
        $command['near'] = $near;
        $command['spherical'] = isset($near['type']);
        $command['query'] = (object) $query;
        $command = array_merge($command, $commandOptions);

        $database = $this->database;
        $result = $this->retry(function() use ($database, $command, $clientOptions) {
            return $database->command($command, $clientOptions);
        });

        if (empty($result['ok'])) {
            throw new ResultException($result);
        }

        $arrayIterator = new ArrayIterator(isset($result['results']) ? $result['results'] : []);
        $arrayIterator->setCommandResult($result);

        return $arrayIterator;
    }
986154
public function findSitemapRecords(Query $query, array $options) {
		$query = $query
			->where($this->_config['conditions'])
			->cache("sitemap_{$query->repository()->alias()}", $this->_config['cacheConfigKey'])
			->order($this->_config['order'])
			->formatResults(function ($results) {
				return $this->mapResults($results);
			});

		if (!empty($this->_config['fields'])) {
			$query = $query->select($this->_config['fields']);
		}

		return $query;
	}
634264
public function scopeSortable($query, $defaultParameters = null)
    {
        if (request()->allFilled(['sort', 'direction'])) { // allFilled() is macro
            return $this->queryOrderBuilder($query, request()->only(['sort', 'direction']));
        }

        if (is_null($defaultParameters)) {
            $defaultParameters = $this->getDefaultSortable();
        }

        if ( ! is_null($defaultParameters)) {
            $defaultSortArray = $this->formatToParameters($defaultParameters);
            if (config('columnsortable.allow_request_modification', true) && ! empty($defaultSortArray)) {
                request()->merge($defaultSortArray);
            }

            return $this->queryOrderBuilder($query, $defaultSortArray);
        }

        return $query;
    }
953639
public function findBanned(Query $query, array $options)
    {
        return $query->where([sprintf('%s.banned', $this->getAlias()) => true]);
    }