查看: 1003|回复: 0

mysql数据库里保存了商店信息,用thinkphp5写一个为用户按距离远近推荐附近商店

[复制链接]

748

主题

39

回帖

3907

积分

管理员

总舵主

积分
3907
QQ
发表于 2024-1-21 12:40:32 | 显示全部楼层 |阅读模式


要实现按距离从近到远推荐附近的商店,你可以使用经纬度坐标来计算距离,并利用数据库中保存的商店信息进行排序和推荐。以下是一个简单的思路,使用ThinkPHP 5框架:
  • 确保你的商店表中包含了商店的经纬度信息。
  • 在你的控制器中,创建一个方法用于处理推荐商店的逻辑。例如:


  1. namespace app\controller;

  2. use think\Controller;
  3. use app\model\Store; // 假设你有一个Store模型用于访问商店数据

  4. class ShopController extends Controller
  5. {
  6.     public function recommendShops($userLatitude, $userLongitude)
  7.     {
  8.         // 获取用户当前位置的经纬度坐标

  9.         // 查询数据库,获取所有商店的经纬度坐标
  10.         $allShops = Store::field('id, name, latitude, longitude')->select();

  11.         // 计算每个商店与用户位置的距离
  12.         foreach ($allShops as $shop) {
  13.             $shop->distance = $this->calculateDistance($userLatitude, $userLongitude, $shop->latitude, $shop->longitude);
  14.         }

  15.         // 根据距离排序商店列表
  16.         $sortedShops = $allShops->sortBy('distance');

  17.         // 返回排序后的商店列表
  18.         return $this->json(['code' => 200, 'data' => $sortedShops]);
  19.     }

  20.     private function calculateDistance($lat1, $lon1, $lat2, $lon2)
  21.     {
  22.         // 实现计算两个经纬度坐标之间距离的函数,可以使用 Haversine 公式等方法
  23.         // 返回距离值
  24.     }
  25. }
复制代码
请注意,上述代码仅为示例,并未包含实际的距离计算方法。你需要根据你的具体情况选择适当的距离计算算法,例如 Haversine 公式。同时,请根据你的项目结构和数据库表结构调整代码。

问:这种方式会导致分页时距离问题
确实,在实际应用中,如果要进行分页,那么在数据库中每次查询都需要计算距离可能会导致性能问题。为了解决这个问题,可以采用以下优化方式:
  • 使用数据库的地理空间函数: 许多数据库系统都提供了专门用于处理地理空间数据的函数。例如,MySQL 有 ST_DISTANCE 函数,可以用来计算两点之间的距离。你可以使用数据库的地理空间功能,而不是在应用层计算距离。
  • 分页时只查询所需数据: 在分页时,不要一次性查询所有商店的信息。而是通过合理的 SQL 查询语句只获取当前页面所需的商店信息,以减轻数据库负担。

下面是一个使用 MySQL 的 ST_DISTANCE 函数和分页查询的例子:
  1. namespace app\controller;

  2. use think\Controller;
  3. use app\model\Store;

  4. class ShopController extends Controller
  5. {
  6.     public function recommendShops($userLatitude, $userLongitude, $page = 1, $pageSize = 10)
  7.     {
  8.         // 使用数据库的地理空间函数计算距离并分页查询
  9.         $result = Store::field('id, name, latitude, longitude, ST_DISTANCE(point(longitude, latitude), point(?, ?)) as distance', [$userLongitude, $userLatitude])
  10.             ->order('distance')
  11.             ->page($page, $pageSize)
  12.             ->select();

  13.         return $this->json(['code' => 200, 'data' => $result]);
  14.     }
  15. }
复制代码
请注意,上述代码中使用了 MySQL 的 ST_DISTANCE 函数。如果你使用的是其他数据库,可能需要相应的函数或方法。此外,具体的 SQL 查询语句可能需要根据你的数据库结构进行调整。
问:如果不使用ST_DISTANCE函数,还有其他方案吗
如果你的数据库不支持或不使用地理空间函数,你仍然可以通过计算经纬度之间的直线距离来实现。以下是另一种方法,使用纯粹的 SQL 计算距离的例子,同时进行分页:
  1. namespace app\controller;

  2. use think\Controller;
  3. use app\model\Store;

  4. class ShopController extends Controller
  5. {
  6.     public function recommendShops($userLatitude, $userLongitude, $page = 1, $pageSize = 10)
  7.     {
  8.         // 计算距离并分页查询
  9.         $result = Store::field('id, name, latitude, longitude,
  10.             ACOS(SIN(RADIANS(latitude)) * SIN(RADIANS(?)) + COS(RADIANS(latitude)) * COS(RADIANS(?)) * COS(RADIANS(longitude - ?))) * 6371 AS distance', [$userLatitude, $userLatitude, $userLongitude])
  11.             ->order('distance')
  12.             ->page($page, $pageSize)
  13.             ->select();

  14.         return $this->json(['code' => 200, 'data' => $result]);
  15.     }
  16. }
复制代码
这里使用了 Haversine 公式,它通过纬度和经度的差异来计算两点之间的直线距离。请注意,这种方法可能在大规模数据集上性能较差,因此在实际应用中可能需要进一步优化。
在实际使用中,还可以考虑使用缓存来存储已计算的距离,以减轻数据库负担。例如,可以使用 Redis 缓存已计算的距离,以避免在每次查询时都重新计算。
问:根据你说的可以用Redis缓存已计算的距离,具体怎么实现
使用 Redis 缓存已计算的距离可以减轻数据库负担,提高性能。以下是一个简单的示例,展示如何使用 Redis 缓存已计算的距离:
  • 安装 Redis 扩展: 确保你的项目中已经安装了 Redis 扩展,可以使用 Composer 安装:

  1. composer require predis/predis
复制代码
  • 在配置文件中配置 Redis 连接信息: 打开 config/database.php 文件,配置 Redis 连接信息,确保 Redis 服务器正常运行。
  • 修改控制器方法: 在控制器方法中添加 Redis 缓存逻辑。

  1. namespace app\controller;

  2. use think\Controller;
  3. use think\facade\Cache;
  4. use app\model\Store;

  5. class ShopController extends Controller
  6. {
  7.     public function recommendShops($userLatitude, $userLongitude, $page = 1, $pageSize = 10)
  8.     {
  9.         $cacheKey = "distance_cache_{$userLatitude}_{$userLongitude}";

  10.         // 尝试从缓存中获取已计算的距离
  11.         $result = Cache::get($cacheKey);

  12.         if ($result === false) {
  13.             // 如果缓存中不存在,则进行计算并存入缓存
  14.             $result = Store::field('id, name, latitude, longitude,
  15.                 ACOS(SIN(RADIANS(latitude)) * SIN(RADIANS(?)) + COS(RADIANS(latitude)) * COS(RADIANS(?)) * COS(RADIANS(longitude - ?))) * 6371 AS distance', [$userLatitude, $userLatitude, $userLongitude])
  16.                 ->order('distance')
  17.                 ->page($page, $pageSize)
  18.                 ->select();

  19.             // 缓存结果,有效期可以根据实际需求设置
  20.             Cache::set($cacheKey, $result, 3600); // 缓存一小时
  21.         }

  22.         return $this->json(['code' => 200, 'data' => $result]);
  23.     }
  24. }
复制代码
这个例子中,我使用了 Redis 的 Cache 类来存取缓存。缓存的键名包含用户的经纬度信息,确保每个用户位置都有独立的缓存。
请注意,缓存的有效期 (3600 秒) 应根据你的实际需求进行调整。在这个例子中,我将有效期设置为一小时。
问:在$result语句种除了使用Store::field方法,可以使用where方法吗
是的,你可以使用 where 方法来添加条件,以筛选满足条件的商店数据。在这里,你可以使用 where 方法来添加计算距离的条件。以下是一个示例:
  1. namespace app\controller;

  2. use think\Controller;
  3. use think\facade\Cache;
  4. use app\model\Store;

  5. class ShopController extends Controller
  6. {
  7.     public function recommendShops($userLatitude, $userLongitude, $page = 1, $pageSize = 10)
  8.     {
  9.         $cacheKey = "distance_cache_{$userLatitude}_{$userLongitude}";

  10.         // 尝试从缓存中获取已计算的距离
  11.         $result = Cache::get($cacheKey);

  12.         if ($result === false) {
  13.             // 如果缓存中不存在,则进行计算并存入缓存
  14.             $result = Store::where('1=1') // 添加一个始终成立的条件
  15.                 ->field('id, name, latitude, longitude,
  16.                     ACOS(SIN(RADIANS(latitude)) * SIN(RADIANS(?)) + COS(RADIANS(latitude)) * COS(RADIANS(?)) * COS(RADIANS(longitude - ?))) * 6371 AS distance', [$userLatitude, $userLatitude, $userLongitude])
  17.                 ->order('distance')
  18.                 ->page($page, $pageSize)
  19.                 ->select();

  20.             // 缓存结果,有效期可以根据实际需求设置
  21.             Cache::set($cacheKey, $result, 3600); // 缓存一小时
  22.         }

  23.         return $this->json(['code' => 200, 'data' => $result]);
  24.     }
  25. }
复制代码
在上述例子中,我使用 where('1=1') 添加了一个始终成立的条件,然后在 field 方法中计算距离。你可以根据具体需求使用 where 方法添加其他条件,例如限制距离范围、过滤特定类型的商店等。

道不同,不相为谋!理想要有,万一实现了呢?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注公众号

相关侵权、举报、投诉及建议等,请发 E-mail:2513533699@qq.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.|鲁ICP备19000917号|鲁公网安备37078302000320号

在本版发帖
关注公众号
QQ客服返回顶部