Code前端首页关于Code前端联系我们

Laravel框架包含快速增删改查数据库的API方法

terry 2年前 (2023-09-25) 阅读数 51 #后端开发

Laravel 的查询构建器写起来很舒服,但仍然避免不了编写大量重复代码。比如我们要实现最基本的用户模块管理功能,至少需要编写如下接口

  • 用户列表接口(分页)
  • 用户添加可选接口选项。用户可选择的附加属性,如用户组、权限等。
  • 用户提交输入界面 // 保存新添加的用户
  • 用户编辑界面 // 必须在此处获取用户数据和附加信息
  • 用户提交提交界面 // 由用户修改提交数据
  • 删除 UI // 删除特定用户
Laravel 内置 Route::resource 是 即时创建舒缓风格界面,即时添加、删除、修改并查询资源。这是非常语义化的。但在工作中使用却存在几个问题
  • 其中一个就是需要编写的功能较多,需要实现7个接口方法。 ,大量重复
  • 使用HTTP方法区分动作,连接前端困难,PUT、POST、.GET、DELETE、PUT、PATCH等。
  • 不支持批量操作。我想删除一百个用户。您想循环发送 DELETE 请求 {user_id} 吗?虽然有办法解决,但是这个就比较棘手了

对于简单的增删改查逻辑,需要实现以下七个接口
Laravel框架封装Api快速增删改查数据库的方法
好吧,就用 RESTfulApi 风格吧,不考虑了暂时,让工作简单无忧。 ,偷懒一点,使用最传统的GET和POST方法与服务器通信
这里我们可以简单分析一下:我们要写的七个接口中,列表接口和删除接口是相对独立的,但是添加页面数据、提交新页面、编辑页面数据、提交编辑四个接口大体相似,因此可以单独抽取出来,封装成方法。下面直接贴代码,下面进行封装。方法基于,当然也可以用,手动狗头

primaryKey:这个主键,默认是ID,如果前端没有给你id,则一定要传给你 user_id ,然后你可以把这里的值改为 user_id
attributes :这个字段就是我们要处理的属性。比如我只想保存用户名,密码。对于这两个属性我可以写 ['username', 'password'] ,但其他参数无法处理。其实和模型上的fillable属性是一样的,只不过但这样写比较灵活
beforeSave:在该方法中,用户保存之前的处理,验证参数,启动交易等。
afterSave 对于比如用户注册后,我查看他的分配关系树,给他的上级分配佣金等等。
loadParams 这是最简单的。当我进入页面时,我需要知道用户可以选择哪些用户组以及可选角色是什么。 ,然后加载
onload 直接从数据库查到的数据并不是前端想要的。我可以把它放在这个回调中进行处理。比如我的数据库中用户等级分别存储为1和2,3分别对应青铜、银、金。然后我删除的时候就可以在这个回调函数里做处理

publicstaticfunctionsimpleEdit(array$options=[]){$model=newstatic;$result=[];$method=request()->method();$request=request();if(!in_array($method,['GET','POST'])){return$model->error("不支持的请求类型");}$options=array_merge(['isEdit'=>true,'primaryKey'=>null,'attributes'=>null,'beforeSave'=>null,'afterSave'=>null,'loadParams'=>[],'validator'=>null,'onload'=>null,],$options);$primaryKey=$options['primaryKey']??$model->getKeyName();$primaryKeyValue=(int)$request->input($primaryKey,null);if($options['isEdit']&&empty($primaryKeyValue)){return$model->error('请求参数错误,hint:没有ID');}if($options['isEdit']){$model=$model->findOrFail($primaryKeyValue);}if($method=="POST"){$model=$model->fill($options['attributes']??$request->all());if($options['beforeSave']instanceofClosure){$result=$options['beforeSave']($model);if($result)return$result;}if(!$model->save()){return$model->error("保存失败");}if($options['afterSave']instanceofClosure){$result=$options['afterSave']($model);if($result)return$result;}return[$primaryKey=>$model->id];}if($options['onload']instanceofClosure){$options['onload']($model);}$data=$model->toArray();if(!empty($data)){$result['data']=$data;}$result=array_merge($result,$options['loadParams']);return$result;}publicstaticfunctionsimpleCreate(array$options=[]){$options['isEdit']=false;returnstatic::simpleEdit($options);}

对于GET和POST请求,简单区分一下
GET只是用来获取数据,POST用来提交数据
然后对应的
GET /user/create 是加载用户的相关关联信息(用户组、权限等数据,如上所述)?获取ID为3的用户所有信息
POST /user/update?id=3 提交用户ID为3的编辑请求
。这是一个演示。首先我们用一个方法完成两个接口

publicfunctioncreate(Request $request){return$this->success(
            User::simpleCreate(['loadParams'=>['groups'=> UserGroup::all(),'roles'=> UserRole::all(),],'attributes'=>$request->only(['nickname','mobile','group_id']),'beforeSave'=>function(User $user){if($user->group_id<0){return$this->error("不合法的组ID");}$user->password=bcrypt(md5(uniqid()));},'afterSave'=>function(User $user){
                    Log::create(['content'=>"当前时间".now().",用户{$user->nickname}注册成功"]);}]));}

本例中我们使用这些配置来实现简单的用户注册,如下所示
GET /api/user/create
结果如下

{"error":0,"groups":[{"id":1,"name":"会员"},{"id":2,"name":"非会员"},{"id":3,"name":"超级会员"}],"roles":[{"id":1,"name":"管理员"},{"id":2,"name":"开发"},{"id":3,"name":"运营"},{"id":4,"name":"财务"},{"id":5,"name":"行政"},{"id":6,"name":"产品"}]}

符合预期,希望用户在进入新页面时可以选择自己的角色和群组

以下请求提交新接口
POST /api/user /create

{"error":0,"id":51}

结果符合预期。返回我们创建的最后一条记录的ID,对于前端跳转或者其他用途很有用

有些接口非常简单。添加新内容时不必选择杂乱的内容。可以只是一个表单,上传保存,然后就不能添加参数了,如下图,这样一个添加的接口就做好了,是不是很简单呢?

return$this->success(User::simpleCreate());

新界面已经准备好了,我们来说说编辑界面,分别是来自的
GET POST接口/api/user/edit与新接口的不同之处在于,任何编辑接口的请求都需要找到源。您需要通过id才能找到源代码。如下

publicfunctionedit(Request $request){return$this->success(
            User::simpleEdit(['loadParams'=>['groups'=> UserGroup::all(),'roles'=> UserRole::all(),],'attributes'=>$request->all(),'beforeSave'=>function(User $user){if($user->where('nickname',$user->nickname)->exists()){return$this->error("昵称已存在,请选择别的昵称吧");}},'afterSave'=>function(User $user, User $old){
                    Log::create(['content'=>"当前时间".now().",用户名称由{$old->nickname}修改为{$user->nickname}"]);},'onload'=>function(User $user){$user['group']=$user->group()->first();$user->makeHidden(['created_at','updated_at']);$user->mobile=str_replace(substr($user->mobile,3,4),'****',$user->mobile);}]));}

我们访问接口
GET /api/user/edit?id=51

{"error":0,"data":{"id":51,"nickname":"vencenty","mobile":"155****8899","password":"$2y$10$","is_admin":0,"group_id":2,"group":{"id":2,"name":"非会员"}},"groups":[{"id":1,"name":"会员"},{"id":2,"name":"非会员"},{"id":3,"name":"超级会员"}],"roles":[{"id":1,"name":"管理员"},{"id":2,"name":"开发"},{"id":3,"name":"运营"},{"id":4,"name":"财务"},{"id":5,"name":"行政"},{"id":6,"name":"产品"}]}

GET /api/user/create小小的区别是多了一个data字段存储了我们需要的用户实体信息

下一个请求
POST /api/user/edit?id=51

{"error":-10000,"message":"昵称已存在,请选择别的昵称吧"}

验证失败,所以满足按照预期,改正请求参数
Laravel框架封装Api快速增删改查数据库的方法

{"error":0,"id":51}

好了,去数据库查看一下
Laravel框架封装Api快速增删改查数据库的方法
就可以了


说一下删除吧

publicstaticfunctionsimpleDelete(array$options=[]){$request=request();$model=newstatic;if(!$request->isMethod('POST')){return$model->error("错误的请求方式");}$options=array_merge(['beforeDelete'=>null,'afterDelete'=>null,'primaryKey'=>null,],$options);if($options['beforeDelete']instanceofClosure){static::deleting($options['beforeDelete']);}if($options['afterDelete']instanceofClosure){static::deleted($options['afterDelete']);}$primaryKey=$options['primaryKey']??$model->getKeyName();$waitDeleteIdCollection=(array)$request->post($primaryKey,null);if(empty($waitDeleteIdCollection)){return$model->error("参数错误, hint:没有{$primaryKey}");}$affectRows=static::destroy($waitDeleteIdCollection);return['affectRows'=>$affectRows];}

接下来比如说我要删除 3 个用户,我只需要传递它。我只需要一个数组ID
Laravel框架封装Api快速增删改查数据库的方法
结果如下

{"error":0,"affectRows":3}

好吧,下一个是更复杂的列表接口,实现如下

publicstaticfunctionsimpleList(array$params=[],$options=[]){$model=newstatic;$request=request();$options=array_merge(['pager'=>true,'page'=>null,'pageName'=>'page','pageSizeName'=>'per_page','toArray'=>false,'callback'=>null,'sort'=>null,'by'=>'desc','attachParams'=>[],],$options);$pageName=$options['pageName']??'page';$currentPage=(int)$request->get($pageName,1);$pageSize=$request->get('per_page')??$model->getPerPage();if(isset($params['queryBuilder'])&&$params['queryBuilder']instanceofClosure){$model=$params['queryBuilder']->call($model,$request);unset($params['queryBuilder']);}$sort=$request->get('sort',$options['sort']);$by=$request->get('by',$options['by']);if(!empty($sort)){$model=$model->orderBy($sort,$by);}foreach($paramsas$key=>$param){if($paraminstanceofClosure){$model=$model->{$key}($param);continue;}if(Arr::isMultipleArray($param)){foreach($paramas$condition){$model=$model->{$key}(...$condition);}continue;}$model=$model->$key(...$param);}$total=$model->count();$data=$model->offset($pageSize*($currentPage-1))->limit($pageSize)->get();$data=$options['toArray']?$data->toArray():$data->all();if($options['callback']instanceofClosure){array_walk($data,$options['callback']);}$pageTotal=ceil($total/$pageSize);returnarray_merge(['data'=>$data,'per_page'=>$pageSize,'current_page'=>$currentPage,'total'=>$total,'page_total'=>$pageTotal,],$options['attachParams']);}

Laravel有自己的分页函数,但总体来说我认为不够灵活。例如,如果我想向外部数组添加数据,这并不容易实现。我在这里重新实现了它。配置参数的说明已注释。这里给前端开放几个接口


per_page 每页有多少数据
page 当前显示的是哪一页? by 可选参数 asc|desc
这个SimpleList方法中有几点需要特别说明。这两个参数,左边的Params参数可以用来生成各种查询条件,options参数用于处理请求的数据,还有附加参数等。我们使用 Laravel 框架。原来的write方法和下面的write方法类似,但是转成数组后怎么处理呢?这里我们使用 ...反汇编字符 将数组一一解析为参数,然后通过
call_user_func_array() 处理链式调用本身,所以原生 Laravel 类似于下面的 write 方法
User::where('id ','

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门