Laravel+Livewire项目二次提交表单出现Undefined array key 'name'错误
Laravel Livewire爬虫项目二次搜索报错「Undefined array key 'name'」解决方案
问题背景
基于Laravel和Livewire开发的电商商品爬虫项目,首次搜索可正常爬取商品并生成Product对象数组;二次提交搜索表单时,抛出「Undefined array key 'name'」错误。
核心原因
- Product模型设计问题:模型继承了Eloquent的
Model类,但重写__construct方法时未调用父类构造逻辑,且未处理属性数组为空的情况。Livewire在组件刷新(二次搜索)时会尝试重新实例化模型,此时若属性缺失会触发数组键不存在的错误。 - 爬虫逻辑无异常处理:DomCrawler找不到指定选择器元素时会直接抛出异常,导致
$name等变量未定义,创建Product时传入无效参数。
修复步骤
1. 修复Product模型
根据需求选择两种方案之一:
方案A:改为普通数据类(无需Eloquent功能)
如果该模型不需要与数据库交互,直接去掉Eloquent继承,同时添加属性默认值处理:
<?php namespace App\Models; class Product { public $name; public $price; public $storeId; public $img; public $discount; public $discountText; public function __construct(array $attributes = []){ // 使用null合并运算符处理属性缺失情况 $this->name = $attributes["name"] ?? null; $this->price = $attributes["price"] ?? null; $this->storeId = $attributes["storeId"] ?? null; $this->img = $attributes["img"] ?? null; $this->discount = $attributes["discount"] ?? 'No'; $this->discountText = $attributes["discountText"] ?? 'None'; } }
方案B:保留Eloquent模型(需数据库交互)
删除自定义的__construct方法,使用Eloquent内置的属性赋值逻辑,同时设置可填充字段:
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Product extends Model { use HasFactory; // 设置允许批量赋值的字段 protected $fillable = ['name', 'price', 'storeId', 'img', 'discount', 'discountText']; }
2. 完善爬虫逻辑的异常处理
在遍历商品元素时添加try-catch块,同时检查每个选择器是否存在元素,避免单个商品爬取失败导致整个搜索崩溃:
public function search(){ $this->searched = false; $prods = []; $coto = []; $browser = new HttpBrowser(HttpClient::create()); // 请求目标页面,对搜索参数做URL编码处理 $crawler = $browser->request('GET', 'https://www.cotodigital3.com.ar/sitios/cdigi/browse?_dyncharset=utf-8&Dy=1&Ntt='.urlencode($this->product).'&Nty=1&Ntk=&siteScope=ok&_D%3AsiteScope=+&atg_store_searchInput='.urlencode($this->product).'&idSucursal=200&_D%3AidSucursal=+&search=Ir&_D%3Asearch=+&_DARGS=%2Fsitios%2Fcartridges%2FSearchBox%2FSearchBox.jsp'); // 收集所有商品节点 foreach($crawler->filter("[class='clearfix']") as $prod){ array_push($prods, $prod); } foreach($crawler->filter("[class='clearfix first']") as $prod){ array_push($prods, $prod); } foreach($crawler->filter("[class='clearfix first_max']") as $prod){ array_push($prods, $prod); } foreach($prods as $prod){ try { $crawlerProd = new Crawler($prod); // 检查必要元素是否存在,不存在则跳过当前商品 if(!$crawlerProd->filter("[class='descrip_full']")->count()) continue; if(!$crawlerProd->filter("[class='atg_store_newPrice']")->count()) continue; if(!$crawlerProd->filter("[class='atg_store_productImage'] > img")->count()) continue; // 提取商品信息 $name = $crawlerProd->filter("[class='descrip_full']")->text(); $price = $crawlerProd->filter("[class='atg_store_newPrice']")->text(); $img = $crawlerProd->filter("[class='atg_store_productImage'] > img")->attr('src'); // 处理折扣信息,默认无折扣 $discountText = 'None'; $discount = 'No'; if($crawlerProd->filter("[class='info_discount']")->count()){ $discountText = $crawlerProd->filter("[class='info_discount']")->children()->text(); if($discountText != $price){ $discount = 'Yes'; $discountText = strstr($discountText, 'OFERTA'); } } // 创建Product实例并加入结果数组 $newProd = new Product([ "name"=> $name, "price"=> $price, "storeId" => 'coto', "img" => $img, "discount" => $discount, "discountText" => $discountText ]); array_push($coto, $newProd); } catch (\Exception $e) { // 可添加日志记录,跳过当前异常商品 continue; } } $this->productsAnswer = $coto; $this->searched = true; }
额外优化建议
- Livewire组件中,将
public $productsAnswer = array();改为public $productsAnswer = [];,符合PHP现代语法规范。
内容的提问来源于stack exchange,提问作者Franco Lago
相关产品推荐
相关产品推荐

