Laravel 与 Angular 提交数据时 preg_replace() 参数不匹配
结论
问题不在 $fillable,而是 dropdown 绑定了错误的值。
当前的 ng-options 是:
ng-options="payment.name for payment in payment"
页面显示的是 payment.name,但 ng-model="payment_post.name" 绑定的其实是整个 payment 对象。因此提交后,Laravel 收到的 name 是数组,而数据库的 name 字段需要字符串。
Eloquent 在构造 SQL、处理这个数组时,旧版 Laravel 可能会将数组传给 preg_replace() 的 replacement 参数,从而报错:
preg_replace(): Parameter mismatch, pattern is a string while replacement is an array
修正 Angular 绑定
如果数据库要保存支付方式名称,需要明确指定 option 的值为 payment.name:
<select
class="form-control input-lg"
ng-model="payment_post.name"
ng-options="item.name as item.name for item in payments">
<option value="">请选择支付方式</option>
</select>
建议把列表变量改成复数,避免集合和循环变量都使用 payment:
$http.get('/api/payment').then(function (res) {
$scope.payments = res.data;
});
修改后提交的数据类似:
name=Cash
而不是:
name[id]=1&name[name]=Cash
如果 agent 表保存的是支付方式 ID,则应绑定 ID,并改用对应的外键字段:
<select
class="form-control input-lg"
ng-model="payment_post.payment_id"
ng-options="item.id as item.name for item in payments">
<option value="">请选择支付方式</option>
</select>
Controller 也要相应修改:
$agent->payment_id = Input::get('payment_id');
关联记录通常更适合保存 ID,而不是显示名称,具体仍要以实际数据库结构为准。
Laravel 端增加验证
不要把未经验证的输入直接赋给模型。至少要先确认 name 是字符串:
public function store()
{
$validator = Validator::make(Input::all(), [
'fname' => 'required|string',
'lname' => 'required|string',
'mname' => 'nullable|string',
'id_number' => 'required|string',
'name' => 'required|string',
'gender' => 'required|string',
'username' => 'required|string',
]);
if ($validator->fails()) {
return Response::json([
'errors' => $validator->errors(),
], 422);
}
$agent = new Agent();
$agent->fname = Input::get('fname');
$agent->lname = Input::get('lname');
$agent->mname = Input::get('mname');
$agent->id_number = Input::get('id_number');
$agent->name = Input::get('name');
$agent->gender = Input::get('gender');
$agent->username = Input::get('username');
$agent->save();
return Response::json($agent, 201);
}
nullable、string 等验证规则要根据 Laravel 版本调整,较旧版本的写法可能有所不同。
调试时,可以直接查看 Laravel 实际收到的数据:
dd(Input::all(), Input::get('name'));
修正以后,Input::get('name') 应该返回字符串,而不是下面这样的数组:
[
'id' => 1,
'name' => 'Cash',
]
Model 是否需要修改
当前代码是逐个给属性赋值:
$agent->name = Input::get('name');
这种写法不会用到 $fillable,所以它不是本次报错的原因。$fillable 主要用于以下批量赋值操作:
Agent::create($data);
或:
$agent->fill($data);
$fillable 可以保留,但一般不需要把 id、created_at 和 updated_at 放进允许批量赋值的字段中:
class Agent extends \Eloquent
{
protected $table = 'agent';
protected $fillable = [
'fname',
'mname',
'lname',
'username',
'id_number',
'name',
'gender',
];
}
如果 agent 表没有 created_at 和 updated_at 字段,还需要在模型中关闭时间戳:
public $timestamps = false;
否则会出现另一类数据库错误,但它与当前的 preg_replace() 参数不匹配无关。
注意事项
$.param(payment_post) 会把嵌套对象序列化为嵌套的表单参数。它只是按原样提交了 dropdown 绑定的整个对象,本身没有问题。需要修正的是 ng-options 的选择值:
item.name as item.name for item in payments
或者:
item.id as item.name for item in payments
Laravel 端也应验证该字段是否为预期的标量类型,这样就能在数据进入 Eloquent 之前发现同类问题。
备注:内容仅供参考。
