目录
2.1 配置数据库(config/database.php)
2.2 添加php表字段配置(config/vue.php)
4.1 使路径参数名支持横杠(config/route.php)
五、图片保存配置(config/filesystem.php)
前篇写的vue后端对应的mysql数据表,这篇继续讲后端thinkphp代码。
需要的知识点:thinkphp,推荐《ThinkPHP6.0完全开发手册》
thinkphp使用的是mvc框架,因为前后端分离,我这里只使用了mc,并没有使用它的模板视图v,现在前后端分离是主流,所以后端的话一般不用学v。
一、需要修改的thinkphp目录文件
红色框的需要添加或修改的,其它不动
二、TP数据库的配置
2.1 配置数据库(config/database.php)
前篇说有mysql数据表的生成,现在使用TP连接mysql,在连接之前需要配置一下,打开config/database.php配置文件,只要填写用户名数据库类型,用户名和密码,数据库名,就行了,如下所示:
// 数据库连接配置信息
'connections' => [
'mysql' => [
// 数据库类型
'type' => env('database.type', 'mysql'),
// 服务器地址
'hostname' => env('database.hostname', '127.0.0.1'),
// 数据库名
'database' => env('database.database', 'hua'),
// 用户名
'username' => env('database.username', 'root'),
// 密码
'password' => env('database.password', 'root'),
// 端口
'hostport' => env('database.hostport', '3306'),
...
2.2 添加php表字段配置(config/vue.php)
上篇我们说了,mysql的vue表和vue的字段并不是一一对应的,而是添加工拼接的,如
SELECT picid, CONCAT('http://192.168.3.200/pic/',savename) AS picurl,lable,`describe`,0 AS isSet FROM vue;
picurl是由'http://192.168.3.200/pic/'加表的“savename”字段拼成的,isSet是直接添加的,如果我们在php写SQL是可以这样直接写,但是如果图片的服务器地址变了,在生产环境中,那你不是要修改源码,php还好,默认源码是可以看到的,但是如果是java、go之类,源码是不可变的。所以一般做法就是把它抽出来放在独立的配置文件中。
我这里建立一个自定义的数据表的php配置就是放vue中没有的字段的。操作如下:
创建config/vue.php代码如下:
<?php
return [
"picpath" => 'http://192.168.3.200/pic/',
'isSet'=> 0
];
这样我用thinkphp就可以这样写了(这字段我写在model/Vue.php中)下面会给全代码。
$vueConf=Config::get('vue');
// 相当于 SELECT picid, CONCAT('http://192.168.3.200/pic/',savename) AS picurl,lable,`describe`,0 AS isSet FROM vue;
$filedStr='picid, concat(\''.$vueConf['picpath'].'\',savename) as picurl,lable,`describe`,'.$vueConf['isSet'].' as isSet';
三、图片放置(public/pic)
我在public/pic放了一些默认的图片,作为例子,随便一些都行,不影响的,只是测试使用。
、
四、路由
这里的路由与vue路由是一样的,不同的是绑定的不是组件,而是函数。
4.1 使路径参数名支持横杠(config/route.php)
thinkphp默认参数是不支持横杠的,要修改config/route.php使它支持
// 默认的路由变量规则 '[\w\.]+',
'default_route_pattern' => '[\w\.\-]+',
4.2 配置路由(route/app.php)
我添加了一个名为vue的控制器,还有批量删除、上传等功能,是不同路径的,需要得添加一下。打开route/app.php,修改后的代码如下:
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
use think\facade\Route;
// Route::get('think', function () {
// return 'hello,ThinkPHP6!';
// });
Route::rule('/','index','GET|POST|DELETE|PUT|')->allowCrossDomain();
Route::get('hello/:name', 'index/hello');
Route::post('index/upload','index/upload')->allowCrossDomain();
Route::delete('index/dels','index/dels')->allowCrossDomain();
Route::resource('vue','Vue')->vars(['vue' => 'picid']);
ps:其中 resource 是资源路由,可以直接生成rest api的填写后执行 php think make:controller Vue就可以自动生成控制器了
五、图片保存配置(config/filesystem.php)
我在文件系统配置config/filesystem.php中,添加了2个配置,一个是 uploads是生产环境保存的图片位置,一个是temp是临时图片保存位置(定时删除就行了,比如这个月2号删除上个月之前所有的临时文件)
config/filesystem.php配置代码如下:
<?php
return [
// 默认磁盘
'default' => env('filesystem.driver', 'local'),
// 磁盘列表
'disks' => [
'local' => [
'type' => 'local',
//'root' => app()->getRuntimePath() . 'pic',
//'root' => 'pic',
'root' => 'pic',
],
'public' => [
// 磁盘类型
'type' => 'local',
// 磁盘路径
// 'root' => app()->getRootPath() . 'public/pic',
'root' => 'pic',
// 磁盘路径对应的外部URL路径
'url' => '/pic',
// 可见性
'visibility' => 'public',
],
// 更多的磁盘配置信息
'uploads' => [
// 磁盘类型
'type' => 'local',
// 磁盘路径
// 'root' => app()->getRootPath() . 'public/pic',
'root' => 'pic',
// 磁盘路径对应的外部URL路径
'url' => '/pic',
// 可见性
'visibility' => 'public',
],
'temp' => [
// 磁盘类型
'type' => 'local',
// 磁盘路径
// 'root' => app()->getRootPath() . 'public/pic',
'root' => 'temp',
// 磁盘路径对应的外部URL路径
'url' => '/temp',
// 可见性
'visibility' => 'public',
],
],
];
六、控制器(app/controller)
这里的控制器有2个,一个是app/controller/Index.php和app/controller/Vue.php,主要是处理
Index.php:主要是处理上传,批量删除
Vue.php:主要是查询所有数据、某行的操作保存、删除、增加行等
6.1 vue.php控制文件
6.1.1 生成vue.php文件
vue.php配置文件是直接使用 “php think make:controller Vue”命令生成的 。执行此命令有2种方式
方法一:使用命令提示符创建,操作如下:
- 打开命令提示符:开始菜单->运行->输入 cmd 回车
- 进入vueapi项目的根目录,我的为D:\phpstudy_pro\WWW\vueapi,相关命令是
d: cd D:\phpstudy_pro\WWW\vueapi
- 执行 php think make:controller Vue 命令
方法二:直接使用phpStorm的终端命令
6.1.2 vue.php控制文件代码
生成的Vue.php文件是id的,我修改一个变成picid这样方便记忆,代码如下:
<?php
declare (strict_types = 1);
namespace app\controller;
use app\model\Vue as ModelVue;
use Symfony\Component\VarDumper\Cloner\Data;
use think\File;
use think\Request;
class Vue
{
/**
* 显示资源列表
*
* @return \think\Response
*/
public function index(Request $request)
{
// pageSize 每页多少条记录
$pageSize=$request['pageSize'];
$db = new ModelVue();
$res=$db->selAll($pageSize);
return json($res);
}
//图片移动,把从临时目录移动到public/pic目录下,以月为单位。
public function picFileMove(Request $request){
$picrow=$request['picrow'];
$fileNew ='';
$savename=$picrow['savename'];
// 如果图片修改了,则移动一下图片
if($savename != '0'){
$savename = date('Ym').'/'.basename($savename);
$fileOrig = '../public/pic/'.$picrow['savename'];
//按月分
$fileNewPath='../public/pic/'.date('Ym').'/';
$fileNew = $fileNewPath.basename($savename);
if(!file_exists($fileNewPath)){
mkdir($fileNewPath);
}
rename($fileOrig,$fileNew);
}else{
$savename='0';
}
return $savename;
}
/**
* 保存新建的资源
*
* @param \think\Request $request
* @return \think\Response
*/
public function save(Request $request)
{
$savename = $this->picFileMove($request);
$picrow=$request['picrow'];
//这里不需要picid,因为前端没有给出后端生成
$queryArray=[
// 因为文件不是很多,是按月来分
'savename'=> $savename,
'lable'=>$picrow['lable'],
'describe'=>$picrow['describe']
];
$db = new ModelVue();
return $db->saveOne($queryArray);
}
/**
* 显示指定的资源
*
* @param int $picid
* @return \think\Response
*/
public function read($picid)
{
//
$db= new ModelVue();
return $db->selOne($picid);
}
/**
* 保存更新的资源
*
* @param \think\Request $request
* @param int $picid
* @return \think\Response
*/
public function update(Request $request, $picid)
{
$savename = $this->picFileMove($request);
$picrow=$request['picrow'];
$queryArray=[
'picid'=>$picid,
// 因为文件不是很多,是按月来分别保存。如果不修改则为字符串'0'
'savename'=> $savename,
'lable'=>$picrow['lable'],
'describe'=>$picrow['describe']
];
$db = new ModelVue();
return $db->updateByPicid($queryArray);
}
/**
* 删除指定资源
*
* @param String $picid
* @return \think\Response
*/
public function delete($picid)
{
$db = new ModelVue();
$savename=$db->findSavename($picid);
if(file_exists('../public/pic/'.$savename)){
unlink('../public/pic/'.$savename);
}
return $db->delOne($picid);
}
}
6.2 index.php控制器代码
index.php控制器代码如下:
<?php
namespace app\controller;
use app\BaseController;
use app\model\Vue as ModelVue;
use app\Request;
use think\console\command\Clear;
use think\db\Query;
use think\facade\Config;
use think\response\Json;
use function PHPSTORM_META\type;
class Index extends BaseController
{
public function index()
{
return 'welcome thinkphp';
}
/*
* 主要是上传文件,主要是图片,我这里是先保存在临时目录中,当vue中点保存,才会
* 把临时目录中的文件移动到public/pic目录下
* */
public function upload(){
//上传先放在临时目录下,定时清空,点保存,如果文件大则按日,很小则按月,我这里很小的,所以不按
$file = request() -> file('file');
// 上传到本地服务器 putFile( 'topic', $file);
// temp/20210330\8c1b99851c3d3ad1f89868ef270a81cf.jpg
$savename = \think\facade\Filesystem::putFile('temp',$file);
//下面代码是调试,可以看效果。如savename值为:“uploads/20210324\9a0cc05f75c6dd4b6ada9326f7ddd937.jpg”
/* $fp = fopen('log.txt','w');
//file_put_contents('log.txt','');
fwrite($fp,'picid值为:'.request()->post('picid').'n');
fwrite($fp,'savename值为:'.$savename);
fclose($fp); */
return json($savename);
}
//批量删除功能,tp支持以数组方式删除,这里是picid数组
public function dels(Request $request)
{
// 还有涉及这些图片的保存也要删除,这里我就不写了
$picids_json=$request['picids'];
// var_dump($picids)知道是数组类型 [ "021", "022" ]
$picids=json_decode($picids_json);
$db=new ModelVue();
$res=$db->delBypicids($picids);
return $res;
}
// 这个是测试删除功能,可以不写
public function tsetDel(){
$fileOrig = '../public/pic/temp/20210328/a.jpg';
$filePath = '../public/pic/a.jpg';
if(!file_exists($fileOrig)){
return "文件或目录不存在!";
}
//以重命名方式移动,目标不能写路径,会报错:拒绝访问,code 5
rename($fileOrig,$filePath);
return "ok";
}
}
七、模型(app/model/Vue.php)
模型文件Vue.php是我手工创建的,目录也是。所有数据库操作都写在这里,代码如下:
<?php
namespace app\model;
use think\facade\Config;
use think\Model;
class Vue extends Model{
/*生成唯一标志
*标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx(8-4-4-4-12)
*/
public static function uuid(){
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr ( $chars, 0, 8 ) . '-'
. substr ( $chars, 8, 4 ) . '-'
. substr ( $chars, 12, 4 ) . '-'
. substr ( $chars, 16, 4 ) . '-'
. substr ( $chars, 20, 12 );
return ''.$uuid ;
}
// 这是要查询的字段,因为多次使用,我直接把变它成字符串了
public static function vueSQLField(){
$vueConf=Config::get('vue');
// 相当于 SELECT picid, CONCAT('http://192.168.3.200/pic/',savename) AS picurl,lable,`describe`,0 AS isSet FROM vue;
$filedStr='picid, concat(\''.$vueConf['picpath'].'\',savename) as picurl,lable,`describe`,'.$vueConf['isSet'].' as isSet';
return $filedStr;
}
// 带分页功能的查询
public function selAll($pageSize){
// isSet是布尔值,不能用双引号
//$result = Vue::field("picid, concat('http://192.168.3.200/pic/',savename) as picurl,lable,`describe`, 0 as isSet")->select();
//$result = Vue::field(self::vueSQLField())->select();
// 带分页功能
$list = Vue::field(self::vueSQLField())->order('id', 'ASC')->paginate($pageSize);
return ['list' => $list];
}
//通过pic查询一条记录
public function selOne($picid){
return vue::where('picid',$picid)->field(self::vueSQLField())->select();
}
//通过picid查数据库的savename
public function findSavename($picid){
return vue::where('picid',$picid)->value('savename');
}
// 通过picid删除一条记录
public function delOne($picid){
return vue::where('picid',$picid)->delete();
}
//通过picid更新一条记录,是直接传数组的方式
public function updateByPicid($arry_row){
// 如果为空则说明,文件交没有修改
if($arry_row['savename']=='0'){
return vue::where('picid',$arry_row['picid'])->update([
'lable' => $arry_row['lable'],
'describe' => $arry_row['describe']
]);
}else{
return vue::where('picid',$arry_row['picid'])->update([
'savename' => $arry_row['savename'],
'lable' => $arry_row['lable'],
'describe' => $arry_row['describe']
]);
}
}
// 保存一条记录,主要是vue增加一条记录,然后点保存
public function saveOne($arry_row){
$picid = self::uuid();
$arry_row['picid']=$picid;
//去掉savename的值
if($arry_row['savename']=='0'){
unset($arry_row['savename']);
}
vue::strict(false)->insert($arry_row);
}
// 批量删除功能,tp支持以数组方式删除,通过图片id数组进行删除
public function delBypicids($picids){
return vue::where('picid','in',$picids)->delete();
}
// 通过picid,更新savename,即更新图片只在路径名。
public function updatepic($picid,$savename){
return vue::where('picid',$picid)->update(['name'=>$savename]);
}
}
八、使用postman测试(可选)
如果要测试上面是否写的正确,可以不使用vue代码,直接用postman实现put、post、delete提交,比如我提交一个删除,看一下是否成功
8.1 rest 风格代码
TP路由部分的资源例子对rest风格代码做了说明
Route::resource('blog', 'Blog');
表示注册了一个名称为
blog
的资源路由到Blog
控制器,系统会自动注册7个路由规则,如下:
标识 请求类型 生成路由规则 对应操作方法(默认) index GET blog
index create GET blog/create
create save POST blog
save read GET blog/:id
read edit GET blog/:id/edit
edit update PUT blog/:id
update delete DELETE blog/:id
delete 具体指向的控制器由路由地址决定,你只需要为
Blog
控制器创建以上对应的操作方法就可以支持下面的URL访问:http://serverName/blog/ http://serverName/blog/128 http://serverName/blog/28/edit
8.2 测试删除功能(delete)
比如我要删除picid为100的postman写法如下图所示:
PS:192.168.3.200/index.php/vue/100,index.php是入口文件,vue表示 vue控制器
8.3 测试更新功能(put)
因为我放在vue中是格式为:IP/index.php/vue/<picid值>
更新是更新某行的信息。这里我就不修改图片了,怎样提交更新主要是看PHP代码决定的。涉及更新部分代码为
public function update(Request $request, $picid)
{
$savename = $this->picFileMove($request);
$picrow=$request['picrow'];
$queryArray=[
'picid'=>$picid,
// 因为文件不是很多,是按月来分,如果不修改则为字符串'0'
'savename'=> $savename,
'lable'=>$picrow['lable'],
'describe'=>$picrow['describe']
];
...
}
从上面知道,使用的键值对方式,而且有嵌套,猜json做前后端交互比较适合,而且主流的语言一般都支持json。上面php数组方式写成json为并值赋值
{
"picrow":{
"savename":"0",
"lable":"111",
"describe":"111"
}
}
PS:上面是图片没有修改,把lable和describe修改为111
我要修改picid为001中的lable和 describe,可以是这样
ps:请求提交并不是一成不变的,是要根据代码写的,结合对应的参数。
可以执行sql查询一下 SELECT * FROM vue WHERE picid=001,发现被修改了,图片并没有修改。
8.4 测试其它
其它get查询、post更新、批量删除我不一一测试了,差不多的。