六、 RBAC权限管理
1、RABC概念和原理
RBAC:全称叫做Role-Based Access Control,中文翻译叫做基于角色的访问控制。其主要的作用是实现项目的权限控制。
效果:让不同的管理员,能够访问的页面不一样。比如运营人员 只能看到运营相关模块。财务人员只能看到财务相关模块。
传统权限管理:
RBAC权限管理:
RBAC权限管理:
权限的管理相对规范,角色拥有的权限可以按照一定的标准定义好。
新增管理员,只需要指定角色,即可拥有对应的权限。
2、数据表设计
基本数据表:管理员表、角色表、权限表
对应关系:管理员对应一个角色,一个角色对应多个权限。(一个管理员也可以对应多个角色)
对应关系的维护:
第一种:三表结构,在管理员表增加角色id字段,在角色表增加权限ids字段,最终三张表。
第二种:五张表,新增一张管理员角色关联表,新增一张角色权限关联表。最终五张表。
这里以三表结构为例:
查询一个管理员拥有的权限:
先查询管理员表,获取到角色id
再查询角色表,获取到角色拥有的权限ids
最后根据权限ids查询权限表,where id in (1,2,3)
特殊情况:超级管理员拥有所有的权限
设置 超级管理员的角色id为1,超级管理员拥有的权限,直接查询权限表所有的数据。
3、表之间的关系
管理员表pyg_admin中的role_id字段对应角色表pyg_role表主键id字段
4、模拟数据
管理员表、角色表和权限表都有一些默认的模拟数据
创建模型:
php think make:model common/Role
php think make:model common/Auth
七、权限管理
1、权限列表
用到的返回父子级或者无限级别列表函数用到递归和索引
if (!function_exists('get_cate_list')) {
//递归函数 实现无限级分类列表
function get_cate_list($list,$pid=0,$level=0) {
static $tree = array();
foreach($list as $row) {
if($row['pid']==$pid) {
$row['level'] = $level;
$tree[] = $row;
get_cate_list($list, $row['id'], $level + 1);
}
}
return $tree;
}
}
if(!function_exists('get_tree_list')){
//引用方式实现 父子级树状结构
function get_tree_list($list){
//将每条数据中的id值作为其下标
$temp = [];
foreach($list as $v){
$v['son'] = [];
$temp[$v['id']] = $v;
}
//获取分类树
foreach($temp as $k=>$v){
$temp[$v['pid']]['son'][] = &$temp[$v['id']];
}
return isset($temp[0]['son']) ? $temp[0]['son'] : [];
}
}
1.1 无限级分类列表
定义路由
创建控制器方法
接收参数
查询数据
无限级分类列表
返回数据
实现
/**
* 显示资源列表
*
* @return 列表 普通列表;无限级分类列表;父子级树状列表
*/
public function index()
{
//接收参数 keyword || type
$params=input();
$where=[];
if(!empty($params['keyword'])){
$where['auth_name']=['like',"%{$params['keyword']}%"];
}
//查询数据
$list=\app\common\model\Auth::field('id,auth_name,pid,pid_path,auth_c,auth_a,is_nav,level')->where($where)->select();
//转化为标准的二维数组
$list=(new \think\Collection($list))->toArray();
if(!empty($params['type'])&&$params['type']=='tree'){
//父子级树状列表
$list=get_tree_list($list);
}else{
//无限级分类列表
$list=get_cate_list($list);
}
//返回数据
$this->ok($list);
}
1.2 父子级树状列表
在1.1基础上
接收参数判断
父子级树状列表
同1.1 见$params[‘type’]的判断
2、权限详情
定义路由
创建控制器方法
查询数据
返回数据
实现
/**
* 显示指定的资源
*
* @param int $id
* @return \think\Response
*/
public function read($id)
{
//查询数据
$auth=\app\common\model\Auth::field('id,auth_name,pid,pid_path,auth_c,auth_a,is_nav,level')->find($id);
//返回数据
$this->ok($auth);
}
3、权限新增
定义路由(已定义资源路由)
创建控制器方法
接收数据
参数检查
添加数据(是否顶级,级别和pid_path处理)
返回数据
实现
/**
* 保存新建的资源
*
* @param \think\Request $request
* @return \think\Response
*/
public function save(Request $request)
{
//接收数据
$params=input();
//参数检测
$validate=$this->validate($params,[
'auth_name|权限名称'=>'require',
'pid|上级权限'=>'require',
'is_nav|菜单权限'=>'require',
//'auth_c|控制器名称'=>'',
//'auth_a|方法名称'=>'',
]);
if($validate!==true){
$this->fail($validate);
}
//添加数据(是否顶级,级别和pid_path处理)
if($params['pid']==0){
$params['level']=0;
$params['pid_path']=0;
$params['auth_c']='';
$params['auth_a']='';
}else{
//不是顶级权限
//查询上级信息
//$p_info=\app\common\model\Auth::where('id',$params['pid'])->find();
$p_info=\app\common\model\Auth::find($params['pid']);
if(empty($p_info)){
$this->fail('数据异常');
}
//设置级别+1 家族图谱拼接
$params['level']=$p_info['level']+1;
$params['pid_path']=$p_info['pid_path'].'_'.$p_info['id'];
}
//实际开发看情况 一般不需要返回数据
//\app\common\model\Auth::create($params,true);
//$this->ok();
//restful 严格风格
$auth=\app\common\model\Auth::create($params,true);
$info=\app\common\model\Auth::find($auth['id']);
//返回数据
$this->ok($info);
}
4、权限修改
定义路由(已定义资源路由)
创建控制器方法
接收数据
参数检测
添加数据(是否顶级,级别和pid_path处理)
返回数据
实现
/**
* 保存更新的资源
*
* @param \think\Request $request
* @param int $id
* @return \think\Response
*/
public function update(Request $request, $id)
{
//接收参数
$params=input();
//参数检测
$validate=$this->validate($params,[
'auth_name|权限名称'=>'require',
'pid|上级权限'=>'require',
'is_nav|菜单权限'=>'require',
//'auth_c|控制器名称'=>'',
//'auth_a|方法名称'=>'',
]);
if($validate!==true){
$this->fail($validate);
}
//修改数据(是否顶级,级别和pid_path处理)
$auth=\app\common\model\Auth::find($id);
if(empty($auth)){
$this->fail('数据异常');
}
if($params['pid']==0){
//说明是修改顶级权限
$params['level']=0;
$params['pid_path']=0;
}else if($params['pid']!=$auth['pid']){
//如果修改其上级权限pid 重新设置level级别和pic_path家族图谱
$p_auth=\app\common\model\Auth::find($params['pid']);
if(empty($p_auth)){
$this->fail('数据异常');
}
//设置级别+1 家族图谱拼接
$params['level']=$p_auth['level']+1;
$params['pid_path']=$p_auth['pid_path'].'_'.$p_auth['id'];
}
\app\common\model\Auth::update($params,['id'=>$id],true);
//返回数据
$info=\app\common\model\Auth::find($id);
$this->ok($info);
}
5、权限删除
定义路由(已定义资源路由)
创建控制器方法
权限下是否有子权限
删除数据
返回数据
实现
public function delete($id)
{
//判断是否有子权限
$total=\app\common\model\Auth::where('pid',$id)->count();
if($total>0){
$this->fail('有子权限,无法删除');
}
//删除数据
\app\common\model\Auth::destroy($id);
//返回数据
$this->ok();
}
6、菜单权限列表
定义路由
创建控制器方法
获取当前管理员的角色id
查询权限
超级管理员:role_id==1,直接查询权限表
获取当前管理员的角色id
查询权限:
超级管理员: role_id == 1 ,直接查询权限表
普通管理员: role_id !=1, 查询角色表,获取role_auth_ids字段,根据role_auth_ids值,查询权限表,使用where id in (1,2,3)条件
菜单权限: is_nav = 1
父子级树状列表
返回数据
public function nav(){
//获取登录的管理员用户id
$user_id=input('user_id');
//查询管理员的角色id role_id
$info=\app\common\model\Admin::find($user_id);
$role_id=$info['role_id'];
//判断是否超级管理员
if($role_id==1){
//超级管理员 直接查询权限表 菜单权限 is_nav=1
$data=\app\common\model\Auth::where('is_nav',1)->select();
}else{
//先查询角色表 role_auth_ids
$role=\app\common\model\Role::find($role_id);
$role_auth_ids=$role['role_auth_ids'];
//再查询权限表
$data=\app\common\model\Auth::where('is_nav',1)->where('id','in',$role_auth_ids)->select();
}
//先转化为标准的二维数组
$data=(new \think\Collection($data))->toArray();
//再转化为 父子级树状结构
$data=get_tree_list($data);
//返回数据
$this->ok($data);
}
八、角色管理
1、角色列表
定义路由
创建控制器方法
查询数据
返回数据
public function index()
{
//查询数据 (不需要查询超级管理员)
$list=\app\common\model\Role::where('id','>',1)->select();
//对每条角色数据,查询对应权限,增加role_auths下标的数据 (父子级树状结构)
foreach ($list as $k=>$v){
//$v['role_auth_ids']
//查询权限表
$auths=\app\common\model\Auth::where('id','in',$v['role_auth_ids'])->select();
// $auths=\app\common\model\Auth::select($v['role_auth_ids']);
//先转化为标准的二维数组
$auths=(new \think\Collection($auths))->toArray();
//再转化为父子级树状结构
$auths=get_tree_list($auths);
//$v['role_auth_ids']=$auths
$list[$k]['role_auths']=$auths;
}
unset($v); //特别是 $v前面有&(引用时)强烈建议 unset
//返回数据
$this->ok($list);
}
2、角色详情
定义路由
创建控制器方法
查询数据
返回数据
public function read($id)
{
//查询数据
$info=\app\common\model\Role::field('id,role_name,desc,role_auth_ids')->find($id);
//返回数据
$this->ok($info);
}
3、角色新增
定义路由
创建控制器方法
接收数据
参数检测
添加数据
返回数据
public function save(Request $request)
{
//接收数据
$params=input();
//参数检测
$validate=$this->validate($params,[
'role_name'=>'require',
//'desc'=>'',
'auth_ids'=>'require',
]);
if($validate!==true){
$this->fail($validate);
}
//添加数据
$params['role_auth_ids']=$params['auth_ids'];
$role=\app\common\model\Role::create($params,true);
$info=\app\common\model\Role::find($role['id']);
//返回数据
$this->ok($info);
}
4、角色修改
定义路由
创建控制器方法
接收数据
参数检测
添加数据
返回数据
public function update(Request $request, $id)
{
//接收数据
$params=input();
//参数检测
$validate=$this->validate($params,[
'role_name'=>'require',
//'desc'=>'',
'auth_ids'=>'require',
]);
if($validate!==true){
$this->fail($validate);
}
//添加数据
$params['role_auth_ids']=$params['auth_ids'];
\app\common\model\Role::update($params,['id'=>$id],true);
$info=\app\common\model\Role::find($id);
//返回数据
$this->ok($info);
}
5、角色删除
定义路由
创建控制器方法
接收数据
参数检测
添加数据
返回数据
public function delete($id)
{
//如果角色下有管理员,不能删除
//根据角色id 查询管理员表的role_id字段
$total=\app\common\model\Admin::where('role_id',$id)->count();
if($total>0){
$this->fail('角色正在使用中,无法删除');
}
//删除数据
\app\common\model\Role::destroy($id);
//返回数据
$this->ok();
}
九、管理员管理
1、管理员列表
定义路由
创建控制器方法
查询数据
返回数据
public function index()
{
//接收参数 keyword page
$params=input();
$where=[];
//搜索条件
if(!empty($params['keyword'])){
$keyword=$params['keyword'];
$where['t1.username']=['like',"%$keyword%"];
}
//分页查询(包含搜索)
//$list=\app\common\model\Admin::where($where)->paginate(2);
//SELECT t1.*,t2.role_name FROM pyg_admin t1 left join pyg_role t2 on t1.role_id=t2.id where username like '%a%' limit 0,2;
$list=\app\common\model\Admin::alias('t1')
->join('pyg_role t2','t1.role_id=t2.id','left')
->field('t1.*,t2.role_name')
->where($where)->paginate(2);
//返回数据
$this->ok($list);
}
2、管理员详情
定义路由
创建控制器方法
查询数据
返回数据
public function read($id)
{
//查询数据
$info=\app\common\model\Admin::find($id);
//返回数据
$this->ok($info);
}
3、管理员新增
定义路由
创建控制器方法
接收数据
参数检测
添加数据
返回数据
public function save(Request $request)
{
//接收参数
$params = input();
$validate=$this->validate($params,[
'username|用户名'=>'require|unique:admin',
//'username|用户名'=>'require|unique:admin,username',
'email|邮箱'=>'require|email',
'role_id|所属角色'=>'require|integer|gt:0',
'password|密码'=>'length:6,20',
]);
if($validate!==true){
$this->fail($validate);
}
//添加数据
if(empty($params['password'])){
$params['password']='123456';
}
$params['password']=encrypt_password($params['password']);
$params['nickname']=$params['username'];
$info=\app\common\model\Admin::create($params,true);
//查询刚才添加的数据
$data=\app\common\model\Admin::find($info['id']);
//返回数据
$this->ok($data);
}
4、管理员修改
定义路由
创建控制器方法
接收数据
参数检测
修改数据
返回数据
public function update(Request $request, $id)
{
if($id==1){
$this->fail('超级管理员,不能修改');
}
//接收参数
$params=input();
if(!empty($params['type'])&&$params['type']=='reset_pwd'){
//修改密码
$password=encrypt_password('123456');
\app\common\model\Admin::update(['password'=>$password],['id'=>$id],true);
}else{
//参数检测
$validate=$this->validate($params,[
'email|邮箱'=>'email',
'role_id'=>'integer|gt:0',
'nickname|昵称'=>'max:50'
]);
if($validate!==true){
$this->fail($validate);
}
//修改数据 (用户名不让改)
unset($params['username']);
unset($params['password']);
\app\common\model\Admin::update($params,['id'=>$id],true);
}
$data=\app\common\model\Admin::find($id);
//返回数据
$this->ok($data);
}
5管理员删除
删除数据
返回数据
public function delete($id)
{
//删除数据(不能删除超级管理员admin、不能删除自己)
if($id==1){
$this->fail('不能删除超级管理员');
}
if($id==input('user_id')){
$this->fail('不能删除自己');
}
\app\common\model\Admin::destroy($id);
//返回数据
$this->ok();
}
十、权限检测
管理员登录后,访问每个页面时,需要进行权限检测:
判断当前管理员是否拥有当前访问页面的权限。
原理:
查询当前管理员所拥有的权限role_auth_ids
查询到当前访问页面对应的权限id(控制器名称和方法名称为条件)
判断权限id和role_auth_ids,是否拥有当前页面访问权限
特殊情况:
超级管理员不需要检测
所有管理员都可以访问的页面,不需要检测(比如首页)
思路:
判断是否特殊页面(比如首页、不需要检测)
获取到管理员的角色id
判断是否超级管理员(超级管理员不需要检测)
查询当前管理员所拥有的权限ids(从角色表查询对应的role_auth_ids)
根据当前访问的控制器、方法名称查询到具体的权限id
判断当前权限id是否在role_auth_ids范围中
实现:
将权限检测的逻辑 封装到 application/adminapi/logic/AuthLogic.php中
<?php
namespace app\adminapi\logic;
class AuthLogic{
public static function check(){
//判断是否是特殊页面(比如首页,不需要检测)
$controller=request()->controller();//返回的是首字母大写
$action=request()->action();
if($controller=='Index'&&$action=='index'){
//不需要检测(有权限访问)
return true;
}
//获取到管理员的角色id
$user_id=input('user_id');
$info=\app\common\model\Admin::find($user_id);
$role_id=$info['role_id'];
//判断是否是超级管理员 (超级管理员不需要检测)
if($role_id==1){
//不需要检测(有权限访问)
return true;
}
//查询当前管理员所拥有的权限ids(从角色表查询对应的role_auth_ids)
$role=\app\common\model\Role::find($role_id);
//取出权限ids 分隔为数组
$role_auth_ids=explode(',',$role['role_auth_ids']);
//根据当前访问的控制器、方法名称查询到具体的权限id
$auth=\app\common\model\Auth::where('auth_c',$controller)->where('auth_a',$action)->find();
$auth_id=$auth['id'];
//判断当前权限id是否在role_auth_ids范围中
if(in_array($auth_id,$role_auth_ids)){
//有权限
return true;
}
return false;
}
}
BaseApi中调用
十一、商品分类管理
商品相关数据表
多条线表示箭头起始表有多条记录对应箭头终止表一条记录。
箭头起始表中包含有xxx_id字段,对应箭头终止表主键id字段
1、商品分类、商品品牌、商品、商品相册
2、商品、商品模型、商品属性
3、商品、商品模型、商品SKU
SPU:最小产品单元,包含多种可选规格的某种商品的集合。iphone6就是一个SPU。
SKU:最小库存单元,不可再拆分的产品。32G的黑色的iphone6就是一个SKU。
完整版
1、商品分类列表
定义路由
创建控制器方法
查询数据
返回数据(无限级分类列表、父子级树状列表)
public function index()
{
//接收pid参数 影响查询的数据
//$pid=input('pid','');
//if($pid===''){}
$params=input();
$where=[];
if(isset($params['pid'])){
$where['pid']=$params['pid'];
}
//接收type参数 影响返回的数据
//查询数据
$list=\app\common\model\Category::where($where)->select();
//转化为无限级分类列表
$list=(new \think\Collection($list))->toArray();
/* if(isset($params['type'])&&$params['type']=='list'){
}else{
$list=get_cate_list($list);
}*/
if(!isset($params['type'])||$params['type']!='list'){
$list=get_cate_list($list);
}
//返回数据
$this->ok($list);
}
2、商品分类详情
定义路由
创建控制器方法
查询数据
返回数据
public function read($id)
{
//查询数据
$info=\app\common\model\Category::find($id);
//返回数据
$this->ok($info);
}
3、商品分类新增
定义路由
创建控制器方法
接收数据
参数检测
添加数据
返回数据
public function save(Request $request)
{
//接收参数
$params=input();
//参数检测
$validate=$this->validate($params,[
'cate_name'=>'require|length:2,20',
'pid'=>'require|integer|egt:0',
'is_show'=>'require|in:0,1',
'is_hot'=>'require|in:0,1',
'sort'=>'require|between:0,9999',
]);
if($validate!==true){
$this->fail($validate);
}
//添加数据 (处理pid_path pid_path_name level)
if($params['pid']==0){
//顶级分类
$params['pid_path']=0;
$params['pid_path_name']='';
$params['level']=0;
}else{
//不是顶级分类,查询其上级分类
$p_info=\app\common\model\Category::where('id',$params['pid'])->find();
if(empty($p_info)){
//没有查到数据
$this->fail('数据异常,请稍后再试');
}
$params['pid_path']=$p_info['pid_path'].'_'.$p_info['id'];
$params['pid_path_name']=$p_info['pid_path_name'].'_'.$p_info['cate_name'];
$params['level']=$p_info['level']+1;
}
$cate=\app\common\model\Category::create($params,true);
$info=\app\common\model\Category::find($cate['id']);
//返回数据
$this->ok($info);
}
4、商品分类修改
定义路由
创建控制器方法
接收数据
参数检测
修改数据
返回数据
实现:
public function update(Request $request, $id)
{
//接收参数
$params=input();
//参数检测
$validate=$this->validate($params,[
'cate_name'=>'require|length:2,20',
'pid'=>'require|integer|egt:0',
'is_show'=>'require|in:0,1',
'is_hot'=>'require|in:0,1',
'sort'=>'require|between:0,9999',
]);
if($validate!==true){
$this->fail($validate);
}
//添加数据 (处理pid_path pid_path_name level)
if($params['pid']==0){
//顶级分类
$params['pid_path']=0;
$params['pid_path_name']='';
$params['level']=0;
}else{
//不是顶级分类,查询其上级分类
$p_info=\app\common\model\Category::where('id',$params['pid'])->find();
if(empty($p_info)){
//没有查到父级
$this->fail('数据异常,请稍后再试');
}
$params['pid_path']=$p_info['pid_path'].'_'.$p_info['id'];
$params['pid_path_name']=$p_info['pid_path_name'].'_'.$p_info['cate_name'];
$params['level']=$p_info['level']+1;
}
//logo图片处理
if(isset($params['logo'])&&!empty($params['logo'])){
$params['image_url']=$params['logo'];
}
\app\common\model\Category::update($params,['id'=>$id],true);
$info=\app\common\model\Category::find($id);
//返回数据
$this->ok($info);
}
5、商品分类删除
删除数据
返回数据
public function delete($id)
{
//删除数据
//判断分类下是否有子分类
$total=\app\common\model\Category::where('pid',$id)->count();
if($total>0){
$this->fail('分类下有子分类,无法删除');
}
\app\common\model\Category::destroy($id);
//返回数据
$this->ok();
}
十三、文件上传
1、单图片上传
定义路由
创建控制器方法
图片上传
返回数据
实现:
//单图片上传
public function logo(){
//接收参数
$type=input('type');
if(empty($type)){
$this->fail('缺少参数');
}
//获取文件
$file=request()->file('logo');
if(empty($file)){
$this->fail('必须上传文件');
}
//图片移动 /public/uploads/goods /public/uploads/categroy public/uploads/brand
$info=$file->validate(['size'=>10*1024*1024,'ext'=>'jpg,jpeg,png,gif'])->move(ROOT_PATH.'public'.DS.'uploads'.DS.$type);
if($info){
//返回图片路径 /uploads/ccategroy/2021/12/06/dawdw.jpg
$logo=DS.'uploads'.DS.$type.DS.$info->getSaveName();
$this->ok($logo);
}else{
//返回报错
$msg=$file->getError();
$this->fail($msg);
}
}
postman测试
总结:
管理员 增删改查接口
权限检测
能够说清RBAC究竟咋回事
商品相关数据表介绍
单品分类 增删改查(三级分类)
单图片上传接口
2、多图片上传
定义路由
创建控制器方法
图片上传
返回数据
实现:
//多图上传
public function images()
{
//接收type参数 图片分组
$type = input('type', 'goods');
//$type=request()->param('type','goods');
//获取上传的文件(数组)
$files = request()->file('images');
//遍历数组逐个上传文件
$data = ['success' => [], 'error' => []];
foreach ($files as $file) {
//移动文件到指定目录下 /public/uploads/goods/目录下
$dir = ROOT_PATH . 'public' . DS . 'uploads' . DS . $type;
if (!is_dir($dir)) {//如果没有这个文件夹就创建
mk_dir($dir);
}
$info = $file->validate(['size' => 10 * 1024 * 1024, 'ext' => 'jpg,jpeg,png,gif'])->move($dir);
if ($info) {
//成功 拼接图片路径
$path = DS . 'uploads' . DS . $type . DS . $info->getSaveName();
$data['success'][] = $path;
} else {
//失败获取错误信息
$data['error'][] = [
'name' => $file->getInfo('name'),
'msg' => $file->getError(),
];
}
}
$this->ok($data);
}
postman测试
十四、商品品牌管理
1、商品品牌列表
定义路由
创建控制器方法
查询数据
返回数据(分页+搜索列表、分类下的品牌列表)
public function index()
{
//接收参数 cate_id; keyword page
$params=input();
$where=[];
if(isset($params['cate_id'])&&!empty($params['cate_id'])){
//分类下的品牌列表
$where['t1.cate_id']=$params['cate_id'];
//查询数据
//SELECT t1.*,t2.cate_name FROM `pyg_brand` t1 left join pyg_category t2 on t1.cate_id=t2.id where cate_id=72;
//$list=\app\common\model\Brand::where($where)->field('id,name')->select();
/*$list=\app\common\model\Brand::alias('t1')
->join('pyg_categroy t2','t1.cate_id=t2.id','left')
->field('t1.*,t2.cate_name')
->where($where)
->select();*/
$list=\app\common\model\Brand::alias('t1')
->join(config('database.prefix').'category t2','t1.cate_id=t2.id','left')
->field('t1.*,t2.cate_name')
->where($where)
->select();
}else{
//分页+搜索列表
if(isset($params['keyword'])&&!empty($params['keyword'])){
$keyword=$params['password'];
$where['t1.name']=['like',"%$keyword%"];
}
//分页查询数据
//SELECT t1.*,t2.cate_name FROM `pyg_brand` t1 left join pyg_category t2 on t1.cate_id=t2.id where name like '%亚%' limit 0,10;
//$list=\app\common\model\Brand::where($where)->paginate(10);
$list=\app\common\model\Brand::alias('t1')
->join('pyg_category t2','t1.cate_id=t2.id','left')
->field('t1.*,t2.cate_name')
->where($where)
->paginate(10);
}
$this->ok($list);
}
2、商品品牌详情
定义路由
创建控制器方法
查询数据
返回数据
public function read($id)
{
//查询数据
$info=\app\common\model\Category::find($id);
//返回数据
$this->ok($info);
}
3、商品品牌新增
定义路由
//安装生成缩略图 如果是完整版tp框架不用安装
php composer.phar require topthink/think-image 1.*
public function save(Request $request)
{
//接收参数
$params=input();
//参数检测
$validate=$this->validate($params,[
'cate_name'=>'require|length:2,20',
'pid'=>'require|integer|egt:0',
'is_show'=>'require|in:0,1',
'is_hot'=>'require|in:0,1',
'sort'=>'require|between:0,9999',
]);
if($validate!==true){
$this->fail($validate);
}
//添加数据 (处理pid_path pid_path_name level)
if($params['pid']==0){
//顶级分类
$params['pid_path']=0;
$params['pid_path_name']='';
$params['level']=0;
}else{
//不是顶级分类,查询其上级分类
$p_info=\app\common\model\Category::where('id',$params['pid'])->find();
if(empty($p_info)){
//没有查到父级
$this->fail('数据异常,请稍后再试');
}
$params['pid_path']=$p_info['pid_path'].'_'.$p_info['id'];
$params['pid_path_name']=$p_info['pid_path_name'].'_'.$p_info['cate_name'];
$params['level']=$p_info['level']+1;
}
//logo图片处理
$params['image_url']=isset($params['logo'])?$params['logo']:'';
//生成缩略图
if(isset($params['image_url'])&&!empty($params['image_url'])&&is_file('.'.$params['image_url'])){
\think\Image::open('.'.$params['image_url'])->thumb(200,100)->save('.'.$params['image_url']);
}
$cate=\app\common\model\Category::create($params,true);
$info=\app\common\model\Category::find($cate['id']);
//返回数据
$this->ok($info);
}
4、商品品牌修改
定义路由
创建控制器方法
接收数据
参数检测
修改数据
返回数据
实现
public function update(Request $request, $id)
{
//接收参数
$params=input();
//参数检测
$validate=$this->validate($params,[
'name'=>'require',
'cate_id'=>'require|integer|gt:0',
'is_hot'=>'require|in:0,1',
'sort'=>'require|between:0,9999'
]);
if($validate!==true){
$this->fail($validate);
}
//修改数据(logo图片缩略图)
if(isset($params['logo'])&&!empty($params['logo'])&&is_file('.'.$params['logo'])){
//生成缩略图
\think\image::open('.'.$params['logo'])->thumb(200,100)->save('.'.$params['logo']);
}
\app\common\model\Brand::update($params,['id'=>$id],true);
$info=\app\common\model\Brand::find($id);
//返回数据
$this->ok($info);
}
5、商品品牌删除
删除数据
返回数据
实现
public function delete($id)
{
//判断 品牌下是否有商品
$total=\app\common\model\Goods::where('brand_id',$id)->count();
if($total>0){
$this->fail('品牌下有商品,不能删除');
}
//删除品牌
\app\common\model\Brand::destroy($id);
//返回结果
$this->ok();
}
十五、连表查询与关联模型
关联模型 见手册–模型–关联(一对一关联,一对多关联,关联预载入)
1、连表查询
需求:查询所有商品品牌信息及其所属的分类名称
连表查询一个品牌及分类名称
SELECT t1.*,t2.cate_name FROM 'pyg_brand' t1 left join pyg_categroy t2 on t1.cate_id=t2.id where t1.id=1;
连表查询所有品牌以及对应的分类名称
SELECT t1.*,t2.cate_name FROM 'pyg_brand' t1 left join pyg_categroy t2 on t1.cate_id=t2.id;
对应框架中的代码:
连表查询一个品牌及分类名称
$info=\app\common\model\Brand::alias('t1')
->join('pyg_category t2','t1.cate_id=t2.id','left')
->field('t1.*,t2.cate_name')
->where('t1.id',$id)
->find();
连表查询所有品牌以及对应的分类名称
$list=\app\common\model\Brand::alias('t1')
->join('pyg_categroy t2','t1.cate_id=t2.id','left')
->field('t1.*,t2.cate_name')
->select();
数组形式的结果:
$info=['id'=>1,'name'=>'华为',...,'cate_name'=>'手机'];
$list=[
['id'=>1,'name'=>'华为',...,'cate_name'=>'手机'],
['id'=>2,'name'=>'小米',...,'cate_name'=>'手机'],
]
2、一对一关联
新增管理员档案表pyg_profile,保存每个管理员的详细信息(身份证号、银行卡号)。
CREATE TABLE `pyg_profile` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`uid` int(11) NOT NULL DEFAULT '0' COMMENT '用户id',
`idnum` varchar(30) DEFAULT NULL COMMENT '身份证号',
`card` varchar(255) DEFAULT NULL COMMENT '银行卡号',
`create_time` int(11) DEFAULT NULL,
`update_time` int(11) DEFAULT NULL,
`delete_time` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
添加测试数据
INSERT INTO `pyg`.`pyg_profile` (`id`, `uid`, `idnum`, `card`, `create_time`, `update_time`, `delete_time`) VALUES ('1', '1', '232332198008083321', '421656421254789', '1520408547', '1520408547', NULL);
INSERT INTO `pyg`.`pyg_profile` (`id`, `uid`, `idnum`, `card`, `create_time`, `update_time`, `delete_time`) VALUES ('2', '2', '435332198108083312', '521656421254777', '1520408547', '1520408547', NULL);
INSERT INTO `pyg`.`pyg_profile` (`id`, `uid`, `idnum`, `card`, `create_time`, `update_time`, `delete_time`) VALUES ('3', '3', '655332198108083357', '681656421254787', '1520408547', '1520408547', NULL);
INSERT INTO `pyg`.`pyg_profile` (`id`, `uid`, `idnum`, `card`, `create_time`, `update_time`, `delete_time`) VALUES ('4', '4', '987067198208083734', '843123421257829', '1520408547', '1520408547', NULL);
INSERT INTO `pyg`.`pyg_profile` (`id`, `uid`, `idnum`, `card`, `create_time`, `update_time`, `delete_time`) VALUES ('5', '5', '657067198408083256', '753623421259523', '1520408547', '1520408547', NULL);
INSERT INTO `pyg`.`pyg_profile` (`id`, `uid`, `idnum`, `card`, `create_time`, `update_time`, `delete_time`) VALUES ('6', '6', '746067198608089463', '534623421259125', '1520408547', '1520408547', NULL);
INSERT INTO `pyg`.`pyg_profile` (`id`, `uid`, `idnum`, `card`, `create_time`, `update_time`, `delete_time`) VALUES ('7', '7', '745367198708089414', '514623426449165', '1520408547', '1520408547', NULL);
创建模型
php think make:model common/Profile
1)关联查询
需求:查询管理员信息及其档案信息
①定义关联关系
档案表pyg_profile中的uid对应于管理员表中的id
以管理员表为主,一个管理员有一个档案,管理员模型中定义关联关系:
//定义管理员-档案关联关系
public function profile()
{
return $this->hasOne('Profile', 'uid', 'id');
}
注:方法名,一般和关联的模型名对应,采用首字母小写的驼峰命名法。
return $this->hasOne(关联model,关联model的联系键,本model的联系键);
第二个参数,可选,默认为 本模型名_id
第三个参数,可选,默认为id
②查询数据
需求:查询管理员数据时,也要查询档案数据
控制器中
$info = \app\common\model\Admin::with('profile')->find(1);
dump($info);
$data = \app\common\model\Admin::with('profile')->select();
dump($data);
1)定义相对的关联
需求:查询档案信息及管理员信息
档案表pyg_profile中的uid对应于管理员表中的id
以档案表为主,一个管理员有一个档案,管理员模型中定义关联关系:
//定义管理员-档案关联关系
public function admin()
{
return $this->belongsTo('Admin', 'uid', 'id');
}
注:方法名,一般和关联的模型名对应,采用首字母小写的驼峰命名法。
return $this->hasOne(关联model,关联外键,关联主键);
第二个参数,可选,默认为 模型名_id
第三个参数,可选,默认为id
②查询数据
需求:查询档案数据时,也要查询管理员数据
控制器中
$info = \app\common\model\Profile::with('admin')->find(1);
dump($info);
$data = \app\common\model\Profile::with('admin')->select();
dump($data);
3、一对多关联
1)关联查询
需求:查询商品分类及其下的商品品牌信息
①定义关联关系
品牌表中的cate_id对应于分类表中的id
以分类表为主,一个分类下有多个品牌,分类模型中定义关联关系:
//定义分类-品牌关联关系
public function brands()
{
return $this->hasMany('Brand', 'cate_id', 'id');
}
注:方法名,一般和关联的模型名对应,采用首字母小写的驼峰命名法,取复数形式。
return $this->hasMany(关联model,关联外键,关联主键);
第二个参数,可选,默认为 模型名_id
第三个参数,可选,默认为id
②查询数据
需求:查询分类数据时,也要查询其下的品牌数据
控制器中
$info = \app\common\model\Category::with('brands')->find(72);
dump($info);
$data = \app\common\model\Category::with('brands')->select();
dump($data);
2)定义相对的关联
需求:查询商品品牌及其所属的商品分类信息
品牌表中的cate_id对应于分类表中的id
以品牌表为主,一个品牌属于一个分类,品牌模型中定义关联关系:(和一对一的相对关联,语法一模一样)
//定义品牌-分类关联关系
public function category()
{
return $this->belongsTo('Category', 'cate_id', 'id');
}
注:方法名,一般和关联的模型名对应,采用首字母小写的驼峰命名法。
return $this->BelongsTo(关联model,关联外键,关联主键);
第二个参数,可选,默认为 模型名_id
第三个参数,可选,默认为id
②查询数据
需求:查询查询商品品牌信息时,也要查询及其所属的商品分类数据
控制器中
$info = \app\common\model\Brand::with('category')->find(1);
dump($info);
$data = \app\common\model\Brand::with('category')->select();
dump($data);
4、绑定属性到父模型
hasOne() 和belongsTo方法后面,调用bind方法,可将属性绑定到父模型中。
注:hasMany方法后不能调用bind方法。
比如,品牌模型中:将分类名称cate_name绑定到品牌模型数据中
public function category()
{
return $this->belongsTo('Category', 'cate_id')->bind('cate_name');
}
控制器中
$info = \app\common\model\Brand::with('category')->find(1)->toArray();
dump($info);
结果结构如下: cate_name和品牌信息属于同一级
$info = ['id'=>1, 'name'=>'华为', 'cate_name'=>'手机'];
对比绑定之前:
$info = ['id'=>1, 'name'=>'华为', 'category'=>['cate_name'=>'手机']];
十六、商品模型(类型)管理
1、商品模型列表
定义路由
创建控制器方法
查询数据
返回数据
实现:
定义路由
创建控制器方法,继承BaseApi
php think make:controller adminapi/Type
创建模型
php think make:model common/Type
查询数据
public function index()
{
//查询数据
$list=\app\common\model\Type::select();
//返回数据
$this->ok($list);
}
2、商品模型详情
定义路由
创建控制器方法
查询数据
返回数据
创建模型(规格名称模型、规格值模型、属性模型)
定义关联模型
查询数据
3、商品模型删除
删除数据
返回数据
注:事务操作 见手册-数据库-事务操作
实现:
商品类型下有商品,则不能删除
删除了商品类型,类型下的规格名、规格值、属性都可以删除了。
普通的删除:
使用事务操作进行删除: