Skip to content

Instantly share code, notes, and snippets.

@vaibhavpandeyvpz
Created January 26, 2020 08:05
Show Gist options
  • Save vaibhavpandeyvpz/51809760061829db94a652c68e3ec8e6 to your computer and use it in GitHub Desktop.
Save vaibhavpandeyvpz/51809760061829db94a652c68e3ec8e6 to your computer and use it in GitHub Desktop.
Find nearby records using latitude & longitude (to get distance) in Symfony PHP + Doctrine ORM
{
"require": {
...
"beberlei/doctrineextensions": "^1.2",
...
},
}
<?php
// src/DoctrineExtensions/Query/Mysql/Distance.php
namespace App\DoctrineExtensions\Query\Mysql;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
class Distance extends FunctionNode
{
private $sourceLatitude = null;
private $sourceLongitude = null;
private $destinationLatitude = null;
private $destinationLongitude = null;
/**
* {@inheritdoc}
*/
public function getSql(SqlWalker $walker)
{
$formulae = '(ACOS(SIN(%s * PI() / 180) * SIN(%s * PI() / 180) + COS(%s * PI() / 180) * COS(%s * PI() / 180) * COS((%s - %s) * PI() / 180)) * 180 / PI()) * 60 * 1.1515';
return sprintf(
$formulae,
$this->destinationLatitude->dispatch($walker),
$this->sourceLatitude->dispatch($walker),
$this->destinationLatitude->dispatch($walker),
$this->sourceLatitude->dispatch($walker),
$this->destinationLongitude->dispatch($walker),
$this->sourceLongitude->dispatch($walker)
);
}
/**
* {@inheritdoc}
*/
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->sourceLatitude = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_COMMA);
$this->sourceLongitude = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_COMMA);
$this->destinationLatitude = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_COMMA);
$this->destinationLongitude = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
# config/packages/doctrine.yaml
doctrine:
...
orm:
...
dql:
numeric_functions:
# beberlei/doctrineextensions
acos: DoctrineExtensions\Query\Mysql\Acos
cos: DoctrineExtensions\Query\Mysql\Cos
pi: DoctrineExtensions\Query\Mysql\Pi
round: DoctrineExtensions\Query\Mysql\Round
sin: DoctrineExtensions\Query\Mysql\Sin
# our custom function
distance: App\DoctrineExtensions\Query\Mysql\Distance
...
<?php
// src/Repository/FriendRepository.php
...
class FriendRepository extends ServiceEntityRepository
{
...
public function findNearby(float $latitude, float $longitude, int $distance, ?int $count = null): array
{
// you may omit * 1.609344 to get distance in miles (instead of KMs)
// you may omit ROUND(..., 1) for precise distance
return $this->createQueryBuilder('f')
->addSelect('ROUND(DISTANCE(:latitude, :longitude, f.latitude, f.longitude) * 1.609344, 1) as HIDDEN distance')
->andHaving('distance <= :distance')
->addOrderBy('distance')
->setMaxResults($count)
->setParameters(compact('latitude', 'longitude', 'distance'))
->getQuery()
->getResult()
;
}
...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment