使用 Laravel 開發簡易的附近動態功能

bestcyt發表於2018-03-30

使用laravel開發簡易的附近動態功能

寫在前面

堅持分享,堅持開源,受各位大佬的影響。繼續分享一些好玩的小東西;

簡介

公司app準備做個附近的功能,方便使用者交(y)流(p),然後呢,大概目標就是點選附近,出來一列的使用者動態列表,按時間地點距離排列。

思路

在網上找了一些資料,發現有三種實現方法,mysql,redis,MongoDB;mysql因為用公式來跑select,使用者一多就難受了,所以採用redis來存使用者的地理位置,再用redis的新命令實現計算篩選使用者。想看額外用法的可以檢視這篇文章

備註:測試小樣就不注重寫法了,一梭子,突突突

開始

redis命令解釋中文命令

GEOADD---(將指定的地理空間位置(緯度、經度、名稱)新增到指定的key中),我們用這個命令將使用者發表動態時的位置資訊記錄進redis:key longitude latitude member [longitude latitude member ...]

  • key:區域名或者空間名,我們統一用‘moment’
  • longitude:經度
  • latitude:緯度
  • member:即為redis的key值,我們用moments表的id來代表
  • 更多資訊參考

GEORADIUS---(以給定的經緯度為中心,返回鍵包含的位置元素當中,與中心的距離不超過給定最大距離的所有位置元素),key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]

  • key::區域名或者空間名,我們統一用‘moment’
  • longitude:經度
  • latitude:緯度
  • radius:距離,比如附近1km內
  • km:預設用千米
  • option:多個返回引數用到的有:(WITHDIST->返回這個點和當前使用者的位置的距離,ASC->從近到遠排列)
  • 更多資訊參考
分析圖:

image
圖解:
一個是mysql的表,一個是redis存放地點;利用moments表的主鍵id來做redis的唯一key

準備兩個api:
post-moment:接收app端傳來的使用者當前定位的經緯度引數並生成一條動態  
get-moments:根據當前使用者的經緯度位置獲取附近動態列表
工具及環境準備:
Windows , laravel5.6 , postman

1.新增兩條路由

//往web.php 新增兩個路由
Route::get('/post-moment', 'NearByController@postMoment');
Route::get('/get-moments', 'NearByController@getMoments');

2.建立model檔案

php artisan make:model Moment

3.新增redisLbs檔案

隨便放到help還是什麼目錄就好,namespace可以找到就行
<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018/3/29
 * Time: 14:26
 */

namespace App\Helps\api;

class redisLbs
{
    /** @var Redis */
    private $redis;
    public function __construct($config = array())
    {
        $host = isset($config['host']) ? $config['host'] : '127.0.0.1';
        $port = isset($config['port']) ? $config['port'] : '6379';
        $redis = new \Redis();
        $redis->connect($host, $port);
        if (env('APP_ENV') != 'local'){
           $redis->auth('myRedis');
           $redis ->set( "root" , "myRedis");
        }
        $this->setRedis($redis);
    }
    public function getRedis()
    {
        return $this->redis;
    }
    public function setRedis($redis)
    {
        $this->redis = $redis;
    }

    //新增點
    public function geoAdd($uin, $lon, $lat)
    {
        $redis = $this->getRedis();
        $redis->geoAdd('moments', $lon, $lat, $uin);
        return true;
    }

    //獲取點
    public function geoNearFind($longitude , $latitude , $maxDistance = 0, $unit = 'km')
    {
        $redis = $this->getRedis();
        $options = ['WITHDIST','ASC','WITHCOORD']; //顯示距離
        $list = $redis->geoRadius('moments', $longitude, $latitude , $maxDistance, $unit, $options);
        return $list;
    }

}

4.建立控制器

php artisan make:controller NearByController

<?php

namespace App\Http\Controllers;

use App\Help\redisLbs;
use App\Moment;
use Illuminate\Http\Request;

class NearByController extends Controller
{

    /*
     * 新增新動態:1.mysql插入 2.redis插入
     */
    public function postMoment(Request $request,Moment $moment)
    {
        $user = $this->authenticate($request);//根據根據實際登入使用者
        if (!$request->input('longitude')){
            return $this->failure(0,'缺少經度');
        }
        if (!$request->input('latitude')){
            return $this->failure(0,'缺少緯度');
        }

        //依賴注入,隨時用
        $moment->user_id = $user->id; //根據實際登入使用者
        $moment->longitude = $request->input('longitude');
        $moment->latitude = $request->input('latitude');
        $moment->created_at = time();
        $moment->updated_at = time();

        if ($moment->save()){
            //連線redis加點
            $lbs = new redisLbs();
            $result = $lbs->geoAdd($moment->id, $request->input('longitude'), $request->input('latitude'));
            if ($result){
                return $this->success(1,'新增動態成功',$moment);
            }
            return $this->failure(0,'動態新增成功,redis同步失敗');
        }
        return $this->failure(0,'動態新增失敗');
    }

    /*
     * 獲取附近動態
     */
    public function getMomentsList(Request $request)
    {
        $lbs = new redisLbs();
        $result = $lbs->geoNearFind($request->input('longitude'),$request->input('latitude'),30000); //獲取附近距離3w米的
        /* 返回的兩個座標點,mid為76和12,距離,還有經緯度
        [
            "76",
            "1112.2630",
            [
                "100.19999831914902",
                "39.999999910849162"
            ]
        ],
        [
            "12",
            "2213.4033",
            [
                "100.19999831914902",
                "10.100000747410967"
            ]
        ],
          */
        $mid = $dist=[];
        for($i=0;$i<count($result);$i++){
            $dist[$i]['mid'] = $mid[$i] = $result[$i][0];  //點的id,也就是moment的主鍵
            $dist[$i]['distance'] = $result[$i][1];
        }
        $moments = Moment::where([
            ['created_at','>',date('Y-m-d H:m:i',time()-1800)],
        ])->whereIn('id', $mid)->orderBy('created_at', 'desc')->get()->toArray();

        //根據距離排序動態列表
        $date = array_column($moments, 'distance');
        array_multisort($date,SORT_ASC,$moments);

        return $this->success(1,'成功獲取附近動態',$moments);
    }
}
使用postman測試

新增動態:
image
檢視附近:
image

參考文章:
https://zhuanlan.zhihu.com/p/31380780
http://www.redis.cn/commands.html

具體的程式碼還需具體實現,比如使用者,認證等等,這裡只有相關演示程式碼。大體思路就是醬紫,有更好的思路或想法歡迎大神們分享指教~ :)

相關文章