craue/geo-bundle

用于在您的 Symfony 项目中计算地理距离的 Doctrine 函数。

安装次数: 309,859

依赖者: 1

建议者: 0

安全: 0

星标: 125

关注者: 10

分支: 16

开放问题: 8

类型:symfony-bundle

1.9.0 2022-01-24 15:10 UTC

This package is auto-updated.

Last update: 2024-08-29 03:47:15 UTC


README

Build Status

CraueGeoBundle 为您的 Symfony 项目提供 Doctrine 函数,允许您在数据库查询中计算地理距离。此包不依赖于任何网络服务,因此一旦运行,它就会继续运行。有两个 Doctrine 函数,返回 km 的距离

  • GEO_DISTANCE 函数需要起始点和目的地的纬度和经度
  • GEO_DISTANCE_BY_POSTAL_CODE 函数需要起始点和目的地的国家 + 邮编

安装

获取包

在 shell 中运行以下命令以让 Composer 下载并安装包:

composer require craue/geo-bundle

启用包

如果您不使用 Symfony Flex,则手动注册包

// in config/bundles.php
return [
	// ...
	Craue\GeoBundle\CraueGeoBundle::class => ['all' => true],
];

或者,对于 Symfony 3.4

// in app/AppKernel.php
public function registerBundles() {
	$bundles = [
		// ...
		new Craue\GeoBundle\CraueGeoBundle(),
	];
	// ...
}

准备用于计算的地理数据的表格

如果您想使用 GEO_DISTANCE_BY_POSTAL_CODE 函数,它依赖于一些必须首先添加到您的数据库中的数据。

创建表

提供的 GeoPostalCode 实体包含地理数据的结构。您可以通过以下方式导入它:

# in a shell
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate

或者

# in a shell
php bin/console doctrine:schema:update

或者您喜欢的任何方式。

导入地理数据

这可能是最烦人的步骤:存储所有邮政编码及其地理位置的数据库。幸运的是,获取这些信息并将其导入数据库并不困难。

访问 http://download.geonames.org/export/zip/ 并下载您需要的国家的存档。让我们以 DE.zip 为例。解压缩包含的 DE.txt 文件,例如到 /tmp/DE.txt

创建一个 fixture 类(在单独的文件夹中,以便只能加载此一个)

// MyCompany/MyBundle/Doctrine/Fixtures/CraueGeo/MyGeonamesPostalCodeData.php
namespace MyCompany\MyBundle\Doctrine\Fixtures\CraueGeo;

use Craue\GeoBundle\Doctrine\Fixtures\GeonamesPostalCodeData;
use Doctrine\Common\Persistence\ObjectManager;

class MyGeonamesPostalCodeData extends GeonamesPostalCodeData {

	public function load(ObjectManager $manager) {
		$this->clearPostalCodesTable($manager);
		$this->addEntries($manager, '/tmp/DE.txt');
	}

}

现在,备份您的数据库!如果出现问题,不要责怪其他人导致数据丢失。然后导入 fixture 并记住使用 --append 参数。

根据您使用的 DoctrineFixturesBundle 版本选择以下步骤。

DoctrineFixturesBundle < 3.0

在给定文件夹中加载 fixture。

# in a shell
php bin/console doctrine:fixtures:load --append --fixtures="src/MyCompany/MyBundle/Doctrine/Fixtures/CraueGeo"
DoctrineFixturesBundle >= 3.1
  1. a) 您首先需要将 fixture 注册为具有您选择的组的服务。
# in app/config/config.yml
services:
  my_geonames_postal_code_data:
    class: MyCompany\MyBundle\Doctrine\Fixtures\CraueGeo\MyGeonamesPostalCodeData
    public: false
    tags:
     - { name: doctrine.fixture.orm, group: my_geo_data }
  1. b) 也可以将特定文件夹中的所有类注册为服务。
# in app/config/config.yml
services:
  MyCompany\MyBundle\Doctrine\Fixtures\CraueGeo\:
    resource: '../../src/MyCompany/MyBundle/Doctrine/Fixtures/CraueGeo/*'
    public: false
    tags:
     - { name: doctrine.fixture.orm, group: my_geo_data }
  1. 然后,加载该组的 fixture。
# in a shell
php bin/console doctrine:fixtures:load --append --group=my_geo_data

就这样。

您也可以使用您有权访问的其他数据源,并编写自定义 fixture 以导入它。

如果导入大量条目时遇到内存不足的问题,请尝试添加 --no-debug 开关以避免记录每个 Doctrine 查询。

用法

假设您有一个包含国家和邮政编码的实体 Poi。现在您希望找到距离给定国家中指定邮政编码 $postalCode 的特定地理距离 $radiusInKm 的所有实体,并按距离排序。

use MyCompany\MyBundle\Entity\Poi;

// example values which could come from a form, remember to validate/sanitize them first
$country = 'DE';
$postalCode = '10115';
$radiusInKm = 10;

// create a query builder
$queryBuilder = $this->getDoctrine()->getEntityManager()->getRepository(Poi::class)->createQueryBuilder('poi');

// build the query
$queryBuilder
	->select('poi, GEO_DISTANCE_BY_POSTAL_CODE(:country, :postalCode, poi.country, poi.postalCode) AS HIDDEN distance')
	->having('distance <= :radius')
	->setParameter('country', $country)
	->setParameter('postalCode', $postalCode)
	->setParameter('radius', $radiusInKm)
	->orderBy('distance')
;

高级内容

使用不同数据库平台的 Doctrine 函数

默认情况下,Doctrine函数会自动注册以用于MySQL。但你可以通过设置一个flavor来告诉bundle你希望使用不同的数据库平台。

# in app/config/config.yml
craue_geo:
  flavor: postgresql

目前支持以下flavor:

  • mysql:MySQL(默认值)
  • postgresql:PostgreSQL
  • none:如果你希望手动注册,则阻止注册Doctrine函数

由于PostgreSQL不支持HAVING子句中的别名,并且还需要在GROUP BY子句中进一步出现poi,你需要调整查询(如上面的使用示例)

$queryBuilder
	->select('poi, GEO_DISTANCE_BY_POSTAL_CODE(:country, :postalCode, poi.country, poi.postalCode) AS HIDDEN distance')
	->having('GEO_DISTANCE_BY_POSTAL_CODE(:country, :postalCode, poi.country, poi.postalCode) <= :radius')
	->setParameter('country', $country)
	->setParameter('postalCode', $postalCode)
	->setParameter('radius', $radiusInKm)
	->groupBy('poi')
	->orderBy('distance')
;

避免创建邮政编码表

如果你希望完全避免注册GeoPostalCode实体(因此避免创建craue_geo_postalcode表),请添加以下内容到你的配置中:

# in app/config/config.yml
craue_geo:
  enable_postal_code_entity: false

到你的配置。

为Doctrine函数使用自定义名称

如果你不喜欢默认名称或需要避免与其他函数冲突,你可以设置自定义名称

# in app/config/config.yml
craue_geo:
  functions:
    geo_distance: MY_VERY_OWN_GEO_DISTANCE_FUNCTION
    geo_distance_by_postal_code: MY_VERY_OWN_GEO_DISTANCE_BY_POSTAL_CODE_FUNCTION