怎么写一个可维护的项目

朴灿烈づ我的快乐病毒、 2023-10-06 18:16 104阅读 0赞

怎么写一个可维护的项目

  • 前言
    • **Service**
    • **Repository**
    • **Action**
  • 示例
    • UserController 控制器
    • UserService 控制器的服务层 service
    • UserRepository 控制器服务层的查询层 repository
    • CreateUser 控制器服务层的增删改层的增文件 action
    • User 模型
    • 预览
    • Common

前言

经常会有人问

– 目录如何设计比较好?
– 代码如何分布好?
– 怎么写一个可维护的项目?

“烂”项目我也没少写,以下是参考互联网各大佬的文章总结及个人开发经验而来。

Controller顾名思义是控制器,在入门PHP的时候,就知道Controller代表MVC中的C层,MVC本身的概念就代码分离,教你如何如何将业务分开,但面临着业务的不断发展,代码的复杂度也随之提高,功能与功能之间的链接错综复杂,最后你的MVC就变成了下图,所以仅仅依托MVC的设计思想已经无法支撑不断发展的业务。

现在我们将Controller的任务和能力重新定义,控制器仅仅控制Http Reqeust的请求,这样就符合了SOLID 单一功能原则。

直接将业务代码写在Controller中,会使得代码及其臃肿,不易于维护和扩展。

这时就应该思考如何分离业务代码,我们引入Service的概念

Service

Service本身译为服务
– 将外部方法,公共方法注入到Service
– 将Service注入到控制器

在这里插入图片描述
到现在为止,我们至少将业务与请求彻底分开了。但还是不如人意,如果把所有的业务及CURD全部写在Service中,那只不过是将Controller的臃肿转移到了Service,那Service就没有什么存在意义了。

【CURD】

代表创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)操作。

所以我们需要继续分割Service,将对数据库的R操作独立出来,因为CUD的操作基本是一贯不变的,而R操作根据业务的复杂度则变的多姿多彩。

所以独立R操作,这个时候我们引用Repository的概念。

Repository

我们使用Repository辅助Model,将相关的查询逻辑封装到不同的repository中,方便逻辑代码的维护。

– 符合SOLID的单一原则
– 符合SOLID的依赖反转

在这里插入图片描述
解决了R的问题,有人就问了,难道因为CUD比较统一简单就可以放在一起了吗?

答案是NO,我们引用一个新的名词Action。

Action

独立每个操作文件,例如CreateUser,DeleteUser,UpdateUser

– 符合SOLID的单一原则
在这里插入图片描述

示例

目录结构
在这里插入图片描述

UserController 控制器

  1. <?php
  2. namespace App\Http\Controllers;
  3. use App\Order;
  4. use App\Service\UserService;
  5. use Illuminate\Http\Request;
  6. class UserController extends Controller{
  7. public $request;
  8. protected $userService;
  9. public function __construct(Request $request, UserService $userService)
  10. {
  11. $this->request = $request;
  12. $this->userService = $userService;
  13. }
  14. public function register(){
  15. //... validation
  16. return $this->userService->register();
  17. }
  18. public function getUserInfo()
  19. {
  20. return $this->userService->getUserInfo();
  21. }
  22. }

UserService 控制器的服务层 service

  1. <?php
  2. namespace App\Service;
  3. use App\Action\CreateUser;
  4. use App\Exceptions\ApiException;
  5. use App\Repository\UserRepository;
  6. class UserService{
  7. public $userRepository;
  8. public $CreateUser;
  9. public function __construct(UserRepository $UserRepository,CreateUser $CreateUser)
  10. {
  11. $this->userRepository = $UserRepository;
  12. $this->CreateUser = $CreateUser;
  13. }
  14. public function getUserInfo()
  15. {
  16. return $this->userRepository->getUserInfo();
  17. }
  18. public function register(){
  19. return $this->CreateUser->execute(request()->all());
  20. }
  21. }

UserRepository 控制器服务层的查询层 repository

  1. <?php
  2. namespace App\Repository;
  3. use App\User;
  4. class UserRepository
  5. {
  6. public $models;
  7. public function __construct()
  8. {
  9. $this->models = new User();
  10. }
  11. public function getUserInfo()
  12. {
  13. return $this->models->where('id',request('user_id'))->first();
  14. }
  15. }

CreateUser 控制器服务层的增删改层的增文件 action

  1. <?php
  2. namespace App\Action;
  3. use App\User;
  4. class CreateUser
  5. {
  6. public $models;
  7. public function __construct()
  8. {
  9. $this->models = new User();
  10. }
  11. public function execute(array $data)
  12. {
  13. return $this->models->create($data);
  14. }
  15. }

上面只是添加数据的文件,还可以继续创建修改的文件。

以上代码逻辑见下图

在这里插入图片描述

User 模型

  1. <?php
  2. namespace App;
  3. use Illuminate\Contracts\Auth\MustVerifyEmail;
  4. use Illuminate\Database\Eloquent\SoftDeletes;
  5. use Illuminate\Foundation\Auth\User as Authenticatable;
  6. use Illuminate\Notifications\Notifiable;
  7. class User extends Authenticatable
  8. {
  9. use Notifiable;
  10. // 调用定义的trait类 和 继承效果一样
  11. use SoftDeletes;
  12. // 软删除标识字段
  13. protected $dates = ['deleted_at'];
  14. /**
  15. * The attributes that are mass assignable.
  16. *
  17. * @var array
  18. */
  19. protected $fillable = [
  20. 'username', 'email', 'password',
  21. ];
  22. /**
  23. * The attributes that should be hidden for arrays.
  24. *
  25. * @var array
  26. */
  27. protected $hidden = [
  28. 'password', 'remember_token',
  29. ];
  30. /**
  31. * The attributes that should be cast to native types.
  32. *
  33. * @var array
  34. */
  35. protected $casts = [
  36. 'email_verified_at' => 'datetime',
  37. ];
  38. }

预览

路由

  1. Route::get('getUserInfo', 'UserController@getUserInfo');
  2. Route::get('register', 'UserController@register');

在这里插入图片描述
在这里插入图片描述

Common

在这里插入图片描述
译为公共的,常用的,再部分开发中,你可能需要一些公共的方法(并非公共的类,例如邮件发送等,用他并不合适),

比如查询用户余额,查询用户是否注册或者是否在线,生成订单号等。使用Common更要简单。他更像一个公共函数库的样子。

在这里插入图片描述
Event

不关心执行结果时可以选使用,不过Event的Listen也是提供了队列。

Exception

不要将你的所有错误提示都使用Return返回,很多时候你的返回未必是你的返回。

发表评论

表情:
评论列表 (有 0 条评论,104人围观)

还没有评论,来说两句吧...

相关阅读

    相关 维护项目

    月份是入职的第一个月,简单认识了同事们,熟悉了公司的主要业务和工作之后,我就开始跟随以为在香港工作的同事维护一个WEB项目。 项目是Java JSP和JQ来实现的多页面...