#### 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-7.分類的模型關聯和通用CRUD接口
> 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-1.工具和本地環境
> 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-2.啟動項目
> 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-3.路由、模型與數據庫操作
> 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-4.跨域且傳輸數據,并優化后端接口
> 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-5.用戶登錄(一),密碼的bcrypt(hash)加密與驗證
> 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-6.用戶登錄(二),token驗證
> 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-7.分類的模型關聯和通用CRUD接口
> 技能學習:學習使用php(tp6框架) + vue.js,開發前端全棧網站-8.使用mavoneditor(vue的markdown編輯器),并批量上傳圖片
###### 1.制作通用CRUD接口
分類內容的接口與管理員接口相同,都是增刪改查,如果每個接口都復制一遍再稍作改動,接口頁面內容會太多。所以我們對兩個接口進行比對,找不同:

只有這里不同,所以我們只要在路由中把這個辨別值改為動態即可:

將這個辨別值當作模型名處理,因為后續數據接口操作實際上就是模型不同,增刪改查方法都一樣。
接口函數接收模型名(以查找所有數據接口為例):
~~~
public function findall(){
// 接收數據
$data = request() -> param();
// return $data;
// 根據模型名判斷
if($data['model'] == "admin"){
$db_data = Admin::select();
// 返回查找到的數據
return $db_data;
}else if($data['model'] == "category"){
$db_data = Category::select();
// 返回查找到的數據
return $db_data;
}
}
~~~
因為php語言以命名空間為主,所以無法直接使用傳值做模型方法使用,故我們需要判斷后再用模型方法進行數據操作。
測試原管理員接口:

沒問題,仿照管理員功能做出CategorySet.vue和CategoryList.vue:
CategorySet.vue:
~~~
<template>
<div>
<h1>{{id ? '編輯' : '創建'}}分類</h1>
<el-form label-width="80px" style="margin-top:20px;" @submit.native.prevent="save">
<el-form-item label="用戶名">
<el-input v-model="model.name"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
props: {
id: {}
},
data(){
return {
model: {},
parentOptions: [],
}
},
methods: {
async save(){
let res
if(this.id){
res = await this.$http.put('rest/category/' + this.id, this.model)
}else{
res = await this.$http.post('rest/category', this.model)
}
console.log("en?",res)
this.$router.push('/categories/list')
this.$message({
type: 'success',
message: '保存成功'
})
},
async fetch(){
const res = await this.$http.get('rest/category/' + this.id)
this.model = res.data
},
},
created(){
this.id && this.fetch()
}
}
</script>
~~~
CategoryList.vue:
~~~
<template>
<div>
<h1>分類列表</h1>
<el-table :data="items">
<el-table-column prop="id" label="ID" width="220">
</el-table-column>
<el-table-column prop="name" label="分類名稱">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button type="text" size="small" @click="$router.push('/categories/edit/' + scope.row.id)">編輯</el-button>
<el-button @click="remove(scope.row)" type="text" size="small">刪除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
items: []
}
},
methods: {
async fetch(){
const res = await this.$http.get('rest/category')
this.items = res.data
},
remove(row){
this.$confirm('是否確定要刪除分類"' + row.name + '"?', '提示', {
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
// 要想使用await,函數必須使用async
// await異步執行,待調用接口獲取數據完成后再將值傳給res,進行下一步操作
const res = await this.$http.delete('rest/category/' + row.id)
this.$message({
type: 'success',
message: '刪除成功!'
});
if(res.status == 200){
// 接口調用成功后,刷新頁面
this.fetch()
}
}).catch(() => {
this.$message({
type: 'info',
message: '已取消刪除'
});
});
}
},
created() {
this.fetch()
}
}
</script>
~~~
然后制作Category模型:
~~~
<?php
namespace app\admin\model;
use think\Model;
class Category extends Model{
// 設置字段信息
protected $schema = [
'id' => 'int',
'name' => 'string',
// 父級分類
'parent' => 'string',
];
public function category(){
// 用于后續數據關聯使用(分類的無限層級關聯)
return $this -> hasMany(Category::class);
}
}
~~~
新建數據表:


刷新頁面查看:

沒有報錯,應該成功了,下面我們將全部接口函數改造:
~~~
<?php
declare (strict_types = 1);
namespace app\admin\controller;
use app\admin\model\Admin;
use app\admin\model\Category;
class Index
{
public function index()
{
return '您好!這是一個[index]示例應用';
}
public function add()
{
// 獲取前端傳值
$data = request() -> param();
if($data['model'] == "admin"){
// 密碼加密
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
// 使用模型格式化傳來的數據
$admin = new Admin;
// 利用模型將數據傳到數據庫
$admin->save([
'username' => $data['username'],
'password' => $data['password']
]);
// 返回結果
return '新增數據成功';
}else if($data['model'] == "category"){
$admin = new Admin;
// 利用模型將數據傳到數據庫
$admin->save([
'name' => $data['name'],
]);
// 返回結果
return '新增數據成功';
}
}
public function findall(){
// 接收數據
$data = request() -> param();
// return $data;
// 根據模型名判斷
if($data['model'] == "admin"){
$db_data = Admin::select();
// 返回查找到的數據
return $db_data;
}else if($data['model'] == "category"){
$db_data = Category::select();
// 返回查找到的數據
return $db_data;
}
}
public function find()
{
// 獲取前端傳值
$data = request() -> param();
if($data['model'] == "admin"){
$db_data = Admin::find($data['id']);
// 返回查找到的數據
return $db_data;
}else if($data['model'] == "category"){
$db_data = Category::find($data['id']);
// 返回查找到的數據
return $db_data;
}
}
public function update()
{
// 獲取前端傳值
$data = request() -> param();
// return $data;
if($data['model'] == "admin"){
// 密碼加密
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
// 靜態方法直接更新
$db_data = Admin::update($data, ['id' => $data['id']]);
return '修改數據成功';
}else if($data['model'] == "category"){
// 靜態方法直接更新
$db_data = Category::update($data, ['id' => $data['id']]);
return '修改數據成功';
}
}
public function delete()
{
// 獲取前端傳值
$data= request() -> param();
if($data['model'] == "admin"){
$db_data = Admin::find($data['id']);
$db_data->delete();
return '刪除數據成功';
}else if($data['model'] == "category"){
$db_data = Category::find($data['id']);
$db_data->delete();
return '刪除數據成功';
}
}
}
~~~
新增數據測試:

修改測試:

成功,都沒問題,到此通用接口完成。
###### 2.無限層級分類
(1)更改新建分類頁面
CategorySet.vue設置上級分類parent,位于分類名之上:
~~~
<el-form-item label="上級分類">
<el-select v-model="model.parent">
<!-- 使用select獲取分類名name和該分類的id,后期如果修改分類名自動更新子分類的上級分類 -->
<!-- 其中label獲取分類名,發送到數據庫的值為該分類的id————以id為分類尋找依據 -->
<el-option v-for="item in parentOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</el-form-item>
~~~
(2)使用查詢接口
下方js使用categories查詢分類接口獲取分類信息,將獲取到的數據傳入分類數據parentOptions中:
~~~
async fetchParentOptions(){
const res = await this.$http.get('rest/category')
this.parentOptions = res.data
}
~~~

此時頁面已接收到分類信息,并可以顯示分類名了。


因為是直接復制的,所以還是用戶名,改一下:

修改添加數據接口,添加parent字段:

此時新建分類:


成功:

這時候修改列表頁,將上級分類顯示出來:

保存后頁面將顯示上級分類:

此時我們需要讓上級分類以分類名形式顯示。
修改查詢所有內容接口:

此時刷新頁面得到關聯分類內容:

但這并不是我們需要的內容,我們要的是上級分類,但是現在給我們查找的是上級分類的所有下級分類,所以再次改動,添加反向查詢父級的方法:

改動find\_all接口函數:

刷新頁面測試:

成功查詢到上級分類數據。
修改前端頁面:

保存查看頁面:

成功。很好。
此時我們再次修改上級分類php1改回php:

由于是id關聯,所以上級分類變動后名字一起變化。
###### 3.關聯上級多個分類
此時我們再創建一個vue分類:

居然報錯,看來我們之前的代碼是將parent當作了必填項?找一下原因:
經過排查,是前端的原因,parent進入頁面時被parentOptions: \[\]定義成了數組,所以如果為空的話就是字符串格式。
所以我們需要在數據模型中提前讓parent定義成數組格式:

保存再次測試:

沒問題,下面正式開始多個上級分類關聯,例如加一個分類名為vue.js+tp6全棧開發的題目,上級分類為vue.js和tp6。
只需要修改前端頁面,在下拉框中加入一個屬性multiple:

又因為我們上一步已經將parent改為數組格式,所以可以放心上傳。
測試:


雖然成功了,但是列表頁一個都沒有顯示,查找原因:
然后我試了一下午,都沒有成功,攤牌了,我不會,大神教教我!
經過又半天的研究,我轉變了思路,部分有些復雜,數據表字段也變了一些,給大家展示下,大家自行研究一段時間研究不出來可以參照一下:
下篇文章學習圖片的上傳。
Category模型:
~~~
<?php
namespace app\admin\model;
use think\Model;
class Category extends Model{
// // 定義json數據
protected $json = ['parent_'];
// // 定義json數據查詢時返回數組
protected $jsonAssoc = true;
// 設置字段信息
protected $schema = [
'id' => 'int',
'name' => 'string',
'parent0' => 'string',
'parent1' => 'string',
'parent2' => 'string',
'parent3' => 'string',
'parent4' => 'string',
'parent0_' => 'string',
'parent1_' => 'string',
'parent2_' => 'string',
'parent3_' => 'string',
'parent4_' => 'string',
'parent_' => 'string'
];
// 查找下級分類
public function children(){
return $this -> hasMany(Category::class, 'parent', 'id');
}
// 查找上級分類
public function parent0(){
return $this -> belongsTo(Category::class, 'parent0', 'id');
}
public function parent1(){
return $this -> belongsTo(Category::class, 'parent1', 'id');
}
public function parent2(){
return $this -> belongsTo(Category::class, 'parent2', 'id');
}
public function parent3(){
return $this -> belongsTo(Category::class, 'parent3', 'id');
}
public function parent4(){
return $this -> belongsTo(Category::class, 'parent4', 'id');
}
}
~~~
接口函數:
~~~
<?php
declare (strict_types = 1);
namespace app\admin\controller;
use app\admin\model\Admin;
use app\admin\model\Category;
use app\admin\model\News;
class Index
{
public function index()
{
return '您好!這是一個[index]示例應用';
}
public function add()
{
// 獲取前端傳值
$data = request() -> param();
if($data['model'] == "admin"){
// 密碼加密
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
// 使用模型格式化傳來的數據
$db_data = new Admin;
// 利用模型將數據傳到數據庫
$db_data->save([
'username' => $data['username'],
'password' => $data['password']
]);
// 返回結果
return '新增數據成功';
}else if($data['model'] == "category"){
$db_data = new Category;
// 利用模型將數據傳到數據庫
$db_data -> name = $data['name'];
$db_data -> parent_ = $data['parent_'];
error_reporting(E_ERROR | E_WARNING | E_PARSE);
if($data['parent_'][0]){
$db_data -> parent0 = $data['parent_'][0];
}else{$data['parent_'][0] = '0';$db_data -> parent0 = $data['parent_'][0];}
if($data['parent_'][1]){
$db_data -> parent1 = $data['parent_'][1];
}else{$data['parent_'][1] = '0';$db_data -> parent1 = $data['parent_'][1];}
if($data['parent_'][2]){
$db_data -> parent2 = $data['parent_'][2];
}else{$data['parent_'][2] = '0';$db_data -> parent2 = $data['parent_'][2];}
if($data['parent_'][3]){
$db_data -> parent3 = $data['parent_'][3];
}else{$data['parent_'][3] = '0';$db_data -> parent3 = $data['parent_'][3];}
if($data['parent_'][4]){
$db_data -> parent4 = $data['parent_'][4];
}else{$data['parent_'][4] = '0';$db_data -> parent4 = $data['parent_'][4];}
$db_data -> save();
// 返回結果
return '新增數據成功';
}
}
public function findall(){
// 接收數據
$data = request() -> param();
// return $data;
// 根據模型名判斷
if($data['model'] == "admin"){
$db_data = Admin::select();
// 返回查找到的數據
return $db_data;
}else if($data['model'] == "category"){
$db_data = Category::with(['parent0','parent1','parent2','parent3','parent4'])->select()->toArray();
for($i = 0; $i<sizeof($db_data); $i++){
$db_data[$i]['parent'] = array();
if(is_array($db_data[$i]['parent0'])){
array_push($db_data[$i]['parent'], $db_data[$i]['parent0']);
}
if(is_array($db_data[$i]['parent1'])){
array_push($db_data[$i]['parent'], $db_data[$i]['parent1']);
}
if(is_array($db_data[$i]['parent2'])){
array_push($db_data[$i]['parent'], $db_data[$i]['parent2']);
}
if(is_array($db_data[$i]['parent3'])){
array_push($db_data[$i]['parent'], $db_data[$i]['parent3']);
}
if(is_array($db_data[$i]['parent4'])){
array_push($db_data[$i]['parent'], $db_data[$i]['parent4']);
}
}
// $db_data['parent'] = explode($db_data[$i]['parent0'],$db_data[$i]['parent1'],$db_data[$i]['parent2'],$db_data[$i]['parent3'],$db_data[$i]['parent4']);
return json($db_data);
}
}
public function find()
{
// 獲取前端傳值
$data = request() -> param();
if($data['model'] == "admin"){
$db_data = Admin::find($data['id']);
// 返回查找到的數據
return $db_data;
}else if($data['model'] == "category"){
$db_data = Category::with(['parent0','parent1','parent2','parent3','parent4'])->find($data['id'])->toArray();
$db_data['parent'] = array();
if(is_array($db_data['parent0'])){
array_push($db_data['parent'], $db_data['parent0']);
}
if(is_array($db_data['parent1'])){
array_push($db_data['parent'], $db_data['parent1']);
}
if(is_array($db_data['parent2'])){
array_push($db_data['parent'], $db_data['parent2']);
}
if(is_array($db_data['parent3'])){
array_push($db_data['parent'], $db_data['parent3']);
}
if(is_array($db_data['parent4'])){
array_push($db_data['parent'], $db_data['parent4']);
}
// 返回查找到的數據
return $db_data;
}
}
public function update()
{
// 獲取前端傳值
$data = request() -> param();
// return $data;
if($data['model'] == "admin"){
// 密碼加密
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
// 靜態方法直接更新
$db_data = Admin::update($data, ['id' => $data['id']]);
return '修改數據成功';
}else if($data['model'] == "category"){
error_reporting(E_ERROR | E_WARNING | E_PARSE);
$db_data = Category::where('id', $data['id'])->find();
// 利用模型將數據傳到數據庫
$db_data -> name = $data['name'];
$db_data -> content = $data['content'];
$db_data -> parent_ = $data['parent_'];
if($data['parent_'][0]){
$db_data -> parent0 = $data['parent_'][0];
}else{$data['parent_'][0] = '0';$db_data -> parent0 = $data['parent_'][0];}
if($data['parent_'][1]){
$db_data -> parent1 = $data['parent_'][1];
}else{$data['parent_'][1] = '0';$db_data -> parent1 = $data['parent_'][1];}
if($data['parent_'][2]){
$db_data -> parent2 = $data['parent_'][2];
}else{$data['parent_'][2] = '0';$db_data -> parent2 = $data['parent_'][2];}
if($data['parent_'][3]){
$db_data -> parent3 = $data['parent_'][3];
}else{$data['parent_'][3] = '0';$db_data -> parent3 = $data['parent_'][3];}
if($data['parent_'][4]){
$db_data -> parent4 = $data['parent_'][4];
}else{$data['parent_'][4] = '0';$db_data -> parent4 = $data['parent_'][4];}
// 靜態方法直接更新
$db_data -> save();
return '修改數據成功';
}
}
public function delete()
{
// 獲取前端傳值
$data = request() -> param();
if($data['model'] == "admin"){
$db_data = Admin::find($data['id']);
$db_data->delete();
return '刪除數據成功';
}else if($data['model'] == "category"){
$db_data = Category::find($data['id']);
$db_data->delete();
return '刪除數據成功';
}
}
}
~~~
CategorySet.vue:
~~~
<template>
<div>
<h1>{{ id ? "編輯" : "創建" }}分類</h1>
<el-form
label-width="80px"
style="margin-top: 20px"
@submit.native.prevent="save"
>
<el-form-item label="上級分類">
<el-select v-model="model.parent_" multiple>
<!-- 使用select獲取分類名name和該分類的id,后期如果修改分類名自動更新子分類的上級分類 -->
<!-- 其中label獲取分類名,發送到數據庫的值為該分類的id————以id為分類尋找依據 -->
<el-option
v-for="item in parentOptions"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="分類名">
<el-input v-model="model.name"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" native-type="submit">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
props: {
id: {},
},
data() {
return {
model: {
parent_: []
},
parentOptions: [],
};
},
methods: {
async save() {
let res;
if (this.id) {
res = await this.$http.put("rest/category/" + this.id, this.model);
} else {
res = await this.$http.post("rest/category", this.model);
}
console.log("en?", res);
this.$router.push("/categories/list");
this.$message({
type: "success",
message: "保存成功",
});
},
async fetch() {
const res = await this.$http.get("rest/category/" + this.id);
this.model = res.data;
},
async fetchParentOptions() {
const res = await this.$http.get("rest/category");
this.parentOptions = res.data;
},
},
created() {
this.id && this.fetch();
this.fetchParentOptions();
},
};
</script>
~~~
CategoryList.vue:
~~~
<template>
<div>
<h1>分類列表</h1>
<el-table :data="items">
<el-table-column prop="id" label="ID" width="220">
</el-table-column>
<el-table-column prop="parent[0].name,parent[1].name,parent[2].name,parent[3].name,parent[4].name" label="上級分類" width="220">
<template slot-scope="scope"> {{scope.row.parent[0].name}} {{scope.row.parent[1].name}} {{scope.row.parent[2].name}} {{scope.row.parent[3].name}} {{scope.row.parent[4].name}} </template>
</el-table-column>
<el-table-column prop="name" label="分類名稱">
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="100">
<template slot-scope="scope">
<el-button type="text" size="small" @click="$router.push('/categories/edit/' + scope.row.id)">編輯</el-button>
<el-button @click="remove(scope.row)" type="text" size="small">刪除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
items: []
}
},
methods: {
async fetch(){
const res = await this.$http.get('rest/category')
this.items = res.data
},
remove(row){
this.$confirm('是否確定要刪除分類"' + row.name + '"?', '提示', {
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
// 要想使用await,函數必須使用async
// await異步執行,待調用接口獲取數據完成后再將值傳給res,進行下一步操作
const res = await this.$http.delete('rest/category/' + row.id)
this.$message({
type: 'success',
message: '刪除成功!'
});
if(res.status == 200){
// 接口調用成功后,刷新頁面
this.fetch()
}
}).catch(() => {
this.$message({
type: 'info',
message: '已取消刪除'
});
});
}
},
created() {
this.fetch()
}
}
</script>
~~~
結果頁面:


- tp6+vue
- 1.工具和本地環境
- 2.啟動項目
- 3.路由、模型與數據庫操作
- 4.優化后端接口,前端使用axios實現接口功能
- 5.用戶登錄,bcrypt(hash)加密與驗證
- 6.用戶登錄(二),token驗證
- 7.分類的模型關聯和通用CRUD接口
- 8.使用vue的markdown編輯器并批量上傳圖片
- Node.js + Vue.js
- 工具,本地環境
- 2.1啟動項目
- 3.element-ui和vue-router路由的安裝和使用
- 4.使用axios,并創建接口上傳數據到mongodb數據庫
- 5.mongoodb數據庫的“刪、改、查”操作
- 6.mongodb數據庫無限層級的數據關聯(子分類)
- 7.使用mongodb數據庫關聯多個分類(關聯多個數據)
- 8.server端使用通用CRUD接口
- 9.圖片上傳
- 10.vue的富文本編輯器(vue2-editor)
- 11.動態添加分欄上傳多組數據
- 12-1.管理員模塊
- 13-1.搭建前臺web端頁面
- 1.使用sass工具搭建前臺web端頁面
- 2.sass工具的變量
- 3.使用flex布局并開始搭建web端
- 4.vue廣告輪播圖,并使用接口引入數據
- 5.使用字體圖標(iconfont)
- 6.卡片組件的封裝
- 14-1.生產環境編譯
- 1.環境編譯
- 2.購買域名服務器并解析域名到服務器
- 3.nginx配置web服務器并安裝網站環境
- 4.git拉取代碼到服務器
- 5.配置Nginx反向代理
- 6.遷移本地數據到服務器(mongodump)
- uni
- 1.工具&本地環境
- 2.頁面制作
- 3.頁面制作、組件與輪播
- 4.頁面跳轉與橫向滑動
- 5.用戶授權登錄和用戶信息獲取
- 6.用戶注冊和數據存儲
- 7.用戶填寫表單信息