# 留言版系统

### 安装

* 项目名: message

```php
composer create-project topthink/think message
```

### 多应用模式

* 前端
* 后端
* 中间件

```php
# 安装多应用扩展
composer require topthink/think-multi-app
    
# 三大应用index(前端)，admin(后台),common(公共应用)
php think build index
php think build admin
php think build common
```

```php
D:\phpstudy_pro\WWW\message>tree app      # APP 应用结构
├─admin            # 后端
└─index            # 前端
└─common           # 中间件
```

### 开启 debug 调式

* 方便开发时候错误信息的定位

```php
# .example.env 修改 .env
APP_DEBUG = true
......
```

### 渲染视图模板

```php
# 安装
composer require topthink/think-view
```

<figure><img src="https://2134947750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvILwbD2PrkBCkSitM3m4%2Fuploads%2Fwr3Wwxbz8XdVctImuoLJ%2F8b458497040fb405e3e776673d279359.png?alt=media&#x26;token=1d7ac27f-062e-43a6-b63c-c26b73314879" alt=""><figcaption></figcaption></figure>



### 模板输出替换

* config/view.php

```php
return [
    ...
    'tpl_replace_string'  =>  [
        '__STATIC__'=>'/static',  // 使用 __STATIC__ 替换 /static
    ]
];

```

* app/index/view/index/index.html

```html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>留言系统</title>
  <link href="__STATIC__/layui/css/layui.css" rel="stylesheet">
</head>
<body>

<div class="layui-container">

    <div class="title">
        留言系统
    </div>

    <form class="layui-form" action="" style="margin-top: 10px;">
    <div class="layui-form-item">
        <label class="layui-form-label">称呼：</label>
        <div class="layui-input-block">
        <input type="text" name="name" lay-verify="required" placeholder="请输入您的称呼" autocomplete="off" class="layui-input">
        </div>
    </div>

    <div class="layui-form-item">
        <div class="layui-form">
        <label class="layui-form-label">电话：</label>
        <div class="layui-input-block">
            <input type="tel" name="mobile" lay-verify="required" autocomplete="off" placeholder="请输入您的称呼"  class="layui-input demo-phone">
        </div>
        </div>
    </div>

    <div class="layui-form-item">
        <div class="layui-form">
        <label class="layui-form-label">邮箱：</label>
        <div class="layui-input-block">
            <input type="text" name="email" lay-verify="required" autocomplete="off" placeholder="请输入您的邮箱" lay-affix="clear" class="layui-input demo-phone">
        </div>
        </div>
    </div>


    <div class="layui-form-item layui-form-text">
        <label class="layui-form-label">内容：</label>
        <div class="layui-input-block">
            <textarea name="content" lay-verify="required"  placeholder="请输入您的内容" class="layui-textarea"></textarea>
        </div>
    </div>
    <div class="layui-form-item">
        <div class="layui-input-block">
        <button type="button"   class="layui-btn"   lay-submit   onclick="message()">立即提交</button>
        <button type="reset" class="layui-btn layui-btn-primary">重置</button>
        </div>
    </div>
    </form>

</div>
  

<script src="__STATIC__/layui/layui.js"></script>


<style>
    .title {
        font-size: 48px;
        width: 100%;
        text-align: center;
        margin: 50px 0px 50px 0px;
    }
</style>
</body>
</html>
```

### 连接数据库

* .env --→ config/database.php
*

```php
# .env
APP_DEBUG = true

[APP]
DEFAULT_TIMEZONE = Asia/Shanghai

[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = tp_db
USERNAME = tp_db
PASSWORD = 123456
HOSTPORT = 3306
CHARSET = utf8
DEBUG = true
PREFIX = message_

[LANG]
default_lang = zh-cn
```

### 创建留言表

```sql
 /* 数据库 tp_db, 留言表 message_index */

CREATE TABLE `tp_db.message_index` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  `content` text,
  `create_at` int(11) DEFAULT NULL,
  PRIMARY KEY (`uid`),
  UNIQUE KEY `message_index_id_IDX` (`uid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 COMMENT='留言系统表';
```

### 定义主键和自动时间戳

* app/index/model/Index.php （默认没有，自己创建）

```php
<?php


namespace app\index\model;
use think\Model;


class Message extends Model
{
    // 默认主键为id
    protected $pk = 'uid';
    // 模型类里面单独开启自动时间戳功能，写入数据的时候，系统会自动写入create_time和update_time字段
    // protected $autoWriteTimestamp = true;

    // 定义时间戳字段名
    protected $createTime = 'create_at';

    // 关闭自动写入update_time字段
    protected $updateTime = false;

}
```

### AJAX提交表单

```php

<script type="text/javascript">

    var layer = layui.layer;
    var _POST_URL = '/message'
    function message(){
        $.ajax({
            type: "POST",
            dataType: "json",
            url: _POST_URL,
            data: $('.layui-form').serialize(),
            success: function(result) {
                if (result.code) {
                    layer.msg(result.msg)
                    setTimeout(()=> {
                        location.reload()
                    }, 1000)
                    
                } else {
                    layer.msg(result.msg)
                }
            },
            error: function(e) {
                layer.msg(e.msg)
            }
        })
    }
</script>
 
```

### 表格分页渲染

```php


<style>
    .title {
        font-size: 48px;
        width: 100%;
        text-align: center;
        margin: 50px 0px 50px 0px;
    }


    .pagination{text-align:center;margin-top:20px;margin-bottom: 20px;}
    .pagination li{margin:8px 10px; border:1px solid #e6e6e6; padding: 0px 5px;display: inline-block;}
    .pagination .active{background-color:#009688; color:#fff;}
    .pagination .disabled{color :#aaa;}

</style>
    
    
    <table class="layui-table">
        <colgroup>
          <col width="150">
          <col width="150">
          <col>
          <col>
          <col>
        </colgroup>
        <thead>
          <tr>
            <th>索引</th>
            <th>ID</th>
            <th>称呼</th>
            <th>电话</th>
            <th>邮箱</th>
            <th>内容</th>
          </tr> 
        </thead>
        <tbody>
            {volist name='list' id='v' key="k" }
                <tr>
                    <td>{$k}</td>
                    <td>{$v.uid}</td>
                    <td>{$v.name}</td>
                    <td>{$v.mobile}</td>
                    <td>{$v.email}</td>
                    <td>{$v.content}</td>
                </tr>
            {/volist}
        </tbody>    

            
      </table>
      {$list|raw}

```

### 路由设置

```php
# app\message\route\app.php
    
<?php


use think\facade\Route;

# 路由地址: http://xxx.com/app_name

// Route::get('$', 'message/Index/index');
// Route::post('', 'message/Index/save');            // 添加路由
// Route::delete('<id>', 'Index/save');     // 删除路由
// Route::get('<id>/edit', 'Index/edit');   // 修改展示路由
// Route::put('<id>', 'Index/update');      // 修改处理路由


Route::resource('', 'message/Index');    // 资源路由 resource（表达式,控制器名）
```

### 模型设置

```php
## app\message\model\Message.php

<?php


namespace app\message\model;
use Exception;
use think\exception\ValidateException;
use think\Model;

use app\message\validate\Add;


class Message extends Model
{

    // 默认是 message_message 表，这里子定义表结构
    protected $table = 'message_index';

    // 默认主键为id
    protected $pk = 'uid';
    // 模型类里面单独开启自动时间戳功能，写入数据的时候，系统会自动写入create_time和update_time字段
    // protected $autoWriteTimestamp = true;

    // 定义时间戳字段名
    protected $createTime = 'create_at';

    // 关闭自动写入update_time字段
    protected $updateTime = false;


    // // 获取器，转变字符为数组；例如：数据库中 name 字段 "a,b" -> ['a','b']
    // public function getNameAttr($value)
    // {
    //     return explode(',', $value);
    // }

    // // 设置器，转变字符为数组；例如：前端数据 name 字段 ['a','b'] -> "a,b"
    // public function setNameAttr($value)
    // {
    //     return implode(',', $value);
    // }


    // 静态方法获取列表
    public static function  list() 
    {
        return self::order('uid', 'desc')->paginate(10);
        // return self::order('uid')->select();
    }

    public static function add($data)
    {
        try{
            validate(Add::class)->check($data);
            self::create($data);
            $arr=['code'=>1, 'msg'=>'添加成功'];
        }catch (ValidateException $e) {
            $arr=['code'=>0, 'msg'=>$e->getError()];
        }catch (Exception $s) {
            $arr=['code'=>0, 'msg'=>'系统错误,请稍后再试'];
        }
        return $arr;
    }


    public static function edit($data)
    {
        try{
            validate(Add::class)->check($data);
            self::update($data); // 数据中含有 uid 字段,所以这里不用写先查询再更新
            $arr=['code'=>1, 'msg'=>'添加成功'];
        }catch (ValidateException $e) {
            $arr=['code'=>0, 'msg'=>$e->getError()];
        }catch (Exception $s) {
            $arr=['code'=>0, 'msg'=>'系统错误,请稍后再试'];
        }
        return $arr;
    }


    public static function del($id)
    {
        try {
            self::destroy($id);
            $res = ['code'=>1, 'msg'=>'删除成功'];
        } catch (Exception $e) {
            $res = ['code'=>0, 'msg'=>'删除失败'];
        }

        return $res;
    }


}
```

### 验证器设置

```php
# app\message\validate\Add.php

<?php

namespace app\message\validate;

use think\Validate;

class Add extends Validate
{
    /**
     * 定义验证规则
     * 格式：'字段名'=>['规则1','规则2'...]
     *
     * @var array
     */
    protected $rule = [
        'name'    => 'require|max:50',
        'mobile'  => 'require|max:25',
        'email'   => 'require|email',
        'content' => 'require|max:500',
    ];
    
    /**
     * 定义错误信息
     * 格式：'字段名.规则名'=>'错误信息'
     *
     * @var array
     */
    protected $message = [
       'name.require'     => '留言称呼不能为空', 
       'name.max'         => '留言称呼过长', 
       'mobile.require'   => '留言号码不能为空', 
       'mobile.max'       => '留言号码过长', 
       'email.require'    => '留言邮箱不能为空', 
       'email.email'      => '留言邮箱格式错误', 
       'content.require'  => '留言内容不能为空', 
       'content.max'      => '留言内容过长,请控制在 500 字符内', 
    ];
    /**
     * 定义验证场景
     * 格式：'场景名'=>['规则1','规则2',...]
     *
     * @var array
     */
    protected $scene = [];
}

```

### 视图API设置

```php
# app\message\controller\Index.php

<?php
// declare (strict_types = 1);  // declare (strict_types = 1);  // 开启严格模式

namespace app\message\controller;
use app\BaseController;


use app\message\model\Message as MessageModel;
use think\facade\View;



class Index extends BaseController
{
    /**
     * 显示资源列表
     *
     * @return \think\Response
     */
    public function index()
    {
        $list = MessageModel::list();
        // return json($list);
        View::assign('list', $list);
        return View::fetch();
        // return view('', ['list'=>$list] );
    }

    /**
     * 显示创建资源表单页.
     *
     * @return \think\Response
     */
    public function create()
    {
        //
    }

    /**
     * 保存新建的资源
     *
     * @param  \think\Request  $request
     * @return \think\Response
     */
    public function save()
    {
        // halt('asdf');
        $data=$this->request->param();
        $res=MessageModel::add($data);
        return json($res);
    }

    /**
     * 显示指定的资源
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function read($id)
    {
        //
    }

    /**
     * 显示编辑资源表单页.
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function edit($id)
    {
        $info = MessageModel::find($id);
        return json($info);
    }

    /**
     * 保存更新的资源
     *
     * @param  \think\Request  $request
     * @param  int  $id
     * @return \think\Response
     */
    public function update($id)
    {
        $data = $this -> request -> param();
        $res = MessageModel::edit($data);
        return json($res);
    }

    /**
     * 删除指定资源
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function delete($id)
    {
        $res = MessageModel::del($id);
        return json($res);
    }
}

```

### 效果图

* 访问: http://127.0.0.1:8000/message&#x20;

<figure><img src="https://2134947750-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FvILwbD2PrkBCkSitM3m4%2Fuploads%2F28yFPgvkwGFSa14sv2ec%2F21a8e07c070adde3f9c5484d1531cd97.png?alt=media&#x26;token=aab682de-1289-47fc-920b-52523bacb20b" alt=""><figcaption></figcaption></figure>
