craue / geo-bundle
用于在您的 Symfony 项目中计算地理距离的 Doctrine 函数。
Requires
- php: ^7.3|^8
- doctrine/doctrine-bundle: ^1.6.12|^2
- doctrine/orm: ^2.5.2
- symfony/config: ~4.4|~5.3|^6
- symfony/dependency-injection: ~4.4|~5.3|^6
- symfony/framework-bundle: ~4.4|~5.3|^6
Requires (Dev)
- doctrine/annotations: ^1.6
- doctrine/data-fixtures: ^1.0.1
- doctrine/dbal: ^2.7|^3
- doctrine/doctrine-fixtures-bundle: ^2.4.1|^3.1
- phpunit/phpunit: ^9.5
- symfony/browser-kit: ~4.4|~5.3|^6
- symfony/phpunit-bridge: ^6
- symfony/yaml: ~4.4|~5.3|^6
Suggests
README
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
- 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 }
- 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 }
- 然后,加载该组的 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
:PostgreSQLnone
:如果你希望手动注册,则阻止注册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