Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Geo Bundle Laravel Package

craue/geo-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require craue/geo-bundle
    

    Enable the bundle in config/bundles.php:

    return [
        // ...
        Craue\GeoBundle\CraueGeoBundle::class => ['all' => true],
    ];
    
  2. Database Setup: Run the migration to create the geo_postal_code table (required for GEO_DISTANCE_BY_POSTAL_CODE):

    php bin/console doctrine:migrations:diff
    php bin/console doctrine:migrations:migrate
    
  3. Populate Postal Data (if using postal codes): Use the provided fixtures or manually insert data into geo_postal_code (e.g., country, postal code, latitude, longitude).

  4. First Query: Add latitude/longitude fields to your entity (e.g., User):

    // src/Entity/User.php
    use Doctrine\ORM\Mapping as ORM;
    
    #[ORM\Entity]
    class User {
        #[ORM\Column(type: 'decimal', precision: 10, scale: 8)]
        private $latitude;
    
        #[ORM\Column(type: 'decimal', precision: 11, scale: 8)]
        private $longitude;
    }
    

    Query distance in DQL:

    $distance = $entityManager->createQuery(
        'SELECT GEO_DISTANCE(u.latitude, u.longitude, :lat, :lng) AS distance FROM App\Entity\User u'
    )->setParameter('lat', 48.8566)
      ->setParameter('lng', 2.3522)
      ->getSingleScalarResult();
    

Implementation Patterns

Core Workflows

  1. Distance Queries:

    • Lat/Lon: Use GEO_DISTANCE(lat1, lon1, lat2, lon2) for direct coordinates.
      $query = $em->createQuery(
          'SELECT u, GEO_DISTANCE(u.latitude, u.longitude, :lat, :lng) AS distance
           FROM App\Entity\User u
           WHERE GEO_DISTANCE(u.latitude, u.longitude, :lat, :lng) < :maxDistance
           ORDER BY distance ASC'
      );
      
    • Postal Codes: Use GEO_DISTANCE_BY_POSTAL_CODE(:country1, :postal1, :country2, :postal2).
      $query = $em->createQuery(
          'SELECT GEO_DISTANCE_BY_POSTAL_CODE(:country, :postal, "FR", "75000") AS distance
           FROM App\Entity\Store'
      );
      
  2. Entity Integration:

    • Add distance methods to entities for reusable logic:
      // src/Entity/User.php
      public function getDistanceTo(User $other): float {
          return $this->getEntityManager()
              ->createQueryBuilder()
              ->select('GEO_DISTANCE(u.latitude, u.longitude, :lat, :lng) AS distance')
              ->setParameter('lat', $other->getLatitude())
              ->setParameter('lng', $other->getLongitude())
              ->getQuery()
              ->getSingleScalarResult();
      }
      
  3. Performance:

    • Indexing: Add spatial indexes to latitude/longitude columns for large datasets:
      CREATE INDEX idx_user_geo ON user (latitude, longitude);
      
    • Caching: Cache frequent postal code lookups (e.g., in Redis) if using GEO_DISTANCE_BY_POSTAL_CODE.
  4. Symfony Forms:

    • Use for location-based validation or filtering:
      // src/Form/UserType.php
      $builder->add('latitude', HiddenType::class, [
          'data' => $user->getLatitude(),
      ]);
      $builder->add('longitude', HiddenType::class, [
          'data' => $user->getLongitude(),
      ]);
      
  5. API Endpoints:

    • Expose distance calculations via API:
      // src/Controller/UserController.php
      #[Route('/users/nearby', name: 'users_nearby', methods: ['GET'])]
      public function nearbyUsers(Request $request, EntityManagerInterface $em): JsonResponse {
          $lat = $request->query->get('lat');
          $lng = $request->query->get('lng');
          $distance = $em->createQuery(
              'SELECT u, GEO_DISTANCE(u.latitude, u.longitude, :lat, :lng) AS distance
               FROM App\Entity\User u
               WHERE GEO_DISTANCE(u.latitude, u.longitude, :lat, :lng) < 10
               ORDER BY distance ASC'
          )->setParameter('lat', $lat)
           ->setParameter('lng', $lng)
           ->getResult();
          return $this->json($distance);
      }
      

Gotchas and Tips

Pitfalls

  1. Postal Code Data:

    • Missing Data: GEO_DISTANCE_BY_POSTAL_CODE requires pre-populated geo_postal_code table. Without it, queries fail silently or return NULL.
  2. Precision Issues:

    • Latitude/longitude stored as decimal(10,8) may lose precision for very small distances (e.g., <1m). Adjust precision if needed:
      #[ORM\Column(type: 'decimal', precision: 15, scale: 10)]
      private $latitude;
      
  3. Database Compatibility:

    • MySQL: Uses GEOMETRY functions under the hood. Ensure your MySQL version supports it (5.7+ recommended).
    • PostgreSQL: Works out-of-the-box but may require earthdistance() for better accuracy (not supported by this bundle).
  4. Performance:

    • N+1 Queries: Avoid fetching entities without distance in the same query. Use JOIN or SELECT only the needed fields:
      // Bad: Loads all user data
      $query = $em->createQuery('SELECT u FROM App\Entity\User u WHERE GEO_DISTANCE(...) < 10');
      
      // Good: Loads only IDs/distance
      $query = $em->createQuery('SELECT u.id, GEO_DISTANCE(...) AS distance FROM App\Entity\User u WHERE distance < 10');
      
  5. Time Zones:

    • Postal code data may include time zone offsets. Ensure your application handles them consistently (e.g., store UTC coordinates).

Debugging

  1. Query Logging: Enable Doctrine logging to debug DQL:

    // config/packages/dev/doctrine.yaml
    doctrine:
        dbal:
            logging: true
            profiling: true
    

    Check logs for malformed queries (e.g., missing parameters).

  2. Postal Code Lookup: Verify postal code data exists:

    $postal = $em->getRepository(Craue\GeoBundle\Entity\GeoPostalCode::class)
        ->findOneBy(['country' => 'FR', 'postalCode' => '75000']);
    
  3. Distance Units: The bundle returns distances in kilometers. Multiply by 0.621371 for miles if needed.

Tips

  1. Bulk Updates: Use a script to update latitude/longitude for existing records (e.g., via Google Maps API):

    // src/Command/UpdateGeoData.php
    use Symfony\Component\Console\Command\Command;
    use Symfony\Component\Console\Input\InputInterface;
    use Symfony\Component\Console\Output\OutputInterface;
    use Symfony\Component\Console\Style\SymfonyStyle;
    
    class UpdateGeoData extends Command {
        protected function execute(InputInterface $input, OutputInterface $output): int {
            $io = new SymfonyStyle($input, $output);
            $users = $this->getDoctrine()->getRepository(User::class)->findAll();
            foreach ($users as $user) {
                $address = $user->getAddress();
                $geoData = $this->geocodeAddress($address); // Use a geocoding service
                $user->setLatitude($geoData['lat']);
                $user->setLongitude($geoData['lng']);
                $this->getDoctrine()->getManager()->flush();
                $io->text(sprintf('Updated %s', $user->getEmail()));
            }
            return Command::SUCCESS;
        }
    }
    
  2. Custom Functions: Extend the bundle by adding your own Doctrine functions. Override the Craue\GeoBundle\DependencyInjection\Compiler\GeoFunctionsPass compiler pass:

    # config/packages/craue_geo.yaml
    craue_geo:
        custom_functions:
            GEO_BEARING: 'Craue\GeoBundle\Doctrine\GeoFunctions::bearing'
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai