实力进阶,教你使用thinkphp6开发一款商城系统

0.开篇

你好!很高兴你能点开这个教程,相信你对这个教程有了那么一点点兴趣,接下来占用你一点点时间,邀你浏览一下本章内容,希望能够让你更加有兴趣去学完这个教程。

作者我是一名九零后程序员,搬砖了好几年,主要使用PHPJava作为开发语言,另外也懂些前端,毕竟刚工作那会写了不少htmlcssjs,到后面也学习了vue。作为一名开发人员,自己喜欢看一些技术文章,也喜欢分享一些东西,这样才能提高自己的技术。

为什么要写这个教程?

其实前两年就想写了,可能是因为项目的原因,也可能是自己还没想好写些什么。不过今年还是下定决心要写一下,一来是能够学习和巩固自己的知识,二来也是分享一下自己的经验。

课程简介

本教程打算以一个商城项目为例,会实现后台权限管理、会员管理、商品管理、商品团购、抢购......反正是一些常用的功能都给它实现了。

本教程不同于网上的一些视频教程,只是为了实现而实现,说真的网上的一些教程,都是按部就班的实现功能,很少会提及为什么要这样做,这样做的好处是啥,更不用说会踩到什么坑了,因此有些人自学了,效果也好不到哪去。

在这个教程中模块与功能的代码实现是次要的,更重要的是我们是如何去规划、设计这些模块,我们要站在一个架构师的角度实现我们的系统。

你能学到什么?

第一 、进一步掌握Thinkphp,让你知道如何阅读文档,让你知道在学习了thinkphp后如何快速的掌握其它框架。

第二 、数据库的设计,如何合理的设计数据表,表设计的越合理,sql就会写得越简单,让你轻松应对上千万的数据。

第三、如何搭建自己的系统,完善基础架构。

第四、在实际工作中是如何处理需求,积累经验。

其实你学到的东西远不止这些,每个模块中所涉及到的知识就很多了。但这些不是重点,重点你是怎么实现的,比如说手机验证码,我要分享的是你要如何阅读第三方的文档,快速提取有效的信息,快速实现手机发送验证码功能。最终的目的是你在掌握了接入阿里云短信功能的同时,自己能够阅读其它第三方短信平台的文档而接入短信发送的功能,例如百度。

总之本教程重在引导。

适合人群

工作一两年的,有接触thinkphp的同学即可

最后

如果说对你有帮助,记得点个赞~~~~

因为篇幅过长,大家可以访问,里面教程会一直更新 gitee.com/myha/demo-s...

1.技术选型

1.1环境搭建

环境 版本
语言 PHP 7.3.4
数据库 mysql 5.7
缓存 redis 3.0.5

这里推荐使用phpstudy集成环境

1.2框架选型

到目前为止,国内使用最多的PHP框架当属ThinkphpLaravel,这两款框架很相似,文档也是比较齐全。作为一个PHP程序员至少要掌握其中一个,当然最好都掌握。

本教程的项目使用了Thinkphp6.1的版本,是当前最新版本。

官方文档链接:www.kancloud.cn/manual/thin...

安装

shell 复制代码
composer create-project topthink/think demo-shop

默认是单应用模式的,这里我们开发的项目是多应用,因此还需执行以下命令

shell 复制代码
composer require topthink/think-multi-app

本项目有后台模块、提供给web端的API模块,因此就需要用到多应用模型、这样有利于我们后期项目的管理和维护

目录结构

把框架下载下来后,我们认识一下里面的一些目录,下面是官网给出的目录结构,实际上下载下来的目录比较简洁

arduino 复制代码
www  WEB部署目录(或者子目录)
├─app           应用目录
│  ├─app_name           应用目录
│  │  ├─common.php      函数文件
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  ├─config          配置目录
│  │  ├─route           路由目录
│  │  └─ ...            更多类库目录
│  │
│  ├─common.php         公共函数文件
│  └─event.php          事件定义文件
│
├─config                全局配置目录
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─console.php        控制台配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─filesystem.php     文件磁盘配置
│  ├─lang.php           多语言配置
│  ├─log.php            日志配置
│  ├─middleware.php     中间件配置
│  ├─route.php          URL和路由配置
│  ├─session.php        Session配置
│  ├─trace.php          Trace配置
│  └─view.php           视图配置
│
├─public                WEB目录(对外访问目录)
│  ├─index.php          入口文件
│  ├─router.php         快速测试文件
│  └─.htaccess          用于apache的重写
│
├─extend                扩展类库目录
├─runtime               应用的运行时目录(可写,可定制)
├─vendor                Composer类库目录
├─.example.env          环境变量示例文件
├─composer.json         composer 定义文件
├─LICENSE.txt           授权说明文件
├─README.md             README 文件
├─think                 命令行入口文件

稍微留意一下框起来的部分,后面会用到

URL重写

重写url主要是为了隐藏入口index.php,我们可以发现很多网站的地址并没有带上入口文件,比如说tp的官网

www.kancloud.cn/manual/thin... 而不是

www.kancloud.cn/index.php/m...

官网文档里面也提到了,它也给出了重写的规则,但是最后会发现有点问题,因此做了一下调整

ruby 复制代码
<IfModule mod_rewrite.c>
  Options +FollowSymlinks -Multiviews
  RewriteEngine On

  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-f
#####RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]  这是官网的
  RewriteRule ^(.*)$ index.php [L,E=PATH_INFO:$1]
</IfModule>

注意上面是基于Apache的规则,我们在根目录(入口文件所在的目录),新建一个.htaccess文件,把规则写进里面去即可

2.完善架构

我们下载下来的代码结构是非常简单的,对于实际项目,我们需求对框架的某些功能进行一些封装以及开启框架的某些功能。

2.1接口的结构

本教程是php+vue前后分离的一个项目,因此接下来都会讲接口的实现。

php 复制代码
<?php
namespace app\controller;

class User 
{
    public function login()
    {
        return 'login';
    }
}

如上面的代码,这就是一个登录接口。对于一个新手来说,可能会把所有逻辑都写在控制器(controller)里面,这种做法对于我来说是绝对禁止的,因为这样做会使你的代码非常臃肿,并且复用性差,因此如何设计代码结构,成为你程序简洁、高效的关键。

本教程的接口的代码结构主要分为三层:控制层、业务层、模型层

控制层:接口名的定义,主要负责参数的过滤及结果的返回

业务层:实现业务功能的具体核心代码

模型层:负责与数据库交互

每个应用都拥有独立的控制层,这个很好理解,因为你总不可能两个不同的应用使用同一个接口。

每个应用也可以拥有独立的业务层,因为不同应用可能业务代码不一样,但不管是哪个应用都离不开对数据库的增删改查,因此我们这里封装一个核心的业务层,只提供增删改查的逻辑,所有应用都可以直接调用它,做到最大程度的复用性

模型层我们可以把它比作数据表,每个应用都可以使用同一个模型

或许你还不理解,不过没关系,有个印象就行,后面具体开发按照这个结构来,你就会明白了

2.2基础控制器

在文档中www.kancloud.cn/manual/thin... ,这里我们后面会讲到。

app目录下我们会发现框架生成的BaseController.php基础控制器,我们可以把它当作最底层的控制器。前面我们说过,我们这个项目是一个多应用项目,因此这里为每个应用都建一个基础控制器,让其继承BaseController.php,这样每个应用即可使用底层控制器提供的方法,又可以定制开发一些当前应用共用的方法。

app下新建一个admin目录,作为后台应用。接着新建controller-->AdminController.php,内容如下

php 复制代码
<?php
// +----------------------------------------------------------------------
// | 应用基础控制器
// +----------------------------------------------------------------------
namespace app\admin\controller;

use think\App;
use app\BaseController;

class AdminController extends BaseController
{   
    public function __construct(App $app)
    {
        parent::__construct($app);
    }

    protected function initialize()
    {
        
    }
}

initialize()为初始化方法,在BaseController构造函数里面调用了它,每次请求都会执行该方法,因此我们可以在这个方法里封装一些公用信息。

2.3返回值的封装

在文档www.kancloud.cn/manual/thin...

一般来说比较常见的是渲染模板输出JSON输出 ,在一些外包公司为了节省开发成本,前后端都是混到一起,他们一般才采用渲染模板输出 ,这也要求后端开发人员要了解html,现在随着vue前端框架的崛起,后端开发只需把数据以 JSON输出返回给前端即可。

返回给前端的数据需要固定的格式,你不能随便返回一些乱七八糟的东西,一般来说会返回给前端下面三个基本字段:

响应码 描述
code 响应码,成功返回200,异常返回500
msg 响应信息
data 响应数据

当然你还可以加别的字段,只要统一就行。

另外Thinkphp框架为止也提供了一个助手函数 json(),一般来说我们是这样使用它的

php 复制代码
json(['code'=>200, 'msg'=>'操作成功','data'=>$data]);
json(['code'=>500, 'msg'=>'服务器异常~']);  
json(['code'=>1000, 'msg'=>'请输入用户名!']);

不知道你们有没有发现,假如说查询列表数据,每次返回成功,你都要写'code'=>200, 'msg'=>'操作成功',这样很繁琐,因此这里我们简单封装一下。

打开app目录下的全局函数文件common.php,这里面新增的函数可以全局调用

php 复制代码
/**
 * 返回值-成功
 * @param string $code    错误码
 * @param string $msg     提示信息
 */
function success($data = []){
    return json(['code'=>200, 'msg'=>'操作成功','data'=>$data]);
}

/**
 * 返回值-失败
 * @param string $code    错误码
 * @param string $msg     提示信息
 */
function failure($code=201, $msg='操作失败'){
    return json(['code'=>$code, 'msg'=> $msg]);
}

/**
 * 返回值-异常
 * @param string $code    错误码
 * @param string $msg     提示信息
 */
function error(){
    return json(['code'=>500, 'msg'=>'服务器异常~']);
}

我们就可以这样调用:

php 复制代码
//返回列表数据
return success($data);
//新增一条数据成功
return success();
//新增一条数据失败
return failure();
//新增一条数据,请输入用户名
return failure(1000,'请输入用户名!');
//异常只返回500
return error();

这里的话我们还需求做个小小优化,当然很多人在开发过程中并没有这样做,这也是情有可原,毕竟时间紧迫的话就会忽略掉。

一般来说我们定义一些错误码及对应信息的常量,例如本项目会在app->config下新建一个error.php

php 复制代码
return [
    'er1'    =>['code'=>'1000','msg'=>'请输入用户名!'],
];

最后我们这样调用

php 复制代码
return failure(config('error.er1')['code'],config('error.er1')['msg']);

2.4表单验证器

前面我们在基础控制器里面提到了验证器,实际上文档中有专门的栏目介绍它www.kancloud.cn/manual/thin...

它的主要用途就是新增或修改数据对数据进行合法性验证,一般来说前端会传一些数据给后端接口,比如新增一条管理员数据,那此时就必须验证账号和密码这两个字段,这两个字段不能为空,同时密码还需要满足一定的条件,此时框架提供的验证器就派上用场了。

打开底层控制器BaseController.php,我们会发现框架已经给我们创建了一个验证码方法,非常的好用

php 复制代码
/**
     * 验证数据
     * @access protected
     * @param  array        $data     数据
     * @param  string|array $validate 验证器名或者验证规则数组
     * @param  array        $message  提示信息
     * @param  bool         $batch    是否批量验证
     * @return array|string|true
     * @throws ValidateException
     */
protected function validate(array $data, $validate, array $message = [], bool $batch = false){
    //内容省略
}

接下来看看调用示例

php 复制代码
$data = $this->request->post();
//验证规则
$validate = [
    'account' => 'require',
    'password'  => 'require|regex:/^[a-zA-Z0-9_]{8,20}$/',
];
//提示信息
$message = [
    'account.require' => '账号不能为空!',
    'password.require' => '密码不能为空!',
    'password.regex' => '密码长度8~20位,包含字母数字下划线!',
];
$this->validate($data, $validate, $message);

如果输入的账号为空,运行这段代码后你会发现抛出以下异常

2.5异常接管

上一节讲验证器的时候,抛出了一个账号不能为空的异常,很显然这种提示方式对于前端很不友好,前端无法获取到相应的信息,因此我们要想办法捕抓到这个异常,将提示信息json的格式返回给前端

我们打开文档www.kancloud.cn/manual/thin...

框架也为我们生成了app/ExceptionHandle.php,打开看看里面的内容,其中有这样的一个函数

render($request, Throwable $e),我们在里面新加参数验证码错误的代码

php 复制代码
public function render($request, Throwable $e): Response
{
     // 参数验证错误
    if ($e instanceof ValidateException) {
        return failure(1004,$e->getError());
    }
    return parent::render($request, $e);
}

这时如果参数验证码错误的话,就会被捕抓到,同时返回json格式的数据给前端,如下

json 复制代码
{
	"code": 1004,
	"msg": "账号不能为空!"
}

同样的我们需要接管其它的异常

php 复制代码
// 参数验证错误
if ($e instanceof ValidateException) {
    return failure(1004,$e->getError());
}

// 其它异常
if ($e instanceof RouteNotFoundException || $e instanceof Exception 
    || $e instanceof HttpException || $e instanceof HttpResponseException || $e instanceof ModelNotFoundException
    || $e instanceof DataNotFoundException || $e instanceof RouteNotFoundException) 
{
    return error();
}

我们统一返回这样的格式给前端

json 复制代码
{
	"code": 500,
	"msg": "服务器异常~"
}

为什么要这样返回?

因为在生产环境,我们一般不会把系统产生的异常信息返回给用户的,因为这是一种很危险的信号。

那有些人可能会问,如果都这样提示,那不是不知道哪里出问题了吗?

别慌,在app/ExceptionHandle.php中,开头有这样的一段代码

php 复制代码
/**
     * 不需要记录信息(日志)的异常类列表
     * @var array
*/
protected $ignoreReport = [
    // HttpException::class,
    // HttpResponseException::class,
    // ModelNotFoundException::class,
    // DataNotFoundException::class,
    // ValidateException::class,
    // RouteNotFoundException::class,
    // Exception::class,
];

我们注释掉它,这样错误信息会被日志记录下来

日志一般记录在runtime下面的log目录里的xxx.log文件

verilog 复制代码
[2023-11-17T11:21:39+08:00][error] [255]账号不能为空1111
[2023-11-17T11:22:36+08:00][error] [255]账号不能为空1111[F:\phpstudy_pro\WWW\demo.shop\app\common.php:84]
[2023-11-17T11:25:57+08:00][error] [8]未定义变量: c[F:\phpstudy_pro\WWW\demo.shop\app\admin\controller\SysUser.php:172]

这样我们就很容易找到问题了

生产环境也一样,运维一般会在某个地方输出这些日志,这样我们可以方便的通过浏览器可以看到这些异常信息

2.6开启Redis缓存

在文档www.kancloud.cn/manual/thin...

打开全局的config/cache.php,新增以下内容

php 复制代码
'redis' => [
    // 驱动方式
    'type' => 'redis',
    //服务地址
    'host' => env('cache.host'),
    //端口
    'port' => env('cache.port'),
    //密码
    'password' => env('cache.password'),
    //节点
    'select' => env('cache.select'),
    // 缓存前缀
    'prefix' => env('cache.prefix'),
    // 缓存有效期 0表示永久缓存
    'expire' => env('cache.expire')
],

打开.env,新增以下内容

ini 复制代码
[CACHE]
DRIVER = redis
HOST = 127.0.0.1
PORT = 6379
PASSWORD = 
SELECT = 0
PREFIX = 
EXPIRE = 0

一般本地的redis配置信息都默认是这样,如果发现你的连接不上,就看看密码是否正确

框架给我们提供了助手函数 cache(),看看它的使用方式

php 复制代码
//设置缓存,且缓存时间是永久,因为配置EXPIRE = 0,标识永久
cache("user_id",1);
//缓存10秒
cache("user_id",1,10);
//获取缓存
cache("user_id");

2.7路由配置

路由能做的东西很多,文档非常详细的介绍www.kancloud.cn/manual/thin...

为什么要使用路由?个人认为主要是有下面两个好处:

  1. 访问地址写到一个地方,方便管理
  2. 使用路由中间件,可以提前过滤接口

打开config/route.php,把以下的配置项设置为true,开启强制路由

php 复制代码
// 是否强制使用路由
'url_route_must'        => true

另外我们在admin应用下新建route/app.php,每个应用可设置单独的路由,例如

php 复制代码
<?php
// +----------------------------------------------------------------------
// | 后台应用路由
// +----------------------------------------------------------------------
// | Author: myh
// +----------------------------------------------------------------------
use think\facade\Route;
//图片验证码
Route::get('login/verify','sysUser/verify');

使用任何一个框架,做任何一个项目都可以按照这样的思路来开发,做一些开放前的准备工作

相关推荐
ServBay2 天前
告别面条代码,PSL 5.0 重构 PHP 性能与安全天花板
后端·php
JaguarJack4 天前
FrankenPHP 原生支持 Windows 了
后端·php·服务端
BingoGo4 天前
FrankenPHP 原生支持 Windows 了
后端·php
JaguarJack5 天前
PHP 的异步编程 该怎么选择
后端·php·服务端
BingoGo5 天前
PHP 的异步编程 该怎么选择
后端·php
JaguarJack6 天前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay7 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954487 天前
CTF 伪协议
php
BingoGo9 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack9 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端