我已经能够在数据库中搜索关键字并将结果作为json返回给应用程序,但现在我正在尝试 准备一个语句,该语句将返回位于选定英里数内的数据,但此语句返回null。 我在$ sql = $ db-> prepare语句和$ sql-> execute语句中做错了什么?
FUNCTION TO GET CARD FROM SEARCH WORD, LONGITUDE, LATITUDE, AMD MILES CALLED FROM GetCards.php
public function getAllCards($word, $longitude, $latitude, $miles) {
//Connect to db using the PDO not PHP
$db = new PDO('mysql:host=localhost;dbname=XXXXX', 'XXXXX', 'XXXXX');
//Here we prepare the SELECT statement from the search, word place holder :word, longitude place holder :longitude, latitude place holder :latitude, miles place holder :miles
$sql = $db->prepare('SELECT * FROM carddbtable WHERE businessNameDB=:word OR lastNameDB=:word OR firstKeywordDB=:word OR secondKeywordDB=:word OR thirdKeywordDB=:word OR fourthKeywordDB=:word OR fithKeywordDB=:word AND latitudeLocateDB, longitudeLocateDB, 3956 * 2 *
ASIN(SQRT( POWER(SIN(($latitude - latitudeLocateDB)*pi()/180/2),2)
+COS($latitude*pi()/180 )*COS(latitudeLocateDB*pi()/180)
*POWER(SIN(($longitude-longitudeLocateDB)*pi()/180/2),2)))
as distance FROM $carddbtable WHERE
longitudeLocateDB between ($longitude-$miles/cos(radians($latitude))*69)
and ($longitude+$miles/cos(radians($latitude))*69)
and latitudeLocateDB between ($latitude-($miles/69))
and ($latitude+($miles/69))
having distance < $miles ORDER BY distance limit 100');
//We execute the $sql with the search word variable $word, $longitude, $latitude, $miles
$sql->execute([':word' => $word, ':longitude' => $longitude, ':latitude' => $latitude, ':miles' => $miles]);
//Looping through the results
while ($row = $sql->fetch(PDO::FETCH_ASSOC)) {
//Store all return rows in $returnArray
$returnArray[] = $row;
//Print to screen
// echo json_encode($row). "<br>"."<br>";
}
//Feedback results
return $returnArray;
}
答案 0 :(得分:1)
尝试使用此代码。我无法测试它,但如果你一步一步地去耐心它会起作用。如果您有错误或遇到问题,请向我们提供反馈。可能是,sql语句中LIMIT
的参数在第一次尝试时不起作用。告诉我们。另外,我使用以下代码进行Haversine公式和矩形计算:Reverse Geocoding with it loaded into MySQL database?,因为它与您的几乎相同。
一些建议:
Exception
给了你一般观点。
通常你应该学习如何抛出和处理SPL
(标准PHP库)类型也是如此。资源:
祝你好运!<?php
require_once 'configs.php';
require_once 'functions.php';
require_once 'geolocationFunctions.php';
// Activate error reporting (only on development).
activateErrorReporting();
try {
// Create db connection.
$connection = createConnection(
MYSQL_HOST
, MYSQL_DATABASE
, MYSQL_USERNAME
, MYSQL_PASSWORD
, MYSQL_PORT
, MYSQL_CHARSET
);
$cardsInRadius = getCardsInRadius($connection, $word, $longitude, $latitude, $radius, $limit = 100);
// For testing purposes.
printData($cardsInRadius, TRUE);
closeConnection($connection);
} catch (PDOException $pdoException) {
// On development.
printData($pdoException, TRUE);
// On production.
// echo $pdoException->getMessage();
exit();
} catch (Exception $exception) {
// On development.
printData($exception, TRUE);
// On production.
// echo $exception->getMessage();
exit();
}
<?php
/*
* ---------------------
* Geolocation functions
* ---------------------
*/
/**
* Search the for a keyword and return data located within given radius.
*
* @param PDO $connection Connection instance.
* @param string $word Keyword to lookup.
* @param double $longitude Longitude value to lookup.
* @param double $latitude Latitude value to lookup.
* @param integer $radius Distance radius (in miles) having lat/long as center point.
* @param integer $limit [optional] Number of records to return.
* @throws Exception
*/
function getCardsInRadius($connection, $word, $longitude, $latitude, $radius, $limit = 100) {
/*
* Create a rectangle in which the circle with the given radius will be defined.
* >> 1° of latitude ~= 69 miles
* >> 1° of longitude ~= cos(latitude) * 69
*/
$rectLong1 = $longitude - $radius / abs(cos(deg2rad($latitude)) * 69);
$rectLong2 = $longitude + $radius / abs(cos(deg2rad($latitude)) * 69);
$rectLat1 = $latitude - ($radius / 69);
$rectLat2 = $latitude + ($radius / 69);
// Approximate the circle inside the rectangle.
$distance = sprintf('3956 * 2 * ASIN(SQRT(POWER(SIN((%s - latitudeLocateDB) * pi()/180 / 2), 2) + COS(%s * pi()/180) * COS(latitudeLocateDB * pi()/180) * POWER(SIN((%s - longitudeLocateDB) * pi()/180 / 2), 2) ))'
, $latitude
, $latitude
, $longitude
);
// Sql statement.
$sql = sprintf('SELECT
*,
%s AS distance
FROM carddbtable
WHERE
(
businessNameDB = :businessNameDB
OR lastNameDB = :lastNameDB
OR firstKeywordDB = :firstKeywordDB
OR secondKeywordDB = :secondKeywordDB
OR thirdKeywordDB = :thirdKeywordDB
OR fourthKeywordDB = :fourthKeywordDB
OR fithKeywordDB = :fithKeywordDB
)
AND longitudeLocateDB BETWEEN :rectLong1 AND :rectLong2
AND latitudeLocateDB BETWEEN :rectLat1 AND :rectLat2
HAVING distance < :distance
ORDER BY distance
LIMIT :limit'
, $distance
);
// Prepare and check sql statement (returns PDO statement).
$statement = $connection->prepare($sql);
if (!$statement) {
throw new Exception('The SQL statement can not be prepared!');
}
// Bind values to sql statement parameters.
$statement->bindValue(':businessNameDB', $word, getInputParameterDataType($word));
$statement->bindValue(':lastNameDB', $word, getInputParameterDataType($word));
$statement->bindValue(':firstKeywordDB', $word, getInputParameterDataType($word));
$statement->bindValue(':secondKeywordDB', $word, getInputParameterDataType($word));
$statement->bindValue(':thirdKeywordDB', $word, getInputParameterDataType($word));
$statement->bindValue(':fourthKeywordDB', $word, getInputParameterDataType($word));
$statement->bindValue(':fithKeywordDB', $word, getInputParameterDataType($word));
$statement->bindValue(':rectLong1', $rectLong1, getInputParameterDataType($rectLong1));
$statement->bindValue(':rectLong2', $rectLong2, getInputParameterDataType($rectLong2));
$statement->bindValue(':rectLat1', $rectLat1, getInputParameterDataType($rectLat1));
$statement->bindValue(':rectLat2', $rectLat2, getInputParameterDataType($rectLat2));
$statement->bindValue(':distance', $radius, getInputParameterDataType($radius));
$statement->bindValue(':limit', $limit, getInputParameterDataType($limit));
// Execute and check PDO statement.
if (!$statement->execute()) {
throw new Exception('The PDO statement can not be executed!');
}
// Fetch person details.
$fetchedData = $statement->fetchAll(PDO::FETCH_ASSOC);
if (!$fetchedData) {
throw new Exception('Fetching data failed!');
}
return $fetchedData;
}
<?php
/*
* ----------------
* Database configs
* ----------------
*/
define('MYSQL_HOST', '...');
define('MYSQL_PORT', '3306');
define('MYSQL_DATABASE', '...');
define('MYSQL_CHARSET', 'utf8');
define('MYSQL_USERNAME', '...');
define('MYSQL_PASSWORD', '...');
<?php
/*
* ---------------------
* Data access functions
* ---------------------
*/
/**
* Create a new db connection.
*
* @param string $host Host.
* @param string $dbname Database name.
* @param string $username Username.
* @param string $password Password.
* @param string $port [optional] Port.
* @param array $charset [optional] Character set.
* @param array $options [optional] Driver options.
* @return PDO Db connection.
*/
function createConnection($host, $dbname, $username, $password, $port = '3306', $charset = 'utf8', $options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_PERSISTENT => true,
)) {
$dsn = getDsn($host, $dbname, $port, $charset);
$connection = new PDO($dsn, $username, $password);
foreach ($options as $key => $value) {
$connection->setAttribute($key, $value);
}
return $connection;
}
/**
* Create a mysql DSN string.
*
* @param string $host Host.
* @param string $dbname Database name.
* @param string $port [optional] Port.
* @param array $charset [optional] Character set.
* @return string DSN string.
*/
function getDsn($host, $dbname, $port = '3306', $charset = 'utf8') {
$dsn = sprintf('mysql:host=%s;port=%s;dbname=%s;charset=%s'
, $host
, $port
, $dbname
, $charset
);
return $dsn;
}
/**
* Close a db connection.
*
* @param PDO $connection Db connection.
* @return void
*/
function closeConnection($connection) {
$connection = NULL;
}
/**
* Get the data type of a binding value.
*
* @param mixed $value Binding value.
* @return mixed Data type of the binding value.
*/
function getInputParameterDataType($value) {
$dataType = PDO::PARAM_STR;
if (is_int($value)) {
$dataType = PDO::PARAM_INT;
} elseif (is_bool($value)) {
$dataType = PDO::PARAM_BOOL;
}
return $dataType;
}
/*
* ---------------
* Print functions
* ---------------
*/
/**
* Print data on screen.
*
* @param mixed $data Data to print.
* @param bool $preformatted Print preformatted if TRUE, print normal otherwise.
* @return void
*/
function printData($data, $preformatted = FALSE) {
if ($preformatted) {
echo '<pre>' . print_r($data, true) . '</pre>';
} else {
echo $data;
}
}
/*
* -------------------------
* Error reporting functions
* -------------------------
*/
/**
* Toggle error reporting.
*
* @param integer $level Error level.
* @param bool $display_errors Display errors if TRUE, hide them otherwise.
* @return void
*/
function activateErrorReporting($level = E_ALL, $display_errors = TRUE) {
error_reporting($level);
ini_set('display_errors', ($display_errors ? 1 : 0));
}
像这样申请qoute()
:
function getCardsInRadius($connection, ...) {
$longitude = $connection->quote($longitude);
$latitude = $connection->quote($latitude);
//...
}
在原始代码中,您使用了cos(radians($latitude))*69
:
...
between ($longitude-$miles/cos(radians($latitude))*69)
and ($longitude+$miles/cos(radians($latitude))*69)
...
在我的代码中,我使用了abs(cos(deg2rad($latitude)) * 69)
。我记得我故意选择了这个:
$rectLong1 = $longitude - $radius / abs(cos(deg2rad($latitude)) * 69);
$rectLong2 = $longitude + $radius / abs(cos(deg2rad($latitude)) * 69);
似乎这可能是问题所在。首先,将deg2rad
替换为radians
。它应该是:
$rectLong1 = $longitude - $radius / abs(cos(radians($latitude)) * 69);
$rectLong2 = $longitude + $radius / abs(cos(radians($latitude)) * 69);
如果仍然无效,请删除abs
。它应该是:
$rectLong1 = $longitude - $radius / (cos(radians($latitude)) * 69);
$rectLong2 = $longitude + $radius / (cos(radians($latitude)) * 69);
注意括号位置。
应用变量测量单位(公里,英里等)作为功能参数,并相应地改变地球半径。如何将$measurementUnit
注入函数是你的一部分。是的,地球半径差不多是3959英里。
function getCardsInRadius($connection, $word, $longitude, $latitude, $radius, $limit = 100, $measurementUnit = 'miles') {
//...
switch ($measurementUnit) {
case 'miles':
$earthRadius = 3959;
break;
case 'km':
$earthRadius = 6371;
break;
default: // miles
$earthRadius = 3959;
break;
}
$distance = sprintf('%s * 2 * ...'
, $earthRadius
, $latitude
, $latitude
, $longitude
);
//...
}