Pārlūkot izejas kodu

add SphericDistance AST

Olivier Massot 3 gadi atpakaļ
vecāks
revīzija
167943e9ce
2 mainītis faili ar 123 papildinājumiem un 0 dzēšanām
  1. 40 0
      src/Commands/TestCommand.php
  2. 83 0
      src/Doctrine/ORM/AST/SphericDistance.php

+ 40 - 0
src/Commands/TestCommand.php

@@ -0,0 +1,40 @@
+<?php /** @noinspection PhpUnused */
+
+namespace App\Commands\Doctrine;
+
+use App\Service\Utils\Path;
+use Doctrine\Bundle\DoctrineBundle\Command\Proxy\UpdateSchemaDoctrineCommand;
+use Doctrine\ORM\Tools\SchemaTool;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Style\SymfonyStyle;
+
+/**
+ * Overrides the default doctrine:schema:update command
+ */
+class SchemaUpdateCommand extends UpdateSchemaDoctrineCommand
+{
+    protected function executeSchemaCommand(InputInterface $input, OutputInterface $output, SchemaTool $schemaTool, array $metadatas, SymfonyStyle $ui)
+    {
+        $output->writeln('-- Executing pre-update scripts');
+
+        // Lists schema extensions scripts in the '/sql/schema-extensions' dir
+        $schemaExtensionsDir = Path::join(Path::getProjectDir(), 'sql', 'schema-extensions');
+        $scripts = Path::list($schemaExtensionsDir, '*.sql');
+        sort($scripts);
+
+        // Execute those scripts in alphabetical order
+        $em = $this->getEntityManager($input);
+        $conn = $em->getConnection();
+
+        foreach ($scripts as $script) {
+            $sql = Path::read($script);
+            $conn->executeQuery($sql);
+        }
+
+        $output->writeln(sprintf('-- Database successfully updated, %s scripts executed', count($scripts)));
+
+        throw new \RuntimeException('<!> Operation interrupted: use the d:s:u command on the current version to update the DB tables');
+//        parent::executeSchemaCommand($input, $output, $schemaTool, $metadatas, $ui);
+    }
+}

+ 83 - 0
src/Doctrine/ORM/AST/SphericDistance.php

@@ -0,0 +1,83 @@
+<?php
+
+namespace App\Doctrine\ORM\AST;
+
+use Doctrine\ORM\Query\AST\ASTException;
+use Doctrine\ORM\Query\AST\ComparisonExpression;
+use Doctrine\ORM\Query\AST\Node;
+use Doctrine\ORM\Query\Lexer;
+use Doctrine\ORM\Query\Parser;
+use Doctrine\ORM\Query\QueryException;
+use Doctrine\ORM\Query\SqlWalker;
+
+/**
+ * Calcules la distance en km à vol d'oiseau entre les coordonnées géographiques données (latitude + longitude) de deux points.
+ *
+ * Implémentation de la formule de Haversine, dont la précision est de l'ordre de la dizaine de mètres dans les cas les plus courants.
+ *
+ * Pour utiliser la fonction :
+ *
+ *     SPHERICAL_DISTANCE(latitude1, longitude1, latitude2, longitude2)
+ *
+ * @see https://fr.wikipedia.org/wiki/Coordonn%C3%A9es_sph%C3%A9riques
+ * @see https://fr.wikipedia.org/wiki/Formule_de_haversine
+ */
+class SphericDistance
+{
+    private Node | string $latitude1;
+    private Node | string $longitude1;
+    private Node | string $latitude2;
+    private Node | string $longitude2;
+
+    /**
+     * Parse DQL Function
+     *
+     * @param Parser $parser
+     * @throws QueryException
+     */
+    public function parse(Parser $parser): void
+    {
+        $parser->match(Lexer::T_IDENTIFIER);
+        $parser->match(Lexer::T_OPEN_PARENTHESIS);
+        $this->latitude1 = $parser->ArithmeticPrimary();
+        $parser->match(Lexer::T_COMMA);
+        $this->longitude1 = $parser->ArithmeticPrimary();
+        $parser->match(Lexer::T_COMMA);
+        $this->latitude2 = $parser->ArithmeticPrimary();
+        $parser->match(Lexer::T_COMMA);
+        $this->longitude2 = $parser->ArithmeticPrimary();
+        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
+    }
+
+    /**
+     * Get SQL
+     *
+     * @param SqlWalker $sqlWalker
+     * @return string
+     * @throws ASTException
+     */
+    public function getSql(SqlWalker $sqlWalker): string
+    {
+        $R = 6371;  // Rayon terrestre, en km
+
+        $rLat1 = '({lat1} * PI() / 180)'; // Latitude 1, en radians
+        $rLon1 = '({lon1} * PI() / 180)'; // Longitude 1, en radians
+        $rLat2 = '({lat2} * PI() / 180)'; // Latitude 2, en radians
+        $rLon2 = '({lon2} * PI() / 180)'; // Longitude 2, en radians
+
+        $sql = sprintf(
+            '2 * %1$s * ASIN(SQRT(POW(SIN((%4$s - %2$s) / 2), 2) + COS(%2$s) * COS(%4$s) * POW(SIN((%5$s - %3$s) / 2), 2)))',
+            $R, $rLat1, $rLon1, $rLat2, $rLon2
+        );
+
+        $sql = str_replace(array('{lat1}', '{lon1}', '{lat2}', '{lon2}'), array('(%1$s)', '(%2$s)', '(%3$s)', '(%4$s)'), $sql);
+
+        return sprintf(
+            $sql,
+            $this->latitude1->dispatch($sqlWalker),
+            $this->longitude1->dispatch($sqlWalker),
+            $this->latitude2->dispatch($sqlWalker),
+            $this->longitude2->dispatch($sqlWalker)
+        );
+    }
+}