关于代码设计的问题咨询请教,主要是数据修改的一些写法设计

大家好,目前遇到一个问题,我想请教一下大家,就是比如我有A B C D四张表,存在部分数据依赖关系,比如 A表的某个字段,依赖B表的某些字段,一旦B表做了修改,A表也得重新更新,同理B表会依赖C表,C表会依赖D表
目前公司代码中,每一个表的ORM都有对应save方法的 前置和后置方法,然后现在用法就是比如B表修改了数据,因为A表依赖B表中的数据,所以B表修改数据之后A表要同步更新,就会将这部分代码写在B表的save前置或者后置方法,但是这样当相互关联依赖的表如果很多的话,感觉代码逻辑的可读性就很差,尤其是在这些前置后置方法里面,又写了非常多的业务逻辑判断,就不知道怎么去优化代码结构
因为我个人理解是这些orm中比如save前置或者后置方法,个人感觉的话,这部分方法里面写的代码内容,最好不要涉及到太多的逻辑判断,只写一些通用的,请问大家有什么建议吗?感谢大家

《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
最佳答案

模型内 一般不去做很复杂的业务逻辑判断,都在控制器 或者 控制器和模型中间加一层

正常应该是 控制器 处理好业务逻辑,判断好之后,才去模型操作数据库

像这种什么A关联B,B关联C的,本质就是一对多,多对多等等数据表关系的维护

一般来说,操作多表要启用事务,并遵循以下原则

开启事务后非必要尽量不要去使用查询操作

组合数据在开启事务前,多条添加组合成一个SQL,

像文中所提,钩子中还有很多业务逻辑的问题,这是明显得要在操作数据库之前要做的事情。

还有1L说的events 也是一种方法。

orm 中比如 save 前置或者后置方法 他提供了,你可以学,但未必要用。

你问这个问题,说明代码基础还相对薄弱,如果说是公司的代码,不建议你碰,你好心改完了全是bug,错误全是你的,改一改没bug能跑通就行了,公司招新人,公司图便宜,新人图练手,都是这样。

public function saveExam(User $user, $title, $questions)
{
    $questionData = [];
    try {
        $now = now();
        foreach ($questions as $group)
        {
            foreach ($group['list'] as $item)
            {
                $questionData[] = [
                    'user_id' => $user->id,
                    'question_id' => $item['question_id'],
                    'score' => (int) $item['score'],
                    'created_at' => $now,
                ];
            }
        }
    } catch (\Exception $e) {
        throw new HttpException(400, '试题数据读取失败');
    }

    if (empty($questionData)) {
        throw new HttpException(400, '试题数据读取失败.');
    }

    $examData = [
        'collection_type' => ExamCollection::SOURCE_SAVE,
        'collection_id' => null,
        'collection_name' => $title,
        'question_nums' => count($questionData),
        'user_id' => $user->id,
    ];

    DB::transaction(function() use ($examData, $questionData) {
        $exam = ExamCollection::create($examData);
        foreach ($questionData as $k => $data)
        {
            $questionData[$k]['collection_exam_id'] = $exam->id;
        }
        CollectionExamQuestion::insert($questionData);
    });

}
1年前 评论
概不对外 (楼主) 1年前
讨论数量: 10

就比如说之前同事遗留的代码,订单主表A表,和订单明细表B表,B表的模型里面save的后置方法就写了非常多的条件判断,根据不同逻辑执行不同的A表的数据以及状态修改,个人感觉就很奇怪,但是又不知道怎么去优化 :flushed:
仔细思考了下,感觉奇怪的原因主要是这种写法,B表模型里面save的后置方法,会在B表所有的修改操作之后都会去触发,这样尽管里面写了很多逻辑判断,并不是无脑全部执行,但是以后扩展部分业务逻辑,这个位置的代码难免会有没有考虑到的情况,而且也会继续在这里面新增逻辑判断和处理代码,就感觉这里面的代码会越来越多,越来越难维护

1年前 评论

事件驱动(events)可以进行解耦。当一个表发生更改时,可以发布一个事件,通知其他表执行相应的操作。这样每个表只需要关注自己的业务逻辑,而不需要关心其他表。

1年前 评论

可以了解一下模型观察者: 快速入门《Laravel 10 中文文档》

1年前 评论
概不对外 (楼主) 1年前
pigzzz (作者) 1年前

模型内 一般不去做很复杂的业务逻辑判断,都在控制器 或者 控制器和模型中间加一层

正常应该是 控制器 处理好业务逻辑,判断好之后,才去模型操作数据库

像这种什么A关联B,B关联C的,本质就是一对多,多对多等等数据表关系的维护

一般来说,操作多表要启用事务,并遵循以下原则

开启事务后非必要尽量不要去使用查询操作

组合数据在开启事务前,多条添加组合成一个SQL,

像文中所提,钩子中还有很多业务逻辑的问题,这是明显得要在操作数据库之前要做的事情。

还有1L说的events 也是一种方法。

orm 中比如 save 前置或者后置方法 他提供了,你可以学,但未必要用。

你问这个问题,说明代码基础还相对薄弱,如果说是公司的代码,不建议你碰,你好心改完了全是bug,错误全是你的,改一改没bug能跑通就行了,公司招新人,公司图便宜,新人图练手,都是这样。

public function saveExam(User $user, $title, $questions)
{
    $questionData = [];
    try {
        $now = now();
        foreach ($questions as $group)
        {
            foreach ($group['list'] as $item)
            {
                $questionData[] = [
                    'user_id' => $user->id,
                    'question_id' => $item['question_id'],
                    'score' => (int) $item['score'],
                    'created_at' => $now,
                ];
            }
        }
    } catch (\Exception $e) {
        throw new HttpException(400, '试题数据读取失败');
    }

    if (empty($questionData)) {
        throw new HttpException(400, '试题数据读取失败.');
    }

    $examData = [
        'collection_type' => ExamCollection::SOURCE_SAVE,
        'collection_id' => null,
        'collection_name' => $title,
        'question_nums' => count($questionData),
        'user_id' => $user->id,
    ];

    DB::transaction(function() use ($examData, $questionData) {
        $exam = ExamCollection::create($examData);
        foreach ($questionData as $k => $data)
        {
            $questionData[$k]['collection_exam_id'] = $exam->id;
        }
        CollectionExamQuestion::insert($questionData);
    });

}
1年前 评论
概不对外 (楼主) 1年前
巴啦啦

这种从设计上就不是很合理。能形成依赖的,最好仅仅只是id,依赖要依赖不可变的,唯一的。依赖易变的会有很多问题。
如果依赖了不可变的,那最好是显式的在程序中表达出来,写在一个代码块中,如有必要可以加锁。尽量少依赖前置后置操作,因为这大多数时候是业务需求,而不是程序内部的转换需求。所以需要清晰的在业务代码层面上表达出来,我修改了B,必然也会去修改A。

1年前 评论
概不对外 (楼主) 1年前
sanders

考虑事务特性最好就用数据库的触发器,也可以考虑使用模型更新事件,但需要注意事件包裹和不能使用异步监听器。如果不考虑事务,方法就更多了。

1年前 评论

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