如何通过用户输入标签索引联系人数组并正确写入数据库?
问题描述
我需要把以下联系人数据表存入数据库:
0 => array:5 [▶] 1 => array:5 [▼ 0 => "John Doe" 1 => "john.doe@example.com" 2 => "15551-234567" 3 => "New York, NY" 4 => "230" ] 2 => array:5 [▼ 0 => "Jane Smith" 1 => "jane.smith@example.com" 2 => "15552345678" 3 => "Los Angeles, CA" 4 => "5201" ] 3 => array:5 [▼ 0 => "Robert Johnson" 1 => "robert.johnson@example.com" 2 => "8.80156E+13" 3 => "Chicago, IL" 4 => "4110" ] 4 => array:5 [▶] 5 => array:5 [▶] 6 => array:5 [▶] 7 => array:5 [▶] 8 => array:5 [▶]
同时有一组标签数组,用来索引上述联系人数据并存入联系人表:
0 => "contact_name" 1 => "Select Schema" 2 => "contact_number" 3 => "Select Schema" 4 => "Select Schema" ]
我写了代码实现索引入库,但只能获取每行数据的第一个有效标签对应的值。比如标签数组包含contact_name、contact_number时,期望$contactToBeInserted格式是:
0 => array:2 [▼ "contact_name" => "John Doe" "contact_number" => "15551-234567" ] 1 => array:2 [▼ "contact_name" => "Jane Smith" "contact_number" => "15552345678" ]
但实际得到的是:
0 => array:1 [▼ "contact_name" => "john doe" ] 1 => array:1 [▼ "contact_name" => "Robert" ]
请问问题出在哪?有没有更高效的方式实现索引并得到预期输出?
我的代码如下:
public function saveReviewData(Request $request) { $allowedTags = ['contact_name','contact_email','contact_number','contact_location']; $contactToBeInserted = []; foreach($request->tag as $tagIndex => $tag) { if (!in_array($tag, $allowedTags)) { continue; } foreach(array_slice($request->data, 1) as $index => $contactData) { $contactToBeInserted[$index][$tag] = $contactData[$tagIndex]; $this->validate($request, [ 'contact_number' => [ Rule::unique('contacts')->where(function ($query) { $query->where('user_id', '=', Auth::user()->id); }) ], ]); $contact = new Contact(); $contact->contact_name = $contactToBeInserted[$index]['contact_name'] ; $contact->contact_email = $contactToBeInserted[$index]['contact_email'] ; $contact->contact_number = preg_replace('/[^0-9]+/', '', $contactToBeInserted[$index]['contact_number']); $contact->contact_location = $contactToBeInserted[$index]['contact_location']; $contact->user_id = Auth::user()->id; $contact->save(); } } return redirect('contacts'); }
问题原因
- 循环逻辑完全搞反了:你把创建并保存
Contact模型的代码放在了标签循环的内层。处理第一个有效标签(比如contact_name)时,就立刻保存了联系人,但此时其他标签(比如contact_number)还没处理,所以每条联系人只会被保存一次,且只带第一个有效标签的数据。 - 验证逻辑错误:在循环里调用
$this->validate()会导致每次处理标签都执行验证,而且验证的是整个请求的contact_number,不是当前行的号码,完全不符合需求。 - 数据覆盖风险:虽然不是主因,但当前逻辑下重复处理同一联系人索引时,可能出现数据覆盖问题。
高效解决方案
正确流程应该是:先把所有联系人数据按标签映射完整,再统一验证、保存。优化后的代码如下:
use Illuminate\Validation\Rule; use App\Models\Contact; public function saveReviewData(Request $request) { $allowedTags = ['contact_name', 'contact_email', 'contact_number', 'contact_location']; $contactDataList = array_slice($request->data, 1); // 跳过第一个空数组 $tags = $request->tag; $userId = Auth::user()->id; $validContacts = []; // 第一步:把所有有效标签映射到对应联系人数据 foreach ($contactDataList as $contactData) { $contact = []; foreach ($tags as $tagIndex => $tag) { if (in_array($tag, $allowedTags) && isset($contactData[$tagIndex])) { $contact[$tag] = $contactData[$tagIndex]; } } // 只保留有有效字段的联系人 if (!empty($contact)) { $validContacts[] = $contact; } } // 第二步:逐条验证并保存 foreach ($validContacts as &$contact) { // 处理电话号码格式 if (isset($contact['contact_number'])) { $contact['contact_number'] = preg_replace('/[^0-9]+/', '', $contact['contact_number']); // 单独验证当前联系人的号码唯一性 $this->validate([ 'contact_number' => $contact['contact_number'] ], [ 'contact_number' => [ 'required', Rule::unique('contacts')->where('user_id', $userId) ] ]); } // 补充用户ID并保存 $contact['user_id'] = $userId; Contact::create($contact); // 需确保Contact模型设置了$fillable属性,包含对应字段 } return redirect('contacts'); }
优化点说明
- 调整循环顺序:先遍历联系人数据,再逐个映射有效标签,确保每条联系人的所有字段都收集完整后再处理。
- 修正验证逻辑:针对每条联系人的电话号码单独验证,保证唯一性检查准确。
- 简化模型创建:使用
Contact::create()批量赋值(需在Contact模型的$fillable中添加contact_name、contact_email、contact_number、contact_location、user_id)。 - 过滤无效数据:跳过没有有效字段的联系人,避免空数据入库。
内容的提问来源于stack exchange,提问作者Hola
相关产品推荐
相关产品推荐

