zoukankan      html  css  js  c++  java
  • 全栈微信小程序商城 学习笔记之八 专题、分类、商品详情接口

    Banner相关表分析

    banner指banner位,banner_item是具体的banner项
    一个banner位有多个banner_item,一个banner_item是能属于一个banner,所以是一对多的关系
    两者通过banner_item的banner_id外键关联

    banner表

    banner_item表

    banner_item 还有一个字段img_id与image表关联

    image表

    模型关联

    applicationapimodelBanner.php

    <?php
    
    namespace appapimodel;
    
    class Banner extends Model
    {
        public function items()
        {
            //参数:1.关联的模型BannerItem2.关联BannerItem的外键3.当前模型的主键
            return $this->hasMany('BannerItem', 'banner_id', 'id');
        }
    }
    

    使用
    applicationapicontrollerv1Banner.php

    <?php
    
    namespace appapicontrollerv1;
    
    
    use appapivalidateIDMustBePositiveInt;
    use appapimodelBanner as BannerModel;
    
    /**
     * Banner资源
     */ 
    class Banner
    {
        public function getBanner($id)
        {
            $validate = new IDMustBePositiveInt();
            $validate->goCheck();
            $banner = BannerModel::with('items')->find($id);
            if (!$banner) {
                throw new BannerMissException();
            }
            return $banner;
        }
    }
    

    查看返回结果

    使用命令新建Image模型

    php think make:model api/Image
    
    <?php
    
    namespace appapimodel;
    
    use thinkModel;
    
    class Image extends Model
    {
    
    }
    

    关联到Image模型
    applicationapimodelBannerItem.php

    <?php
    
    namespace appapimodel;
    
    use thinkModel;
    
    class BannerItem extends Model
    {
    
        public function img()
        {
            //一对一关系使用
            //参数:1.关联模型名2.关联外键3.关联模型主键
            return $this->belongsTo('Image', 'img_id', 'id');
        }
    }
    

    使用
    applicationapicontrollerv1Banner.php

    class Banner
    {
        public function getBanner($id)
        {
            $validate = new IDMustBePositiveInt();
            $validate->goCheck();
            //嵌套关联查询
            $banner = BannerModel::with(['items', 'items.img'])->find($id);
            return $banner;
        }
    }
    

    返回的数据

    从规范性来看,把查询的代码封装下更好
    applicationapimodelBanner.php

    class Banner extends Model
    {
        public function items()
        {
            return $this->hasMany('BannerItem', 'banner_id', 'id');
        }
        public static function getBannerById($id)
        {
            $banner = self::with(['items','items.img'])
                ->find($id);
    
            return $banner;
        }
    }
    

    controller/v1/banner.php

    class Banner
    {
        public function getBanner($id)
        {
            $validate = new IDMustBePositiveInt();
            $validate->goCheck();
            $banner = BannerModel::getBannerById($id);
            if (!$banner) {
                throw new BannerMissException();
            }
            return $banner;
        }
    }
    

    隐藏模型字段

    返回的因为是受保护的类型,不能直接访问其成员,要先转成数组

    class Banner
    {
        public function getBanner($id)
        {
            $validate = new IDMustBePositiveInt();
            $validate->goCheck();
            $banner = BannerModel::getBannerById($id);
            $data = $banner->toArray();
            unset($data['dalete_time']); //删除delete_time字段
            if (!$banner) {
                throw new BannerMissException();
            }
            return $data;
        }
    }
    

    上面的方法不推荐使用,建议用模型的方法

    class Banner
    {
        public function getBanner($id)
        {
            $validate = new IDMustBePositiveInt();
            $validate->goCheck();
            $banner = BannerModel::getBannerById($id);
            if (!$banner) {
                throw new MissException([
                    'msg' => '请求banner不存在',
                    'errorCode' => 40000
                ]);
            }
            //传进一个要隐藏的数组
            $banner->hidden(['delete_time', 'update_time']) 
            //只显示数组内的字段
            // $banner->visible(['id']) 
            return $banner;
        }
    }
    

    查看返回结果

    在模型内部隐藏字段

    applicationapimodelBanner.php

    class Banner extends Model
    {
        protected $hidden = ['id']; // visible方法也可用
    }
    

    查看返回结果

    图片资源url配置

    如果图片存在本地服务器上,在数据库url字段用相对路径,可以适应不同的根目录配置。
    在application新建文件夹extra,这里面的配置文件会被tp5自动加载
    applicationextrasetting.php

    <?php
    return [
        //将图片资源images文件夹放在zergpublic文件夹下
        'img_prefix' => 'http://127.0.0.1:8000/public/images'
    ];
    

    读取器的妙用

    applicationapimodelImage.php

    <?php
    
    namespace appapimodel;
    
    use thinkModel;
    
    class Image extends Model
    {
        protected $hidden = ['delete_time', 'id', 'from'];
        //这个命名是特定的,表示是读取器,get和attr是固定式,url是字段名
        public function getUrlAttr($value, $data) {
            $finalUrl = $value;
            if ($data['from'] == 1) {
                //本地图片
                //使用了助手函数config这里可以成功拿到图片的基地址
                $finalUrl = config('setting.img_prefix').$value;
            }
            //如未通过以上判断说明是网络图片
            return $finalUrl;
        }
    }
    

    查看返回结果

    自定义模型基类

    提取一个基类优化结构
    命令
    php think make:model api/BaseModel

    applicationapimodelBaseModel.php

    <?php
    
    namespace appapimodel;
    
    use thinkModel;
    
    class BaseModel extends Model
    {
        protected $hidden = ['delete_time'];
        protected function prefixImgUrl($value, $data){
            $finalUrl = $value;
            if($data['from'] == 1){
                $finalUrl = config('setting.img_prefix').$value;
            }
            return $finalUrl;
        }
    }
    

    重新定义读取器
    Img.php

    <?php
    
    namespace appapimodel;
    
    use thinkModel;
    
    class Image extends BaseModel
    {
        protected $hidden = ['delete_time', 'id', 'from'];
    
        public function getUrlAttr($value, $data)
        {
            return $this->prefixImgUrl($value, $data);
        }
    }
    

    让所有的模型都继承自己写的BaseModel

    定义API版本号

    能更好适应业务变化
    复制applicationapicontrollerv1Banner.php到
    applicationapicontrollerv2Banner.php (仅用于测试)

    新增路由
    application oute.php

    Route::get('api/:version/banner/:id', 'api/:version.Banner/getBanner');
    

    此时可以通过更改url调用接口的不同版本

    专题接口模型分析

    一个theme下面有多个商品product,如果用一个字段表示拓展性就差了,这里可以新增中间表theme_product表示

    theme表

    product表

    theme_product表

    创建Theme和Product模型类
    applicationapimodelTheme.php

    class Theme extends BaseModel
    {
        //
    }
    
    class Product extends BaseModel
    {
        //
    }
    

    没必要建立中间表的模型类,tp5已经自动处理

    一对一关系

    applicationapimodelTheme.php

    class Theme extends BaseModel
    {
        public function topicImg()
        {
            return $this->belongsTo('Image', 'topic_img_id', 'id');
        }
    
        public function headImg()
        {
            return $this->belongsTo('Image', 'head_img_id', 'id');
        }
    }
    

    一对一也有主从关系
    主题Theme有关联Image表的外键,而Image表中并没有此类字段,如果用Image关联Theme则要用hasOne

    theme表

    image表

    theme接口验证与重构

    定义路由
    application oute.php

    Route::get('api/:version/theme', 'api/:version.Theme/getSimpleList');
    

    applicationapicontrollerv1Theme.php

    class Theme
    {
        //返回一组theme模型
        public function getSimpleList($ids='') {
    
        }
    }
    

    applicationapivalidateIDCollection.php

    <?php
    namespace appapivalidate;
    
    
    class IDCollection extends BaseValidate
    {
        // 千万不要在require|checkIDS中加空格
        // 不然你会哭的
        // 源码中是没有去处多余空格的判断的
        // 这将导致验证不执行
        protected $rule = [ 
            'ids' => 'require|checkIDs'
        ];
        //这个message和rule一样也是内置的,如果规则未通过则用此信息通知
        protected $message = [ 
            'ids' => 'ids参数必须为以逗号分隔的多个正整数,仔细看文档啊'
        ];
    
        protected function checkIDs($value)
        {
            $values = explode(',', $value);
            if (empty($values)) {
                return false;
            }
            foreach ($values as $id) {
                if (!$this->isPositiveInteger($id)) {
                    // 必须是正整数
                    return false;
                }
            }
            return true;
        }
    }
    

    上面的如果要生效还要更改下验证器基类
    applicationapivalidateBaseValidate.php

    class BaseValidate extends Validate
    {
        // 检测所有客户端发来的参数是否符合验证类规则
        public function goCheck()
        {
            $request = Request::instance();
            $params = $request->param();
            //同时校验
            $result = $this->batch()->check($params);
            if (!$result) {
                $e = new ParameterException([
                    'msg' => $this->error,
                    'code' => 400,
                    'errorCode' => 10002
                ]);
              throw $e;
            } else {
                return true;
            }
        }
        //把applicationapivalidateIDMustBePositiveInt.php的方法移动到这
        protected function isPositiveInteger($value, $rule='', $data='', $field='')
        {
            if (is_numeric($value) && is_int($value + 0) && ($value + 0) > 0) {
                return true;
            }
            //不能返回字符串,否则会被认为通过验证
            //return $field . '必须是正整数';
            return false;
        }
    }
    

    applicationapicontrollerv1Theme.php

    class Theme
    {
        public function getSimpleList($ids='') 
        {
            (new IDCollection())->goCheck();
            return 'success'
        }
    }
    

    测试,如果返回success表示通过验证

    完成Theme简要信息接口

    applicationapicontrollerv1Theme.php

    class Theme
    {
        public function getSimpleList($ids='') {
            (new IDCollection())->goCheck();
            $ids = explode(',', $ids);
            $result = ThemeModel::with('topicImg,headImg')->select($ids);
    
            if (!$result) {
                throw new ThemeException();
            }
            return $result;
        }
    }
    

    applicationlibexceptionThemeException.php

    <?php
    
    namespace applibexception;
    
    
    class ThemeException extends BaseException
    {
        public $code = 404;
        public $msg = '指定主题不存在,请检查主题ID';
        public $errorCode = 30000;
    }
    

    把不需要返回给客户端的字段隐藏
    applicationapimodelTheme.php

    class Theme extends BaseModel
    {
        protected $hidden = ['delete_time', 'topic_img_id', 'head_img_id'];
        //...
    }
    

    theme详情接口

    多对多关系
    applicationapimodelTheme.php

    class Theme extends BaseModel
    {
        public function products()
        {
            //1.关联模型名2.中间表名3.中间表的当前模型外键4.中间表的当前模型关联键
            return $this->belongsToMany(
                'Product', 'theme_product', 'product_id', 'theme_id'); 
        }
    }
    

    applicationapicontrollerv1Theme.php

    class Theme
    {
        public function getSimpleList($ids='') {
            (new IDCollection())->goCheck();
            $ids = explode(',', $ids);
            $result = ThemeModel::with('topicImg,headImg')->select($ids);
    
            if (!$result) {
                throw new ThemeException();
            }
            return $result;
        }
        //theme详情接口
        public function getComplexOne($id)
        {
            return 'success';
        }
    }
    

    定义路由
    application oute.php

    Route::get('api/:version/theme/:id', 'api/:version.Theme/getComplexOne');
    

    使用时发现路由不能正常使用,会被上一个theme路由匹配成功

    路由使用完整匹配
    applicationconfig.php

    return [
        //...
        'route_complete_match'   => true,
        //...
    ];
    

    测试,返回success

    Theme详情

    applicationapicontrollerv1Theme.php

    class Theme
    {
        public function getSimpleList($ids='') {
            (new IDCollection())->goCheck();
            $ids = explode(',', $ids);
            $result = ThemeModel::with('topicImg,headImg')->select($ids);
    
            if (!$result) {
                throw new ThemeException();
            }
            return $result;
        }
        public function getComplexOne($id)
        {
            (new IDMustBePositiveInt())->goCheck();
        }
    }
    

    applicationapimodelTheme.php

    class Theme extends BaseModel
    {
        public static function getThemeWithProducts($id)
        {
            $themes = self::with('products,topicImg,headImg')
                ->find($id);
            return $themes;
        }
    }
    

    applicationapicontrollerv1Theme.php

    public function getComplexOne($id)
    {
        (new IDMustBePositiveInt())->goCheck();
        $theme = ThemeModel::getThemeWithProducts($id);
        if(!$theme){
            throw new ThemeException();
        }
        return $theme;
    }
    

    REST的合理利用

    新建一个读取器
    applicationapimodelProduct.php

    class Product extends BaseModel
    {
        //pivot如果是多对多关系会tp5自动带上的中间表属性,这里不需要
        protected $hidden = [
            'delete_time', 'main_img_id', 'pivot', 'from', 'category_id',
            'create_time', 'update_time']; 
    
        public function getMainImgUrlAttr($value, $data)
        {
            return $this->prefixImgUrl($value, $data);
        }
    }
    

    查看返回结果

    最近新品

    使用模型插入数据库,update_time、create_time、delete_time会自动更新
    新建路由
    application oute.php

    Route::get('api/:version/product/recent', 'api/:version.Product/getRecent');
    

    applicationapicontrollerv1Product.php

    class Product 
    {
        public function getRecent($count = 15)
        {
            (new Count())->goCheck();
            return 'success';
        }
    }
    

    新建验证器
    applicationapivalidateCount.php

    <?php
    namespace appapivalidate;
    
    class Count extends BaseValidate
    {
        protected $rule = [
            'count' => 'isPositiveInteger|between:1,15',
        ];
    }
    

    applicationapimodelProduct.php

    class Product extends BaseModel
    {
        public static function getMostRecent($count)
        {
            $products = self::limit($count)
                ->order('create_time desc') //排序
                ->select();
            return $products;
        }
    }
    

    applicationapicontrollerv1Product.php

    <?php
    
    namespace appapicontrollerv1;
    
    use appapimodelProduct as ProductModel;
    use appapivalidateCount;
    use applibexceptionProductException;
    
    class Product
    {
        public function getRecent($count = 15)
        {
            (new Count())->goCheck();
            $products = ProductModel::getMostRecent($count);
            if (!$products)
            {
                throw new ProductException();
            }
            return $products;
        }
    }
    

    applicationlibexceptionProductException.php

    <?php
    
    namespace applibexception;
    
    
    class ProductException extends BaseException
    {
        public $code = 404;
        public $msg = '指定商品不存在,请检查商品ID';
        public $errorCode = 20000;
    }
    

    使用数据集还是数组?

    观察接口的返回结果,有一个summary字段是不需要的

    applicationapimodelProduct.php

    class Product extends BaseModel
    {   
        //这里不能直接加summary,否则所有与Product相关联的接口都不会显示这个字段
        protected $hidden = [
            'delete_time', 'main_img_id', 'pivot', 'from', 'category_id',
            'create_time', 'update_time']; 
    }
    

    在接口中对summer做隐藏

    applicationapicontrollerv1Product.php

    class Product
    {
        public function getRecent($count = 15) {
            (new Count())->goCheck();
            $products = ProductModel::getMostRecent($count);
            if (!$products)
            {
                throw new ProductException();
            }
            //将数组转成数据集
            $collection = collection($products)
            $products = $collection->hidden(['summary'])
            return $products
        }
    }
    

    数据集包含了许多对数据的有用操作,可以在配置文件配置,让数据集不再以二维数组的方式返回而是以数据集类的方式返回
    applicationdatabase.php

    return [
        //...
        'resultset_type'  => 'collection',
        //...
    ];
    

    applicationapicontrollerv1Product.php

    class Product
    {
        (new Count())->goCheck();
        $products = ProductModel::getMostRecent($count);
        //返回数据集类,原来的判空方式不再适用,这里使用数据集类的isEmpty方法判空
        //其他接口使用了模型的select方法返回数据集的地方也要改
        if ($products->isEmpty())
        {
            throw new ProductException();
        }
        $products = $products->hidden(['summary']);
        return $products;
    }
    

    查看结果

    分类接口

    application oute.php

    Route::get('api/:version/category/all', 'api/:version.Category/getAllCategories');
    

    applicationapimodelCategory.php

    class Category extends BaseModel
    {
        public function img()
        {
            return $this->belongsTo('Image', 'topic_img_id', 'id');
        }
    }
    

    applicationapicontrollerv1Category.php

    class Category
    {
        public function getAllCategories()
        {
            //[]代表查询所有
            //以下语句相当于CategoryModel::with('img')->select()
            $categories = CategoryModel::all([], 'img'); 
            if($categories -> isEmpty()){
               throw new CategoryException();
            }
            return $categories;
        }
    }
    

    定义异常
    applicationlibexceptionCategoryException.php

    <?php
    
    namespace applibexception;
    
    
    class CategoryException extends BaseException
    {
        public $code = 404;
        public $msg = '指定类目不存在,请检查商品ID';
        public $errorCode = 20000;
    }
    

    分类商品接口

    新建路由
    application oute.php

    Route::get('api/:version/product/by_category', 'api/:version.Product/getAllInCategory');
    

    applicationapicontrollerv1Product.php

    class Product
    {
        public function getAllInCategory($id)
        {
            (new IDMustBePositiveInt())->goCheck();
            $products = ProductModel::getProductsByCategoryID(
                $id);
            if ($products->isEmpty()) {
                throw new ProductException();
            }
            $products = $products->hidden(['summary']);
            return $products;
        }
    }
    

    applicationapimodelProduct.php

    class Product extends BaseModel
    {
        public static function getProductsByCategoryID($categoryID)
        {
            $products = self::where('category_id', '=', $categoryID) -> select();
            return $products;
        }
    }
    
  • 相关阅读:
    failed to push some refs to 'git@github.com:cq1415583094/MyBatis.git'解决办法
    MyBatis 安装和配置
    MyBatis入门
    LinkedList 源码分析
    ArrayList 源码分析
    什么是注解?
    什么是泛型?
    什么是反射?
    php针对各数据库系统对应的扩展
    DedeCMS文章标题长度最全修改方法
  • 原文地址:https://www.cnblogs.com/Qyhg/p/14106689.html
Copyright © 2011-2022 走看看