我的个人博客:逐步前行STEP
有时候我们使用Laravel-admin管理数据时,需要保存一些通过程序运算出来的数据,而不只是存储写在表单中的数据,也就是需要在保存数据前可以设置或改变数据。
比如存在这么个需求:
为了快速创建\管理一些测试数据,在Admin管理端创建一个用户账户的同时,创建相关的用户信息,用户订单、地址库等等一系列信息。
以这个需求中用户关联的数据量来说,每个数据都手动输入的话,效率太低了,所以只能自动创建。
所以实现的思路是这样的,表单中只有用户的基本信息,其它的订单、地址都是在执行store函数前自动生成的,然后关联保存即可。
先看Form.php的store函数源码:
public function store()
{
$data = Input::all();
......
}
数据来源是request对象,所以,轻而易举地想到:
在store之前往request中塞入订单、地址的关联数据
这个数据的格式在我的另一篇博文中有详细解释:laravel-admin grid中使用switch操作一对一关联属性,但是执行提交后发现并没有成功保存,于是走一波源码调试。
首先看到更新关联模型数据的代码:
public function store()
{
......
DB::transaction(function () {
$inserts = $this->prepareInsert($this->updates);
foreach ($inserts as $column => $value) {
$this->model->setAttribute($column, $value);
}
$this->model->save();
//在这里保更新关联模型
$this->updateRelation($this->relations);
});
......
}
继续进入updateRelation方法跟踪:
protected function updateRelation($relationsData)
{
foreach ($relationsData as $name => $values) {
if (!method_exists($this->model, $name)) {
continue;
}
$relation = $this->model->$name();
//在这里判定是否是一对一
$oneToOneRelation = $relation instanceof Relations\HasOne
|| $relation instanceof Relations\MorphOne
|| $relation instanceof Relations\BelongsTo;
//在这里做一个预处理
$prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
//预处理的结果为空则没有后续处理
if (empty($prepared)) {
continue;
}
经过打断点调试,发现我在store之前插入的订单、地址关联数据并没有通过预处理,所以再来看看prepareUpdate预处理是什么鬼:
protected function prepareUpdate(array $updates, $oneToOneRelation = false)
{
$prepared = [];
/** @var Field $field */
//$this->builder->fields() 就是表单字段相关属性
foreach ($this->builder->fields() as $field) {
$columns = $field->column();//这个是字段名称了
// If column not in input array data, then continue.
if (!array_has($updates, $columns)) {//关键在这,如果表单字段不在request的数据中就过滤掉
continue;
}
......
}
在上面的注释中,清楚地表明了一个状况:
form表单的提交保存只能保存表单中有的字段,不然都会被过滤掉。
所以,我们需要让Laravel-admin认为我们表单中有订单、地址关联字段,根据我们的需求,很容易想到使用hidden组件:
只要把所有关联关系需要更新的字段都使用hidden列出来即可,不需要赋值
而且在store前,如果根据某些条件不需要保存这个关联关系的话,直接使用request的offsetUnset将那个关联关系整体删除即可。