TOP-framework/README.md

17 KiB
Raw Blame History

TOP-Framework

这是一个部分代码源自三年前毕业设计中的代码集合,后经过一系列重构,形成的一套框架。在此准备写一个文档。

模块

创建模块

  1. 手动创建 在application可更改目录下创建home目录再创建config、controller必须、model、view目录
  2. 命令行自动创建 命令格式([]为可选参数):
php create.php 目录 模块名 [入口文件]

进入framework/create/目录,执行以下命令:

php create.php application home index

至此已经通过简单的命令创建了home模块浏览器输入127.0.0.1即可访问home模块系统默认模块为home模块可在入口文件中指定亦可命令行访问home模块。

控制器

创建控制器

一个典型的控制器Index.php

<?php

namespace app\home\controller;

class Index
{
    public function index()
    {
        return [];
    }
}

其中包括了命名空间、以及一个默认的index方法index方法返回数组或布尔值true则会显示模板如果返回的是数字或字符串则会直接输出返回对象直接输出[OBJECT],关于模板部分后面会详细介绍。

如果当前控制器继承自top\library\Controller基础控制器则会继承以下方法

  1. json($msg, $code = 1, $data = [], $ext = [])

返回json数据。

  1. cache($status = true)

如果在方法中调用了此方法则会将模板做静态缓存,缓存时间在配置文件中设置。

  1. param($name, $value)

将参数传递到模板文件。

  1. view($file = '', $param = [], $cache = false)

显示模板(得到模板文件渲染后的内容)。

  1. redirect($url)

利用header函数跳转。

  1. tips($message, $url = '', $sec = 3)

如果是AJAX请求则会返回json数据调用json方法普通请求则返回tips模板文件渲染后的内容。

展示模板

public function index()
{
    return $this->view();
}

调用基础控制器中的view方法、并return出去完成模板的展示。

模板传值

  1. view方法
public function index()
{
    return $this->view(null, [
        'param' => 'Hello world!'
    ]);
}
  1. 直接return数组
public function index()
{
    return [
        'param' => 'Hello world!'
    ];
}

以上两种方式等效。

控制器方法的前置、后置操作

public function before_index()
{
}

public function after_index()
{
}

public function index()
{
    return [
        'param' => 'Hello world!'
    ];
}

命名规范before_方法名前置、after_方法名后置执行index方法之前会先执行before_index方法如果存在执行完index方法后会执行after_index方法如果存在。当前置方法return的值为空字符串、null、true时才会继续执行否则前置方法的return等效于index方法的return。

模型

创建模型

一个典型的模型Users.php

<?php

namespace app\home\model;

use top\library\Model;

class 模型名称 extends Model
{
}

系统会根据模型名称去绑定对应同名称的数据表模型名称为Users时则绑定名为”前缀_users“的数据表。如果模型名称为UsersInfo时则绑定名为“前缀_users_info”的数据表。

继承自top\library\Model基础模型后模型将拥有以下方法或属性

方法:

  1. data($data = [], $notRaw = true) 获取即将操作的数据

接收两个参数参数一指定的数据数组传入空数组则为POST数据。参数二是否返回进行数据表字段过滤的原始数据布尔值

未通过验证则返回false否则返回数组。

  1. query($query) 执行一条SQL语句

成功返回true失败抛出DatabaseException异常。

  1. insert($data = []) 插入一条记录

传入数组为即将插入的记录

成功返回受影响的记录数失败抛出DatabaseException异常。

  1. update($data, [$param = false]) 更新一条记录

第一个参数为即将更新的数据,可传入第二个参数为主键。

$this->update([
    'username' => 'TOP糯米'
], 1);

除此之外,提供另一种方式,传递更多条件或更复杂的条件

$this->update([
    'username' => 'TOP糯米'
], function ($model) {
    $model->where('id', 1);
});

当然,也可以使用连贯操作

$this->where('id', 1)->update([
    'username' => 'TOP糯米'
]);

成功返回受影响的记录数失败抛出DatabaseException异常。

  1. find($param = false, $notRaw = true) 查找一条记录

可传入第一个参数为主键第二个参数为是否按指定的规则outReplace属性进行处理。

一般调用

$this->find(1);

匿名函数

$this->find(function ($model) {
    $model->where('id', 1);
});

连贯操作

$this->where('id', 1)->find();

成功返回一个一维数组失败抛出DatabaseException异常。

  1. select($param = false, $notRaw = true) 查找多条记录

使用方法同find

成功返回一个二维数组失败抛出DatabaseException异常。

  1. delete 删除记录

直接传入主键

$this->delete(1);

匿名函数

$this->delete(function () {
    $model->where('id', 1);
});

连贯操作

$this->where('id', 1)->delete();

成功返回受影响的记录数失败抛出DatabaseException异常。

  1. count 返回记录数

一般调用

$this->count();

第一个参数同样可以为匿名函数、并且同样支持连贯操作

成功返回记录数失败抛出DatabaseException异常。

  1. avg 计算平均值

接收一个参数当没有使用field方法指定字段时可直接传入字段名以计算平均值。

$this->avg('score');

使用field方法指定字段

$this->field('score')->avg();

匿名函数中指定字段或条件

$this->avg(function ($model) {
    $model->where('score', '>=', 60);
    $model->field('score');
});

成功返回平均值失败抛出DatabaseException异常。

  1. max 计算最大值

同avg方法

  1. min 计算最小值

同avg方法

  1. sum 求和

同avg方法

  1. _sql 返回最后执行的SQL语句

  2. tableDesc 返回表结构

接收参数为一个完整表名。

成功返回表结构失败抛出DatabaseException异常。

  1. distinct 过滤记录中的重复值

接收一个为字段名称的参数

调用

$this->distinct('sex')->select();

失败抛出DatabaseException异常。

  1. effect 删除时指定表(别)名

接收一个参数,可以为字符串或数组,参数为表名或表别名

调用

$this->delete(function ($model) {
    $model->effect('s,this');
    $model->join('left', 'score', 's')->on('s.uid = this.id');
    $model->where(['this.id' => 3]);
});
  1. field 指定字段

可传入字符串或数组

  1. where 指定条件

最多可接收三个参数

仅传入一个参数时,可传入字符串或数组

$where = ['id' => ['>', 10], 'sex' => 1];
$where = 'id > 10 and sex = 1';
$this->where($where)->select();

两个参数,解析为“字段=值”

$this->where('sex', 1)->select();

三个参数,指定字段与字段值的连接符

$this->where('sex', '>', 1)->select();
  1. order 对结果进行排序
$this->order('id desc')->select();

也可以使用匿名函数调用order方法

  1. limit 查询范围

接收一个参数,可以是字符串或数组

一般调用

$this->limit('0, 5')->select();
$this->limit([0, 5])->select();

两种方式等效

  1. join 加入多表进行查询通常情况下与on方法同时使用

接收三个参数第一个参数为连接方式空、left、right、full第二个参数为表名不包含前缀第三个参数为别名当前表会自动将”this“作为别名

一般调用

$this->select(function ($model) {
    $model->join('left', 'score', 's')->on('s.uid = this.id');
});

同样也可以使用连贯操作

  1. on 表字段连接关系

见join方法

属性:

  1. $table 指定当前模型的表名(优先于模型名称)
protected $table = 'users';
  1. $pk 指定当前模型的主键(如果不指定,程序将自动查询以当前模型命名的表的主键)
protected $pk = 'id';
  1. $map 指定传入数据的键与数据库字段名称的映射关系
protected $map = [
    'name' => 'username'
];
  1. $inReplace 入库时替换值

数据入库时自动格式化时间为unix时间戳

protected $inReplace = [
    'create_time' => ['formatTime', true]
];

protected function formatTime($time)
{
    return strtotime($time);
}

至此在数据在被写入数据库之前会先调用inReplace中设定的函数、方法并将return的值作为新的值写入数据库。

注意当以字段为键名的数组的值为一个字符串时则该字符串为即将调用的函数如果值为一个数组且无第二个值或第二个值为false、空则该数组第一个值为即将调用的函数如第二个值为true则表示当前调用的方法存在于本类或父类中。

  1. $outReplace 出库时替换值
protected $outReplace = [
    'sex' => ['outFormatSex', true]
];

protected function outFormatSex($sex)
{
    switch ($sex) {
        case 1:
            return '男';
            break;
        case 2:
            return '女';
            break;
        default:
            return '未知';
    }
}

注意当以字段为键名的数组的值为一个字符串时则该字符串为即将调用的函数如果值为一个数组且无第二个值或第二个值为false、空则该数组第一个值为即将调用的函数如第二个值为true则表示当前调用的方法存在于本类或父类中。

  1. $updateReplace 数据更新时替换值

基本类似于inReplace但仅当执行更新操作时执行。

  1. $validate 自动验证

验证不为空

protected $validate = [
    'username' => ['notNull', '用户名不能为空'],
];

验证不等于

protected $validate = [
    'username' => ['notEqual', '0', '用户名不能为0'],
];

多条件用户名不为空且不为0

protected $validate = [
    'username' => [
        ['notNull', '用户名不能为空'],
        ['notEqual', '0', '用户名不能为0'],
    ],
];

自定义函数验证新建函数demo到函数库

function demo($name, $n1, $n2)
{
    if ($name == $n1 || $name == $n2) {
        return false;
    }
    return true;
}

添加到自动验证

protected $validate = [
    'username' => [
        ['notNull', '用户名不能为空'],
        ['notEqual', '0', '用户名不能为0'],
        ['demo', 'TOP糯米', '张三', '用户名不能为TOP糯米或张三'],
    ],
];

调用模型

调用模型有两种方式:

  1. model函数推荐 model函数会返回一个模型的单例使用方式与直接new无差别。
$object = model(模型);
  1. new 模型
$object = new 模型();

模板

模板继承

模板继承通过extend标签与block标签配合使用实现。 一个最简单的继承

// Base/layout.html父级模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<block name="body"></block>
</body>
</html>

// Index/index.html

<extend file="Base/layout" />
<block name="body">
    <h3>内容主体</h3>
</block>

模板标签

内置一些常用标签

  1. php

php标签。此标签为闭合标签标签内的内容将被解析为原生php代码执行。

<php>
    echo '你好';
</php>
  1. if

if标签。此标签为闭合标签condition属性为if的条件属性列表condition。

<if condition="$age eq 10">
    // do something...
</if>
  1. else

else标签。此标签为自闭合标签可选属性condition存在condition属性则被解析为elseif属性列表condition可选

<if condition="$age eq 10">
    // do something...
<else />
    // do something...
</if>

<if condition="$age eq 10">
    // do something...
<else condition="$age eq 20" />
    // do something...
</if>
  1. volist

循环标签。此标签为闭合标签属性列表name、id、key可选

<volist name="lists" id="item">
    {$item['id']}
</volist>

<volist name="lists" id="item" key="i">
    {$i}、{$item['id']}
</volist>
  1. assign

赋值标签在模板中创建新的php变量。此标签为自闭合标签属性列表name、value。

<assign name="username" value="TOP糯米" />
  1. raw

该标签为闭合标签。raw标签中的内容不会被编译。

<raw>
    <volist name="lists" id="item">
        {$item['id']}
    </volist>
</raw>

上例volist标签会被原样输出。

  1. 变量、函数输出
// 变量输出
{$username}

// 调用函数,左定界符后加上:表示调用函数
{:mb_substr($username, 0, 3, 'utf8')}

自定义标签

新建自定义标签库类文件/application/home/taglib/Extend.php目录及文件名称没有要求。

闭合标签

namespace app\home\taglib;

class Extend
{
    public $tags = [
        'test' => ['attr' => 'start,length,id', 'close' => 1]
    ];

    public function _test($tag, $content)
    {
        $parse = '<?php ';
        $parse .= 'for ($' . $tag['id'] . ' = ' . $tag['start'] . '; $' . $tag['id'];
        $parse .=  ' < ' . $tag['start'] . ' + ' . $tag['length'] . '; ';
        $parse .= '$' . $tag['id'] . '++): ?>';
        $parse .= $content;
        $parse .= '<?php endfor; ?>';
        return $parse;
    }
}

类创建完成后到配置文件config.php的view下的tagLib中添加Extend类

'view' => [
        'tagLib' => [
            \app\home\taglib\Extend::class
        ]
    ],

添加完成后即可在模板中使用

<test start="1" length="10" id="test">
    {$test}
</test>

自闭合标签

添加一个描述

'say' => ['attr' => 'what', 'close' => 0]

新建_say方法

public function _say($tag)
{
    return "<?php echo '{$tag['what']}'; ?>";
}

模板调用

<say what="Hello world!" />

自定义路由

路由配置文件位于 application 下文件名route.php 现有News控制器中的detail方法

public function detail($id)
{
    return [
        'id' => (int) $id
    ];
}

假设访问地址为: http://127.0.0.3/home/news/detail/id/1.html

必须参数

添加如下规则

'detail' => [
    '[id]',
    'home/news/detail'
]

完成后,可使用 http://127.0.0.3/detail/1.html 访问到对应位置。

可选参数

修改detail方法

public function detail($id = 0)
{
    return [
        'id' => (int) $id
    ];
}

添加路由规则

'detail' => [
    '[:id]',
    'home/news/detail'
]

完成后,可使用 http://127.0.0.3/detail.html 访问到对应位置如果没传递id则使用默认值。

多个参数

'detail' => [
    '[id][:type]',
    'home/news/detail'
]

其他

Request类

获取实例

  1. instance方法获取单例
Request::instance();
  1. request函数获取单例
request();

供调用的方法

  1. isPost

判断是否是POST请求

  1. isGet

判断是否是GET请求

  1. isPut

判断是否是PUT请求

  1. isDelete

判断是否是DELETE请求

  1. isHead

判断是否是HEAD请求

  1. isPatch

判断是否是PATCH请求

  1. isOptions

判断是否是OPTIONS请求

  1. isAjax

判断是否是AJAX请求

  1. create

创建一个HTTP请求

原型create($url, $data = [], $header = [])

第一个参数为请求的链接第二个参数为将要POST的数据第三个参数为指定Header参数

  1. ip

返回客户端IP地址

  1. module

当前请求的模型名称

  1. classname

当前请求的完整控制器名称

  1. controller

当前请求的不包含命名空间的控制器名称

  1. method

当前请求的方法名称

  1. params

当前请求所带的参数

  1. get

获取get数据

原型get($name = '*', $except = [], $filter = 'filter')

第一个参数为将要获取的变量名称(' * ' 为全部),第二个参数为过滤的变量,第三个参数为指定的过滤函数(可以为自定义函数名称或匿名函数)。

函数名称:

request()->get('id', ['type'], 'filter');

匿名函数:

request()->get('id', ['type'], function ($value) {
    return (int) $value;
});
  1. post

获取post数据

使用同get方法

  1. except

指定过滤的变量

取出全部get数据但不包括type

request()->except('type')->get();

面向控制器的前置、后置方法(请求拦截)

创建application/home/filter/Auth.php测试文件

namespace app\home\filter;

use top\middleware\ifs\MiddlewareIfs;

class Auth implements MiddlewareIfs
{
    public function before()
    {
        return '拒绝请求';
    }

    public function after($data)
    {
        // TODO: Implement after() method.
    }
}

创建完成后,加入配置

'middleware' => [
    \app\home\filter\Auth::class
],

现在,访问项目则会得到 ' 拒绝请求 ' 结果。仅当before方法return的值为true时程序才会继续执行否则return等效于控制器方法的return。