/** * Find By Distance using spherical cosine law * * @param \Cake\ORM\Query $query Query to modify * @param array $options Options for the query * @throws GeoDistanceInvalidArgumentException If parameters are missing or invalid * @return \Cake\ORM\Query */ public function findByDistance(Query $query, array $options) { // set up parameters $this->_validateOptions($options); $latitude = $options['latitude']; $longitude = $options['longitude']; $radius = $options['radius']; if (isset($options['units']) && !empty($options['units'])) { $units = $options['units']; } else { $units = $this->_config['units']; } $earthRadius = $this->_getEarthRadius($units); // construct query $sphericalCosineSql = "(:earth_radius * ACOS(\n COS(RADIANS(:latitude)) *\n COS(RADIANS({$this->_config['latitudeColumn']})) *\n COS( RADIANS({$this->_config['longitudeColumn']}) - RADIANS(:longitude) ) +\n SIN(RADIANS(:latitude)) *\n SIN(RADIANS({$this->_config['latitudeColumn']}))\n ) )"; $connection = $query->connection(); if ($connection->driver() instanceof Mysql) { $distance = "ROUND({$sphericalCosineSql}, 3)"; } elseif ($connection->driver() instanceof Postgres) { $distance = "ROUND( CAST({$sphericalCosineSql} AS numeric), 3)"; } $queryOptions = ['fields' => ['distance' => $distance], 'order' => ['distance ASC'], 'conditions' => ["{$distance} <= :radius"]]; $query->find('all', $queryOptions)->autoFields(true)->bind(':earth_radius', $earthRadius, 'integer')->bind(':latitude', $latitude, 'float')->bind(':longitude', $longitude, 'float')->bind(':radius', $radius, 'float'); return $query; }
/** * Casts all values from a row brought from a table to the correct * PHP type. * * @param Table $table The table object * @param array $values The values to cast * @return array */ protected function _castValues($table, $values) { $alias = $table->alias(); $driver = $this->_query->connection()->driver(); if (empty($this->types[$alias])) { $schema = $table->schema(); foreach ($schema->columns() as $col) { $this->types[$alias][$col] = Type::build($schema->columnType($col)); } } foreach ($values as $field => $value) { if (!isset($this->types[$alias][$field])) { continue; } $values[$field] = $this->types[$alias][$field]->toPHP($value, $driver); } return $values; }
/** * Constructor * * @param \Cake\ORM\Query $query Query from where results come * @param \Cake\Database\StatementInterface $statement The statement to fetch from */ public function __construct($query, $statement) { $repository = $query->repository(); $this->_query = $query; $this->_statement = $statement; $this->_driver = $driver = $this->_query->connection()->driver(); $this->_defaultTable = $this->_query->repository(); $this->_calculateAssociationMap(); $this->_hydrate = $this->_query->hydrate(); $this->_entityClass = $repository->entityClass(); $this->_useBuffering = $query->bufferResults(); $this->_defaultAlias = $this->_defaultTable->alias(); $this->_calculateColumnMap(); $this->_calculateTypeMap(); if ($this->_useBuffering) { $count = $this->count(); $this->_results = new SplFixedArray($count); } }
/** * Helper function used to return the keys from the query records that will be used * to eagerly load associations. * * @param array $external the list of external associations to be loaded * @param \Cake\ORM\Query $query The query from which the results where generated * @param BufferedStatement $statement The statement to work on * @return array */ protected function _collectKeys($external, $query, $statement) { $collectKeys = []; foreach ($external as $meta) { $instance = $meta->instance(); if (!$instance->requiresKeys($meta->config())) { continue; } $source = $instance->source(); $keys = $instance->type() === Association::MANY_TO_ONE ? (array) $instance->foreignKey() : (array) $instance->bindingKey(); $alias = $source->alias(); $pkFields = []; foreach ($keys as $key) { $pkFields[] = key($query->aliasField($key, $alias)); } $collectKeys[$meta->aliasPath()] = [$alias, $pkFields, count($pkFields) === 1]; } if (empty($collectKeys)) { return [[], $statement]; } if (!$statement instanceof BufferedStatement) { $statement = new BufferedStatement($statement, $query->connection()->driver()); } return [$this->_groupKeys($statement, $collectKeys), $statement]; }
/** * Assertion method for select clause contents. * * @param array $expected Array of expected fields. * @param \Cake\ORM\Query $query The query to check. * @return void */ protected function assertSelectClause($expected, $query) { if ($this->autoQuote) { $connection = $query->connection(); foreach ($expected as $key => $value) { $expected[$connection->quoteIdentifier($key)] = $connection->quoteIdentifier($value); unset($expected[$key]); } } $this->assertEquals($expected, $query->clause('select')); }
/** * Gets the name of the class driver used by the given $query to access the DB. * * @param \Cake\ORM\Query $query The query to inspect * @return string Lowercased drive name. e.g. `mysql` */ protected function _driverClass(Query $query) { $conn = $query->connection(null); list(, $driverClass) = namespaceSplit(strtolower(get_class($conn->driver()))); return $driverClass; }
/** * Checks that the fetching query either has auto fields on or * has the foreignKey fields selected. * If the required fields are missing, throws an exception. * * @param \Cake\ORM\Query $fetchQuery The association fetching query * @param array $key The foreign key fields to check * @return void * @throws InvalidArgumentException */ protected function _assertFieldsPresent($fetchQuery, $key) { $select = $fetchQuery->aliasFields($fetchQuery->clause('select')); if (empty($select)) { return; } $missingKey = function ($fieldList, $key) { foreach ($key as $keyField) { if (!in_array($keyField, $fieldList, true)) { return true; } } return false; }; $missingFields = $missingKey($select, $key); if ($missingFields) { $driver = $fetchQuery->connection()->driver(); $quoted = array_map([$driver, 'quoteIdentifier'], $key); $missingFields = $missingKey($select, $quoted); } if ($missingFields) { throw new InvalidArgumentException(sprintf('You are required to select the "%s" field(s)', implode(', ', (array) $key))); } }