You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中Distinct查询失效:重复获取诊所数据问题

解决Laravel中Google地图定位重复显示诊所的问题

我明白你的问题:当一个诊所的某个地点提供多项服务时,你的查询会返回多条重复的诊所/位置记录(每条服务对应一条),导致前端地图上重复显示同一个诊所标记。这是因为你关联了location_services表后,每条服务都会生成一条独立的数据库记录,而distinct()在这里无法生效——因为每条记录的serviceName等字段是不同的,所以整行记录并不重复。

下面是两种可行的解决方案,你可以根据自己的需求选择:

方案一:通过SQL分组聚合直接去重

这种方式在数据库层面完成分组和服务信息合并,性能更优,适合数据量较大的场景。

修改你的查询逻辑,使用GROUP BY按诊所ID和位置ID分组,同时用GROUP_CONCAT将同一位置的所有服务名称合并成一个字符串:

use Illuminate\Support\Facades\DB;

public function mapService(Request $request) {
    $postdata = $request->all();
    // Start XML file, create parent node
    $dom = new \DOMDocument("1.0");
    $node = $dom->createElement("markers");
    $parnode = $dom->appendChild($node);
    
    $services_id = $postdata['id'] ?? '';
    $apiKey = $postdata['apikey'];
    
    if( $services_id ) {
        $loc_services = Clinic::select(
            // 明确选择需要的字段,避免select(*)带来的冲突
            'clinics.clinicID',
            'clinics.clinicName',
            'clinics.clinicFname',
            'clinics.clinicLname',
            'clinics.clinicAddress',
            'clinics.clinicCity',
            'clinics.clinicPhone',
            'clinics.clinicState',
            'clinics.clinicZip',
            'clinics.clinicEmail',
            'locations.locationID',
            'locations.locationName',
            'locations.locationAddress1',
            'locations.locationCity',
            'locations.locationState',
            'locations.locationZip',
            'locations.locationLat',
            'locations.locationLong',
            'locations.distance',
            // 合并同一位置的所有服务名称,用逗号分隔
            DB::raw('GROUP_CONCAT(DISTINCT services.serviceName SEPARATOR ", ") as serviceNames')
        )
        ->join('locations', 'locations.clinicID', '=', 'clinics.clinicID')
        ->join('location_services', 'location_services.locationID', '=', 'locations.locationID')
        ->join('services', 'services.serviceID', '=', 'location_services.serviceID')
        ->whereIn('services.serviceID',$services_id)
        ->where('clinics.api_key','=',$apiKey)
        // 按诊所和位置分组,确保每个位置只返回一条记录
        ->groupBy('clinics.clinicID', 'locations.locationID')
        ->get();
    } else {
        $loc_services = Clinic::select(
            'clinics.*',
            'locations.*'
        )
        ->join('locations', 'locations.clinicID', '=', 'clinics.clinicID')
        ->where('clinics.api_key','=',$apiKey)
        ->get();
    }
    
    foreach ($loc_services as $loc_service) {
        $node = $dom->createElement("marker");
        $newnode = $parnode->appendChild($node);
        
        // 诊所基础信息
        $newnode->setAttribute("clinicID", $loc_service->clinicID);
        $newnode->setAttribute("clinicName", $loc_service->clinicName);
        $newnode->setAttribute("clinicFname", $loc_service->clinicFname);
        $newnode->setAttribute("clinicLname", $loc_service->clinicLname);
        $newnode->setAttribute("clinicAddress", $loc_service->clinicAddress);
        $newnode->setAttribute("clinicCity", $loc_service->clinicCity);
        $newnode->setAttribute("clinicPhone", $loc_service->clinicPhone);
        $newnode->setAttribute("clinicstate", $loc_service->clinicState);
        $newnode->setAttribute("cliniczip", $loc_service->clinicZip);
        $newnode->setAttribute("clinicemail", $loc_service->clinicEmail);
        
        // 位置信息
        $newnode->setAttribute("id", $loc_service->locationID);
        $newnode->setAttribute("locationName", $loc_service->locationName);
        $newnode->setAttribute("locationAddress1", $loc_service->locationAddress1);
        $newnode->setAttribute("locationCity", $loc_service->locationCity);
        $newnode->setAttribute("locationState", $loc_service->locationState);
        $newnode->setAttribute("locationZip", $loc_service->locationZip);
        $newnode->setAttribute("locationLat", $loc_service->locationLat);
        $newnode->setAttribute("locationLong", $loc_service->locationLong);
        $newnode->setAttribute("distance", $loc_service->distance);
        
        // 合并后的服务信息(仅当有服务筛选时存在)
        if(isset($loc_service->serviceNames)) {
            $newnode->setAttribute("serviceNames", $loc_service->serviceNames);
        }
    }
    
    echo $dom->saveXML();
}

关键修改点:

  1. 替换select('*')为明确的字段列表,避免表字段冲突(比如clinicID在两个表中都存在)
  2. 使用GROUP_CONCAT聚合函数,将同一位置的所有服务名称合并成一个字符串
  3. 按clinicID和locationID分组,确保每个诊所的每个位置只返回一条记录

如果你的MySQL版本支持JSON,可以用JSON_ARRAYAGG替代GROUP_CONCAT,生成JSON数组方便前端处理:

DB::raw('JSON_ARRAYAGG(DISTINCT services.serviceName) as serviceNames')

方案二:通过Laravel集合分组处理

如果你不想修改SQL逻辑,可以用Laravel的集合方法在内存中完成分组和去重,适合数据量较小的场景:

public function mapService(Request $request) {
    $postdata = $request->all();
    // Start XML file, create parent node
    $dom = new \DOMDocument("1.0");
    $node = $dom->createElement("markers");
    $parnode = $dom->appendChild($node);
    
    $services_id = $postdata['id'] ?? '';
    $apiKey = $postdata['apikey'];
    $loc_services = collect();
    
    if( $services_id ) {
        // 先获取所有原始记录
        $rawRecords = Clinic::select('*')
            ->join('locations', 'locations.clinicID', '=', 'clinics.clinicID')
            ->join('location_services', 'location_services.locationID', '=', 'locations.locationID')
            ->join('services', 'services.serviceID', '=', 'location_services.serviceID')
            ->whereIn('services.serviceID',$services_id)
            ->where('clinics.api_key','=',$apiKey)
            ->get();
            
        // 按诊所+位置的组合键分组,合并服务信息
        $loc_services = $rawRecords->groupBy(function($item) {
            return $item->clinicID . '-' . $item->locationID;
        })->map(function($group) {
            $record = $group->first();
            // 提取唯一的服务名称并合并
            $record->serviceNames = $group->pluck('serviceName')->unique()->implode(', ');
            return $record;
        })->values();
    } else {
        $loc_services = Clinic::select('*')
            ->join('locations', 'locations.clinicID', '=', 'clinics.clinicID')
            ->where('clinics.api_key','=',$apiKey)
            ->get();
    }
    
    // 后续循环逻辑和方案一一致,这里省略...
    foreach ($loc_services as $loc_service) {
        // ... 生成XML节点的代码不变
    }
    
    echo $dom->saveXML();
}

关键逻辑:

  1. 先用原始查询获取所有包含重复的记录
  2. 用groupBy方法按诊所ID-位置ID的组合字符串分组
  3. 对每个分组,保留第一条记录,并将分组内的所有服务名称合并成一个字符串

两种方案都能解决重复显示诊所的问题,你可以根据自己的业务场景和数据量选择合适的方式。

内容的提问来源于stack exchange,提问作者Xavier Issac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 19:32:41