Laravel中如何校验匹配数据并仅插入新数据?
我从API获取商品数据,需要和数据库中ProductZoovu表的现有记录匹配,只插入新数据,但当前代码会插入所有数据,包括已存在的。以下是我的代码:
$retailer_id = ZoovuRetailer::where('name', $name)->first(); if ($retailer_id) { $get_api = ZoovuRetailerUrl::where('zoovu_retailer_id', $retailer_id->id)->first()->uri; $history = $retailer_id->setFeedStart(FeedHistory::TASK_ZOOVU); $client = new Client(['exceptions' => false]); $response = $client->request('GET', $get_api); if ($get_api) { try { $data = json_decode($response->getBody(), true); $brands = WcBrand::all()->pluck('brand_name')->toArray(); $existing_products = ProductZoovu::where('zoovu_retailer_id', $retailer_id->id) ->pluck('offer_product_id') ->toArray(); // Loop through the API data and insert new products foreach ($data as $item) { // Check if the brand is in the list of allowed brands if (in_array($item['brand'], $brands)) { // Check if the product ID already exists in the database if (!in_array($item['id'], $existing_products)) { DB::beginTransaction(); try { // Insert the new product ProductZoovu::create([ 'zoovu_retailer_id' => $retailer_id->id, 'model' => $item['variants'][0]['sku'], 'msrp' => round($item['variants'][0]['price'], 2), 'offer_url_en' => $item['url'], 'offer_product_id' => $item['id'], ]); DB::commit(); } catch (\Exception $exception) { DB::rollback(); dd($exception); } } } } } catch (\Exception $exception) { $errorHandlingService = ErrorHandlingService::class; $errorHandlingService->handleRecordFeedHistory($exception, $history); } $retailer_id->setFeedEnd($history); } }
排查与解决方案
1. 检查ID字段的数据类型匹配
数据库中offer_product_id字段的类型(如字符串/整数)与API返回的$item['id']类型可能不一致,导致in_array匹配失败。比如数据库存的是字符串,API返回的是整数,默认严格比较会判定为不相等。
可以强制统一类型:
// 将数据库查询到的ID转为字符串数组 $existing_products = ProductZoovu::where('zoovu_retailer_id', $retailer_id->id) ->pluck('offer_product_id') ->map(fn($id) => (string)$id) ->toArray(); // 循环中也将API返回的ID转为相同类型 $itemId = (string)$item['id']; if (!in_array($itemId, $existing_products)) { // 插入逻辑 }
2. 确认API返回的ID字段正确性
检查API返回的$item['id']是否为商品的唯一标识,是否存在字段名错误(比如应该用$item['product_id'])。可以用dd($data)打印API返回的完整结构,确认唯一ID的键名。
3. 验证existing_products的查询结果
确认ProductZoovu::where('zoovu_retailer_id', $retailer_id->id)是否正确过滤了当前零售商的商品。比如是否字段名写错(如zoovu_retailer_id应为retailer_id),或者$retailer_id->id的值不正确。可以用dd($existing_products)查看查询结果,是否包含已存在的商品ID。
4. 排查品牌过滤逻辑的干扰
品牌名可能存在大小写、空格差异(比如API返回"Nike",而$brands里是"nike"),导致品牌过滤失效,所有商品都进入插入逻辑。可以统一转为小写后再比较:
$brands = WcBrand::all()->pluck('brand_name')->map(fn($b) => strtolower($b))->toArray(); // 循环中 if (in_array(strtolower($item['brand']), $brands)) { // ... }
5. 改用数据库层面的唯一约束与firstOrCreate
为彻底避免重复插入,建议给ProductZoovu表添加(zoovu_retailer_id, offer_product_id)联合唯一索引,同时用Laravel的firstOrCreate方法简化逻辑,该方法会自动处理查询与插入,比手动数组检查更可靠:
// 循环中替换原有的插入逻辑 ProductZoovu::firstOrCreate( // 查询条件:唯一标识组合 ['zoovu_retailer_id' => $retailer_id->id, 'offer_product_id' => $item['id']], // 新记录的其他字段 [ 'model' => $item['variants'][0]['sku'], 'msrp' => round($item['variants'][0]['price'], 2), 'offer_url_en' => $item['url'], ] );
这种方式无需手动查询existing_products,还能避免并发场景下的重复插入问题。
内容的提问来源于stack exchange,提问作者SCloud

