记一次 Laravel-Admin 的 Debug 过程

缘由

最近在使用Laravel-Admin后台进行开发时,发现了框架的一个Bug。那就是编辑的时候无法回显。然后偶然的机会,我将驼峰和下划线都写上了,最后震惊了。

记一次Laravel-Admin的Debug过程
下划线有值,驼峰的反而没值????emmmmmm

记一次Laravel-Admin的Debug过程

直接使用下划线而不用驼峰就没值,真是头疼呢?

/**
 * Make a form builder.
 *
 * @return Form
 */
 protected function form()
{
        $form = new Form(new Students);
        $form->text('classInfo.grade', '年级');
        $form->text('class_info.grade', '年级');
        $form->text('classInfo.class', '班级');
        $form->text('class_info.class', '班级');
        return $form;
}

classInfo模型

class ClassInfo extends Model
{
    protected $table = 'class_info';
    protected $fillable = ['grade', 'class'];
    public function students()
    {
        return $this->belongsTo(Students::class);
    }
}

students模型

class Students extends Model
{
    protected $table = 'students';
    public function contacts()
    {
        return $this->hasMany(Contacts::class, 'student_id');
    }

    public function classInfo()
    {
        return $this->hasOne(ClassInfo::class, 'student_id');
    }
}

无奈

只能是自己Debug了,因为找了很多文章也没解决方案,项目issue也没有一对一模型出错的文章。我就奇怪了,为什么这么常用的功能有Bug呢?还是只是我在用而已(逃)

首先思路要清晰,毕竟要找问题究竟出在哪里。因为是我是用form组件的text方法,首先就会想到是form排查。所以我找到了src/Form.php这个文件。

在使用Laravel-Admin命令生成的控制器会带上一个修改函数

    /**
     * Edit interface.
     *
     * @param mixed $id
     * @param Content $content
     * @return Content
     */
    public function edit($id, Content $content)
    {
        return $content
            ->header('Edit')
            ->description('description')
            ->body($this->form()->edit($id));
    }

然后我们会看见$this->form()这个函数不就是我们编写字段的函数吗?然后调用了edit函数

    /**
     * Generate a edit form.
     *
     * @param $id
     *
     * @return $this
     */
    public function edit($id)
    {
        $this->builder->setMode(Builder::MODE_EDIT);
        $this->builder->setResourceId($id);
        $this->setFieldValue($id);

        return $this;
    }

这个函数会设置编辑模式,然后设置资源id这些其实都不是我们要管的。毕竟我们是value值没有.那么当我看见setFieldValue函数后就突然兴奋,这不就是设置input输入框的value值吗?

    /**
     * Set all fields value in form.
     *
     * @param $id
     *
     * @return void
     */
    protected function setFieldValue($id)
    {
        $relations = $this->getRelations();
        $builder = $this->model();

        if ($this->isSoftDeletes) {
            $builder = $builder->withTrashed();
        }

        $this->model = $builder->with($relations)->findOrFail($id);

        $this->callEditing();

//        static::doNotSnakeAttributes($this->model);

        $data = $this->model->toArray();

        $this->builder->fields()->each(function (Field $field) use ($data) {
            if (!in_array($field->column(), $this->ignored)) {
                $field->fill($data);
            }
        });
    }

这里面要关注的点还有就是获取依赖的getRelations函数,这个函数其实就是上面模型的函数名称。所以我们的$relations=['classInfo, contacts']

重点来了,我们将$data打印出来看看

array:13 [▼
  "id" => 5
  "name" => "s1"
  "gender" => 0
  "enrol" => 2019
  "birth" => "2019-04-22"
  "address" => "test"
  "avatar" => null
  "status" => 0
  "desc" => "test"
  "created_at" => "2019-04-22 15:42:20"
  "updated_at" => "2019-04-22 15:42:20"
  "class_info" => array:7 [▼
    "id" => 1
    "student_id" => 5
    "grade" => "1"
    "director" => null
    "class" => "2"
    "created_at" => "2019-04-22 15:42:20"
    "updated_at" => "2019-05-20 14:37:40"
  ]
  "contacts" => array:2 [▶]
]

emmmmm,依赖传进去后得到的数据竟然是以表名来做键的呀。

结论

这是因为驼峰和下划线的问题。我在数据库使用下划线class_info,然后我的form函数里面写的是

        $form->text('classInfo.grade', '年级');
        $form->text('classInfo.class', '班级');

然后这个是对应在模型中的函数名的,然后在field->fill填充$data数据进去赋值时,就会发现找不到classInfo,只有class_info。然后这里的解决方案有两种:

  1. 直接将模型函数使用下划线,不用驼峰,那么数据字段就对得上了
  2. 添加代码,将data变量填充classInfo变量进来
        foreach ($this->model->getRelations() as $k => $v) {
            if ($v instanceof Model && isset($data[$v->getTable()])) {
                $data[$k] = $data[$v->getTable()];
            }
        }

以上就是我的Debug思路,可能解决方案有点差,欢迎提出更好的方案一起交流~

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 6
falling-ts

input 标签的 name 属性,用驼峰标记,之前我也遇到过获取不到值的问题。现在我一般在 PHP 中的数组键和数据库字段中使用下划线,PHP 变量使用驼峰法。

4年前 评论

@yuanshang 这个想法不错 :+1:

4年前 评论

issue 里很早就有关于 使用驼峰法命名模型关联 的问题了,作者也回应了(链接),也有很多提PR的,可能改动影响较大,一直没改。

看了你提的PR,作者可能也不会合并

不过,在 v1.6.15 版本后,新增了方法 customFormat() 用来处理数据,你可以试一试。

4年前 评论

模型里面加上

public static $snakeAttributes = false; // 设置关联模型在打印输出的时候是否自动转为蛇型命名

就可以了,默认会自动把驼峰转蛇形

4年前 评论

@showcj 嗯额,我也挺奇怪的,作者文档也不解释一下,出问题后一脸懵逼

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!