博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
微信小程序后端笔记(PHP)
阅读量:6159 次
发布时间:2019-06-21

本文共 14080 字,大约阅读时间需要 46 分钟。

微信小程序商城构建全栈应用

  • php+微信小程序全栈应用

软件/素材

  • mac os 10.13.3
  • PhpStorm 2018
  • Postman
  • XAMPP 7.0.2-1
  • ThinkPHP 5.0.7

项目目录结构

├─application           应用目录  ├─api                公共模块目录(可以更改)  │  │-controller      控制器目录  (版本以及业务)  │  │-model           模型目录  (关联模型处理)  │  │-service         模型服务层(相对复杂的业务处理)  │  └─validate        验证层     (客户端数据验证)  ├─extra              自定义公共资源层(tp5自带的)  ├─lib                模块目录  │  ├─enum            枚举  │  └─exception       全局异常处理目录  │  ├─command.php        命令行工具配置文件  ├─common.php         公共函数文件  ├─config.php         公共配置文件  ├─route.php          路由配置文件  ├─tags.php           应用行为扩展定义文件  └─database.php       数据库配置文件复制代码

笔记

第八章

数据表关系分析 (写着写着就绕了)

  1. 数据表之间的关系: 1 对 1 1 对多 多对多
    • 如何判断数据表之间的结构
  2. 首先确立是否是一个多对多的关系
    • 查看表与表之间是否存在双方的外建均能被多个表调用,如果不是那就去除多对多关系
  3. 1 对 1 1 对多
    • 在 thinkphp 中问题不大
    • 如何去分析 1 对多或 1 对 1
    • 1 对 1 的关系中, 两个表直接同时并且单次被执行,就是说一个关联请求中,表 1 一次只可以调用一个表 2 的元素,并且表 2 也只是被调用了一次
    • 1 对多 的关系中, 表 1 通过一个外建,调用了多个表 2 的数据,并且表 2 的数据不能属于多个表 1,这样就是 1 对多的表现了

模型关联(我们确立了 er 关系再来做这么的一个关联)

  1. 模型关联查询

    • 在我们的 model 是作为一个 ORM 模式的模型结构
    • 在这之前我们就已经定义了模型了
    • 我们有两个模型 Banner 与 BannerItem
    • tp5 对我们提供了关联查询的方法 hasMany
    • 定义关联查询
    // 在当前模型 Banner 新建类  类名自定义喜欢什么来什么    // 函数体要写在 Banner 这个主模型中,BannerItem是被关联模型    // 调用模型关联时要清晰的知道 外键 以及主建(某程度下是不用写后面两个,不建议)    public function items () {            // 关联查询方法hasMany 关联模型      外建      当前模型 banner id主建           return $this->hasMany('BannerItem','banner_id','id');    }复制代码
    • 调用关联查询
    //  在调用 模型的时候加上 with这么个方法 (括号内填写的就是刚才定义的函数名)$banner = BannerModel::with('items')->find($id);复制代码
  2. 模型嵌套关联查询

    • 在我们的 查询中 会存在被关联体中还关联着变得关联体,在 tp5 中就形成了嵌套查询
    • 当然 tp5 也给我们提供了方法:belongsTo
    • 嵌套关系 Banner -> BannerItem -> Image (这里就存在了多重的嵌套)
    • 模型 Banner BannerItem Image
    • 是 BannerItem 关联 Image 所以关联函数我们写在 BannerItem 中
    • 定义嵌套查询
    public function img() {//        处理方法名其他都是一样的,这里就不多说了      return $this->belongsTo('Image','img_id','id');  }复制代码
    • 调用查询 (这个比较关键,不过还是很简单的)
    // with 可以是字符串也可以是数组(嵌套关联时就会用数组)// 为什么是items.img 而不是 直接img呢,因为是嵌套关系,在模型中可以嵌套这里也是可以的// 但是在 嵌套时 是items 关联的 img ,这里就会用.来链接// 这个解释比较绕但是,知道方法就是要这样去用的就好啦$banner = BannerModel::with(['items','items.img'])->find($id);复制代码

隐藏模型字段 (模型自带)

  1. hidden 方法隐藏字段
// 数据      方法      字段名 $banner->hidden(['字段名例:id'])复制代码
  1. visible 只显示的字段
$banner->visible(['字段名例:id','update_time])复制代码

模型内部隐藏字段 (自定义模型的内部隐藏,把一些前端不需要的字段隐藏了)

  • hidden 隐藏
  • 直接在 model 定义的模型内添加方法 (以 Banner 为例)
namespace app\api\model;use think\Model;class Banner extends Model{     // 直接添加 $hidden的数组填入要隐藏的字段即可     // visible 等方法用法一样,那个模型内部的字段要隐藏就在那个模型内部设置    protected $hidden = ['id'];    public function items () {                                // 关联模型           外建                当前模型 banner id主建        return $this->hasMany('BannerItem','banner_id','id');    }    public static function getBannerByID($id) {        $banner = self::with(['items','items.img'])->find($id);        return $banner;    }}复制代码

自定义配置

  • /application/extra (extra 自己新建的,凡是放在这里面的配置文件都会被自动加载)
  • 手动配置一个本地的 img 图片路径
  1. 在 extra 下 新建 setting.php
return [     //  名称                 域名   路径(直接放在public下的images就是这样写就可以了)     'img_prefix' => 'http://zerg.cn/images' ];复制代码
  1. 使用自定义变量
    • 因为是在 extra 内部定义的所以会自动调用,那么我们用 config 就可以去掉用到了
    // 配置文件名.变量名config('setting.img_prefix');复制代码

静态文件存放

  • 静态的外部文件,例如图片啊文本啊等的文件,必须放在 public 这个公共目录下
  • 并不是放在 application 的这个开发目录下,因为 tp5 的架构里面只有 public 这个目录是对外开放的
  • 所以文件都必须是要放在 public 目录下

tp 模型读取器 (数据拼合)

  • 为了获取数据/修改数据,tp5 给出了一个读取器的方法
  • 用来给我们读取数据修改数据用的
  • 那个模型要修改数据就在哪个模型定义
  1. 定义读取器(其实也是一个函数方法)
    • 读取器命名规范 开头 get 必须有 + 读取数据的名称并且开头要大写例 Url + Attr 必须加的(利用驼峰命名法)
    • getUrlAttr (完整的编写,除了中间的那个数据,其他都是必须有的,中间数据名开头必须大写)
    • 传入一个值,名字自定义 (这个传入的数据其实就是我们要获取到要修改的数据)
    • 每一次传入一个数据,有多个输出就会重复的执行读取器
    • 因为在我们的业务逻辑中会调用到当前模型的其他数据,但是第一个参数只是获取到的是当前读取器的数据,并无法读取到其他的数据
    • 所以添加了第二个参数 (这个参数会给我们返回一个这个模型的数据,就是所有的数据)
public function getUrlAttr ($value,$data) { }复制代码
  1. 使用读取器 (做数据的修改然后返回)
public function getUrlAttr ($value) {        // 这里我们只是做了一个自定义的 变量和url路径的拼接        return config('setting.img_prefix').$value;    }复制代码
  1. 业务逻辑添加
public function getUrlAttr ($value,$data) { $finalUrl = $value;     // 判断是否要拼接 if ($data['from'] === 1) {     $finalUrl = config('setting.img_prefix') . $value; } return $finalUrl; }复制代码

自定义基类 (面向对象,提取模型读取器)

  • 一开始这样做会觉得好像代码还多了啊,这么不就是做无用功吗,在业务不断增加的时候,后期修改就可以看出来好处了
  • 集中业务逻辑
  • 创建 BaseModel.php 作为模型基类
  • 把让所有的模型都继承这个基类
  1. 把读取器提取到 模型基类 (这样做是一个面向对象的思想)
    • 但是提取了模型基类后我们所有的子模型都会自动的去执行模型
    • 这样可能会造成一些数据的变更和错误,比如说,两个命名一样但是代表的数据不同是就会出现错误
    • 所以我们把它封装为一个自调用的方法
    // BaseModel// 读取器protected function prefixImgUrl ($value,$data) {    $finalUrl = $value;    if ($data['from'] === 1) {        $finalUrl = config('setting.img_prefix') . $value;    }    return $finalUrl;}复制代码
  2. 子模型调用基类方法
    • Image
    public function getUrlAttr ($value,$data) {    return $this->prefixImgUrl($value,$data);}复制代码

定义 api 版本号

  • 在互联网的项目中,我们会对项目版本对升级,以及业务逻辑改变和变更
  • 同时也是需要去兼容旧版本,所以会保留旧版本的 api
  1. 开发开闭原则
    • 代码对拓展开发,对修改封闭
    • 添加功能直接以拓展的方式添加就可以,不需要去改变代码
    • 修改是封闭的,业务变更上升版本
    • 不可以修改原来的版本代码,会破坏了原版本的代码,和影响功能调用的风险
    • 需要修改就要添加新的版本
  2. 多版本
    • 版本的分离,新旧版本不发生冲突
    • 新老版本的兼容问题
    • 给用户缓冲时间,也不能兼容太多的版本,成本太高
    • v1 做 v1 版本层
    • v2 做 v2 版本层

路由 api 动态变更

//              动态版本 实现传什么就调用什么版本的api,同时也是要修改版本指向接口//                 传 v1 就是 v1//                 传 v2 就是 v2           动态写入Route::get('api/:version/banner/:id','api/:version.Banner/getBanner');复制代码

一对一关系选择关联方法

  1. belongsTo
    • 在有外建的表内请求就用 belongsTo
  2. hasOne
    • 在没有外建的表亲求就用 hasOne

多对多查询 (belongsToMany)

  • 多对多的查询呢 就比一对多和 1 对 1 的查询要多了一个参数
  • 在参数中第二个是放入第三个表也就是中间表
public function products () {                                    // 关联表名        中间表名                 关联表id            主建    return $this->belongsToMany('Product','theme_product','product_id','theme_id');}复制代码

开启路由完整匹配模式

  • 开我们开发的过程中难免会有 api 相同当是请求的方式以及传参的不同,但是又需要相同的 api 名称
  • 在我们的 tp5 中,会自动追寻一个半路径的匹配,所以当匹配到了相关的路由时就会停止匹配
  • 但是这样返回的结果肯定不是我们要的,所以就要开启这个完整的路由匹配模式
  • 在 config.php 配置文件中,我们就可以来更改了
//  只有找到这句话改变就可以了   false -> true // 路由使用完整匹配 'route_complete_match'   => true,复制代码

合理利用数据冗余

  • 在查询量上来的时候避免数据量大多表查询之间耗时
  • 合理的利用数据冗余来减少联合表的查询减少查询时间
  • 但不要太过多但使用,只是为了减少数据库压力
  • 在数据库中做相关的优化

collection 字符集

  • 我们使用获取到的数据是字符集更方便让我们来修改数据
  1. tp5 修改获取返回数据 (/application/database.php)
// 找到这个吧 arr改为 collection // 数据集返回类型 'resultset_type'  => 'collection',复制代码
  1. 使用字符集就可以轻松的临时隐藏字段

    • 当我们在开发的过程中,不是所有业务逻辑都需要隐藏的字段,我们就不可以在关联模型中直接就隐藏字段
    • 我们会使用临时隐藏字段
    • 当然数组我们是不可以直接这样来隐藏的,但是使用字符集的话就可以直接的去使用函数进行数据的隐藏
    // 使用hidden进行隐藏$products = $products->hidden(['summary']);复制代码
  2. 字符集判空

    • isEmpty 内置函数
// 判断空抛出异常     if ($products->isEmpty()) {         throw new ProductException();     }复制代码

##第九章

service (建立在 model 上的,用来处理复制的业务)

  • 在我们的 tp5 中,我们的 model 代表的一个很重要的位置
  • 可以写业务逻辑,也访问数据库
  • 但是 service 不可以用来访问数据库,因为上建立在 model 之上的
  • 我们都会把复杂的业务逻辑放在 service 层中

公共应用文件 common.php

  • 编写公共的 http 请求
/** @param string $url get 请求地址* @param int $httpCode 返回状态码* @return mixed*/function curl_get ($url,&$httpCode = 0) { $ch = curl_init(); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);//    不做证书校验,部署在linux环境下请改为true curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); curl_setopt($ch,CURLOPT_CONNECTTIMEOUT,10); $file_contents = curl_exec($ch); $httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch); return $file_contents;}复制代码

模型插入数据(create)

  • 在 tp5 中如何向数据库插入数据
  • tp5 模型给我们准备了 create 的方法
//  模型名   create方法 数组传入要添加的字段和数据 $user = UserModel::create([         'openid' => $openid     ]);复制代码

动态传入数值随机生成字符串方法

/* * 生成随机字符串 */function getRandChar ($length) {    $str = null;    $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";    $max = strlen($strPol) - 1;    for ($i=0;$i < $length; $i++) {        $str .= $strPol[rand(0,$max)];    }    return $str;}复制代码

文件缓存 chache

  • 使用 cache 写入缓存
  • 使用文件存储的方式
  • 缓存的地址在目录文件/runtime/cache 文件内
$request = cache($key,$value,$expire_in);复制代码

路由分组

  • 由于我们 api 接口的不断增加
  • 在一个分类中会有很多的相同的接口路由
  • 这个时候如果我们业务的变更修改起来就会很麻烦
  • 所以我们是用来路由分组来实现
  • group 方法
  • 第一个是公共的路由部分,第二个是一个闭包(也就是一个 function 的方法)
  • 在里面还是安装路由一样去定义就可以了
  • 也能提高路由的效率
//Route::get('api/:version/product/recent','api/:version.Product/getRecent');//Route::get('api/:version/product/by_category','api/:version.Product/getAllInCategory');//Route::get('api/:version/product/:id','api/:version.Product/getOne',[],['id'=>'\d+']);Route::group('api/:version/product', function () {    Route::get('/recent','api/:version.Product/getRecent');    Route::get('/by_category','api/:version.Product/getAllInCategory');    Route::get('/:id','api/:version.Product/getOne',[],['id'=>'\d+']);});复制代码

关联模型下个关联数据排序(tp5 没有的,重点)

  • 使用 模型+query 添加排序
// 关联模型 imgs  properties 查询 // 模型的嵌套 imgurl public static function getProductDetail ($id) {     // 在 with 中 嵌套function     // 在内部添加 query     $product = self::with(['imgs' => function ($query) {         $query->with(['imgUrl'])->order('order','asc');     }])->with(['properties'])->find($id);     return $product; }复制代码

使用 关联模型 添加/更新数据

  • 添加数据的方法有很多,我们来使用一下关联模型的方法
  • 两个的区别在于 修改操作的 关联 不可以用括号
// 调用 user 中的 address 关联 使用 save方法添加数据    $user->address()->save($dataArray);    // 调用 user 中的 address 关联 使用 save方法修改数据    $user->address->save($dataArray);复制代码

第十章

前置操作

  • 在我们编写 api 业务逻辑的时候,我们会想在调用 api 接口之前,需要满足某些条件
  • 这样才可以去访问我们的接口中的业务逻辑
  • 所以我们要在做一个前置操作,抵挡不满足条件的抛出异常
  1. tp5 中使用前置操作需要基础自带的一个基类 Controller
  2. 定义一个名为 $beforeActionList 的数组
use think\Controller class Address extends Controller {     // 定义前置属性     // 第一个字段是 访问api接口前 需要 访问的一个前置方法     // 箭指的 是一个数组     // 数组内部定义一个箭指数据,也可以直接是一个字符串(内部填入api接口函数就可以了)     // 否则向下面这样写     // 多api编写     protected $beforeActionList = [         'first' => ['only' => 'second,third']     ];     // 触发api前 执行的前置函数     protected function first () {         echo 'first';     }     // api接口     public function second () {         echo 'second';     }     // api接口     public function third () {         echo 'third';     } }复制代码

重构前置验证操作 (实现面向对象)

  • 提取验证业务逻辑到 service 的基类中
  • 提取前置方法到 BaseController 的基类中
  • 继承基类,执行前置方法
  1. 提取出一个前置的基类 BaseController (继承内置 Controller)
use app\api\service\Token as TokenService;// 继承class BaseController extends Controller{ // 前置方法 // 验证初级权限作用域,用户和cms都可以访问 protected function checkPrimaryScope () {     // 向Token调用验证方法     TokenService::needPrimaryScope(); } // 验证权限,只有用户可以访问,cms无法访问 protected function checkExclusiveScope () {     TokenService::needExclusiveScope(); }}复制代码
  1. 提取验证业务逻辑(因为是 token 相关的就归并到 token 的 service 业务层中)
// 重构前置方法,验证权限 // 用户和cms管理员都可以访问的权限 public static function needPrimaryScope () {     // 调用token中的方法获取scope     $scope = self::getCurrentTokenVar('scope');     // 判断是否存在     if ($scope) {         // 判断 scope的权限大小         if ($scope >= ScopeEnum::User) {             return true;         } else {             throw new ForbiddenException();         }     } else {         throw new TokenException();     } }复制代码
  1. 继承 BaseController 基类使用前置方法
// 继承基类class Address extends BaseController{ // 调用前置的方法 protected $beforeActionList = [     // 前置验证的方法名                   需要前置验证的函数     'checkPrimaryScope' => ['only' => 'createOrUpdateAddress'] ]; /*  * @url api/v1/address  */ public function createOrUpdateAddress () { }}复制代码

验证器数据自定义子项验证

  • 自定义子项验证,通过自定义的方法调用实现
  • 当我们在验证时,传入的是一个二维数组,就可以使用来验证子项
  • 我们就自定义一个验证的方法,通过基类的验证的调用
// 整体验证 protected $rule = [   'products' => 'checkProducts' ]; // 数据子项的验证 protected $singleRule = [     'product_id' => 'require|isPositiveInteger',     'count' => 'require|isPositiveInteger' ]; /*  * 自定义整体验证  */ protected function checkProducts ($values) {     // 验证是不是数组     if (!is_array($values)) {         throw new ParameterException([             'msg' => '商品参数不正确'         ]);     }     // 验证不为空     if (empty($values)) {         throw new ParameterException([             'msg' => '商品列表不能为空'         ]);     }     // 循环对每一项进行验证     foreach ($values as $value) {         $this->checkProduct($value);     }     return true; } // 基础调用子项验证 protected function checkProduct ($value) {     $validate = new BaseValidate($this->singleRule);     $result = $validate->check($value);     if (!$result) {         throw new ParameterException([             'msg' => '商品参数不正确'         ]);     } }复制代码

自动添加时间戳(TP5 内置添加时间戳)

  • 在我们的操作中,我们的数据中会带有数据,tp5 为我们提供了自动添加时间戳
  1. 找到自己要添加的时间戳的模型 我是在 order 添加那我就去 orde 人的模型中
  2. $autoWriteTimestamp 添加为 true,需要是模型的方式才可以使用的
  3. 创建 修改 删除
  4. 默认为 create_time update_time delete_time
  5. 修改方法名 在模型下修改
// 自动写入时间戳    protected $autoWriteTimestamp = true;    // 修改字段名    //         内置名称           自定义修改的名称    protected $createTime = 'create_timestamp';复制代码

Tp5 事务应用

  • 在我们的应用中可能会出现分步的操作,可能会本地与服务端出现不一致
  • 所以我们使用事务来做处理
  • 在中间出现错误就会把数据回滚保持数据的一致性
// 开头加入开始     Db::startTrans();     try {         $orderNo = $this->makeOrderNo();         $order = new \app\api\model\Order();         $order->user_id = $this->uid;         $order->order_no = $orderNo;         $order->total_price = $snap['orderPrice'];         $order->total_count = $snap['totalCount'];         $order->snap_img = $snap['snapImg'];         $order->snap_name = $snap['snapName'];         $order->snap_address = $snap['snapAddress'];         $order->snap_items = json_encode($snap['pStatus']);         $order->save();         $orderID = $order->id;         $create_time = $order->create_time;         foreach ($this->oProducts as &$p) {             $p['order_id'] = $orderID;         }         $orderProduct = new OrderProduct();         $orderProduct->saveAll($this->oProducts);         // 结尾加上结束         Db::commit();         return [             'order_no' => $orderNo,             'order_id' => $orderID,             'create_time' => $create_time         ];     } catch (Exception $ex) {         // 异常出现回滚         Db::rollback();         throw $ex;     }复制代码

引入没有命名空间的文件与调用(Loader),手动引入微信支付 php

  • 使用 loader 的 import 方法
  • extend/WxPay/WePay.Api.php
//         文件开头的第一个  文件路径       // 类的名称Loader::import('WxPay.WxPay',EXTEND_PATH,'.Api.php');// 调用// 调用的时候前面要加反斜杠$wxOrderData = new \WxPayUnifiedOrder();复制代码

TP5 模型实现数据减少 setDec

// 前面是查询  第一个数是写要改变的字段  第二个是要减少的数量Product::where('id','=',$singlePStatus['id'])->setDec('stock',$singlePStatus['count']);复制代码

数据库锁与事务锁的区别

  • 数据库模型->lock(true)
  • 事务锁 Db
  1. 事务锁是等待整个事务提交才会执行第二次事务,但是数据库模型锁只是单步的锁着了数据库查询语句
  2. 在后面的操作还没有执行时,数据库模型锁已经放开了

外部网址使用

  • 要从根目录一直到 index.php
  • 后面才是路由
  • www.yhf7/zerg/public/index.php/api/v1/pay/notify

模型分页查询(paginate)

  • 第一个参数是分类数
  • 第二个数是否简洁模式
  • 第三个是数组填入分页数
public static function getSummaryByUser ($uid,$page=1,$size=15) {     $paginData = self::where('user_id','=',$uid)->order('create_time desc')->paginate($size,true,['page' => $page]);     return $paginData; }复制代码

后记

  • 这是学习微信小程序开发后端PHP时候的笔记,欢迎更多的同行大哥指导交流
  • :https://yhf7.github.io/
  • 如果有什么侵权的话,请及时添加小编微信以及qq也可以来告诉小编(905477376微信qq通用),谢谢!

转载于:https://juejin.im/post/5c85c07df265da2dd94ce0bf

你可能感兴趣的文章
使用局部标准差实现图像的局部对比度增强算法。
查看>>
2017-2018-1 20165313 《信息安全系统设计基础》第八周学习总结
查看>>
《代码敲不队》第四次作业:项目需求调研与分析
查看>>
菜鸡互啄队—— 团队合作
查看>>
HttpWebRequest的GetResponse或GetRequestStream偶尔超时 + 总结各种超时死掉的可能和相应的解决办法...
查看>>
SparseArray
查看>>
第二章
查看>>
android背景选择器selector用法汇总
查看>>
[转]Paul Adams:为社交设计
查看>>
showdialog弹出窗口刷新问题
查看>>
java
查看>>
Vue.js连接后台数据jsp页面  ̄▽ ̄
查看>>
关于程序的单元测试
查看>>
mysql内存优化
查看>>
都市求生日记第一篇
查看>>
Java集合---HashMap源码剖析
查看>>
SQL优化技巧
查看>>
thead 固定,tbody 超出滚动(附带改变滚动条样式)
查看>>
Dijkstra算法
查看>>
css 动画 和 响应式布局和兼容性
查看>>