基于PHP的mvc框架

基于PHP的mvc框架:https://gitee.com/fyiyy/yogurt

 

yogurt

介绍

yogurt是基于php的mvc框架

软件架构

软件架构说明

安装教程

  1. 下载地址:https://gitee.com/fyiyy/yogurt

使用说明

基础

1.安装yogurt

yogurt的环境要求如下:

PHP >= 7.3

PDO PHP Extension

MBstring PHP Extension

CURL PHP Extension

2.开发规范

3.目录结构

project 应用部署目录

├─application 应用目录(可设置)

├─config 公共配置

├─framework 框架系统目录

├─public WEB 部署目录(对外访问目录)

│ ├─static 静态资源存放目录(css,js,image)

│ ├─index.php 应用入口文件

│ ├─router.php 快速测试文件

│ └─.htaccess 用于 apache 的重写

├─runtime 应用的运行时目录(可写,可设置)

├─vendor 第三方类库目录(Composer)

架构

1.架构总览

2.入口文件

默认的应用入口文件位于public/index.php,内容如下:

namespace yogurt;

require_once __DIR__ . '/../framework/start.php';
 

3.URL访问

没有启用路由的情况下典型的URL访问规则是:

http://serverName/模块/控制器/操作?[参数名=参数值...] http://serverName/index.php(或者其它应用入口文件)?m=模块&c=控制器&a=操作&[参数名=参数值...]

配置

1.配置目录

系统默认的配置文件目录就是项目目录(ROOT_PATH),也就是默认的project下面,并分为应用配置(整个应用有效)和模块配置(仅针对该模块有效)。

2.配置格式

返回PHP数组的方式是默认的配置定义格式,例如:

return [
    // 调试模式
    'debug'                 => true,
    // 访问日志写入
    'log_write'             => true,
    // 默认模板后缀
    'default_template_exit' => 'html',
    // URL模式, 可选值:0=>兼容模式,1=>pathinfo,2=>rewrite
    'url_model'             => 0,
    // 默认模块
    'default_model'         => 'index',
    // 默认控制器
    'default_controller'    => 'index',
    // 默认方法
    'default_action'        => 'index',
    // 控制器文件夹,自定义修改必须保证有对应的目录
    'controller_directory'  => 'controller',
    // 视图文件夹,自定义修改必须保证有对应的目录
    'view_directory'        => 'view',
    // 程序app文件名
    'app_name'              => 'application',
    // 是否强制开启路由模式
    'url_route_must'        => false,
];
 

3.读取配置

设置完配置参数后,就可以使用get方法读取配置了,例如:

echo Config::get('配置参数1');
 

如果需要读取二级配置,可以使用:

echo Config::get('配置参数.二级参数');
 

4.环境变量配置

在开发过程中,可以在应用根目录下面的.env来模拟环境变量配置,.env文件中的配置参数定义格式采用ini方式,例如:

app_debug =  true
app_trace =  true
 
[DATABASE]
HOSTNAME = 127.0.0.1,127.0.0.1
DATABASE = yogurt
USERNAME =  root
PASSWORD =  root,root
 

路由

1.路由模式

2.路由定义

在route目录下面定义任意php文件,目前路由只支持精确匹配,暂不支持正则匹配

use yogurt\Route;

Route::get('/hello', function () {
    return 'hello';
});

Route::get('/route', function () {
    return 'route';
});

Route::get('/index/hello', '/index/index/yogurt');
 

控制器

1.控制器定义

yogurt的控制器定义比较灵活,可以无需继承任何的基础类,也可以继承官方封装的\yogurt\Controller类或者其他的控制器类。

控制器定义

一个典型的控制器类定义如下:

namespace app\index\controller;

class Index 
{
    public function index()
    {
        return 'index';
    }
}
 

控制器类文件的实际位置是:

application\index\controller\Index.php
 

如果继承了yogurt\Controller类的话,可以直接调用yogurt\View及yogurt\Request类的方法,例如:

namespace app\index\controller;

use yogurt\Controller;

class Index extends Controller
{
    public function index()
    {
        // 获取包含域名的完整URL地址
        $this->assign('domain',$this->request->url(true));
        return $this->fetch('index');
    }
}
 

2.控制器重定向

\yogurt\Controller类的redirect方法可以实现页面的重定向功能。

//重定向到News模块的Category操作
Response::redirect('News/Category');
 

请求

1.请求信息

如果要获取当前的请求信息,可以使用\yogurt\Request类, 除了下文中的

Request::get('id');
 

2.请求类型

Request::isPost();
 

3.参数绑定

按名称绑定 参数绑定方式默认是按照变量名进行绑定,例如,我们给Blog控制器定义了两个操作方法read和archive方法,由于read操作需要指定一个id参数,archive方法需要指定年份(year)和月份(month)两个参数,那么我们可以如下定义:

namespace app\index\Controller;

class Blog 
{
    public function read($id)
    {
        return 'id='.$id;
    }

    public function archive($year='2016',$month='01')
    {
        return 'year='.$year.'&month='.$month;
    }
}
 

URL的访问地址分别是:

http://serverName/index.php/index/blog/read/?id=1
http://serverName/index.php/index/blog/archive?year=2016&month=06
 

4.依赖注入

Yogurt的依赖注入(也称之为控制反转)是一种较为轻量的实现,无需任何的配置,并且主要针对访问控制器进行依赖注入。可以在控制器的构造函数或者操作方法(指访问请求的方法)中类型声明任何(对象类型)依赖,这些依赖会被自动解析并注入到控制器实例或方法中。

在控制器的架构方法中会自动注入当前请求对象,例如:

namespace app\index\controller;

use yogurt\Request;

class Index
{
	public function hello(Request $request)
    {
        return 'Hello,' . $request->param('name') . '!';
    }
    
}
 

数据库

1.连接数据库

常用的配置在config目录下面的database.php中添加下面的配置参数:

use yogurt\Env;

return [
    // 数据库类型:mysql,sqlsrv
    'type'               => 'mysql',
    // 服务器地址
    'hostname'           => Env::get('database.hostname', '127.0.0.1'),
    // 数据库名
    'database'           => Env::get('database.database', 'yogurt'),
    // 用户名
    'username'           => Env::get('database.username', 'root'),
    // 密码
    'password'           => Env::get('database.password', 'root'),
    // 端口
    'hostport'           => '3306',
    // 连接dsn
    'dsn'                => '',
    // 数据库连接参数
    'params'             => [],
    // 数据库编码默认采用utf8
    'charset'            => 'utf8',
    // 数据库表前缀
    'prefix'             => 'y_',
    // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
    'deploy'             => 1,
    // 数据库读写是否分离 主从式有效
    'rw_separate'        => true,
    // 读写分离后 主服务器数量
    'master_number'      => 1,
];
 

2.基本使用

配置了数据库连接信息后,我们就可以直接使用数据库运行原生SQL操作了,支持query(查询操作)和execute(写入操作)方法,并且支持参数绑定。

Db::query("select * from y_user where id=1");
Db::execute("insert into y_user (id, name) values (1, 'yogurt')");
 

3.查询构造器

查询一个数据使用:

// table方法必须指定完整的数据表名
Db::table('y_user')->where('id',1)->find();
 

查询数据集使用:

Db::table('y_user')->where('status',1)->select();
 
添加数据

使用 Db 类的 insert 方法向数据库提交数据

$data = ['name' => 'yogurt'];
Db::table('y_user')->insert($data);
 
添加多条数据
$data = [
    ['name' => 'yogurt'],
    ['name' => 'yogurt1'],
    ['name' => 'yogurt2']
];
Db::name('user')->insertAll($data);
 
更新数据
Db::table('y_user')->where('id', 1)->update(['name' => 'yogurt']);
 
删除数据表中的数据
Db::table('y_user')->where('id',1)->delete();
 
链式操作

表达式查询

Db::table('y_user')
    ->where('id','>',1)
    ->where('name','yogurt')
    ->select(); 
 

数组条件

$map['name'] = 'yogurt';
$map['status'] = 1;
// 把查询条件传入查询方法
Db::table('y_user')->where($map)->select(); 
 

4.分布式数据库

Yogurt内置了分布式数据库的支持,包括主从式数据库的读写分离,但是分布式数据库必须是相同的数据库类型。

配置database.deploy 为1 可以采用分布式数据库支持。如果采用分布式数据库,定义数据库配置信息的方式如下

//分布式数据库配置定义
return [
    // 启用分布式数据库
    'deploy'    =>  1,
    // 数据库类型
    'type'        => 'mysql',
    // 服务器地址
    'hostname'    => '192.168.1.1,192.168.1.2',
    // 数据库名
    'database'    => 'demo',
    // 数据库用户名
    'username'    => 'root',
    // 数据库密码
    'password'    => '',
    // 数据库连接端口
    'hostport'    => '',
];
 

模型

1.定义

视图

1.视图实例化

2.模版引擎

3.模版赋值

除了系统变量和配置参数输出无需赋值外,其他变量如果需要在模板中输出必须首先进行模板赋值操作,绑定数据到模板输出有下面几种方式:

namespace index\app\controller;

class Index extends \yogurt\Controller
{
    public function index()
    {
        // 模板变量赋值
        $this->assign('name','Yogurt');
        $this->assign('email','1719847255@qq.com');
        // 或者批量赋值
        $this->assign([
            'name'  => 'Yogurt',
            'email' => '1719847255@qq.com'
        ]);
        // 模板输出
        return $this->fetch('index');
    }
}
 

4.模版渲染

模版

1.模版定位

2.模版标签

普通标签用于变量输出和模板注释,普通模板标签默认以{ 和 } 作为开始和结束标识,并且在开始标记紧跟标签的定义,如果之间有空格或者换行则被视为非模板标签直接输出。 例如:{<span id="MathJax-Span-2" class="noError">name} 、{<span class="MJX_Assistive_MathML">name} 、{vo['name']} 都属于正确的标签,而{ <span id="MathJax-Element-2-Frame" class="MathJax" data-mathml="<math xmlns="http://www.w3.org/1998/Math/MathML"><merror><mtext>name} 、{</mtext></merror></math>"><span id="MathJax-Span-3" class="math"><span id="MathJax-Span-4" class="noError">name} 、{<span class="MJX_Assistive_MathML">name} 、{vo.name}则不属于。

要更改普通标签的起始标签和结束标签,可以更改下面的配置参数:

return [
    // 模板后缀
    'view_suffix'   => 'html',
    // 标签左界定符
    'taglib_begin'  => '{',
    // 标签右界定符
    'taglib_end'    => '}',
    // 是否支持原生PHP标签
    'php_turn'     => true,
];
 

3.变量输出

4.模版注释

{#...# 或者 *...#,注释}
 

5.模版继承

模板继承是一项更加灵活的模板布局方式,模板继承不同于模板布局,甚至来说,应该在模板布局的上层。模板继承其实并不难理解,就好比类的继承一样,模板也可以定义一个基础模板(或者是布局),并且其中定义相关的区块(block),然后继承(extend)该基础模板的子模板中就可以对基础模板中定义的区块进行重载。

因此,模板继承的优势其实是设计基础模板中的区块(block)和子模板中替换这些区块。

每个区块由{block} {/block}标签组成。 下面就是基础模板中的一个典型的区块设计(用于设计网站标题):

{block name="title"}<title>网站标题</title>{/block}
 

block标签必须指定name属性来标识当前区块的名称,这个标识在当前模板中应该是唯一的,block标签中可以包含任何模板内容,包括其他标签和变量,例如:

{block name="title"}<title>{$web_title}</title>{/block}
 

你甚至还可以在区块中加载外部文件:

{block name="include"}{include file="public/header" /}{/block}
 

一个模板中可以定义任意多个名称标识不重复的区块,例如下面定义了一个base.html基础模板:


<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>{block name="title"}标题{/block}</title>
</head>
<body>
{block name="menu"}菜单{/block}
{block name="left"}左边分栏{/block}
{block name="main"}主内容{/block}
{block name="right"}右边分栏{/block}
{block name="footer"}底部{/block}
</body>
</html>
 

然后我们在子模板(其实是当前操作的入口模板)中使用继承:

{extend name="base" /}
{block name="title"}{$title}{/block}
{block name="menu"}
<a href="/">首页</a>
<a href="/info/">资讯</a>
<a href="/bbs/">论坛</a>
{/block}
{block name="left"}{/block}
{block name="main"}
{volist name="$list" id="vo"}
<a href="/new/{$vo['id']}">{$vo['title']}</a><br/>
{$vo['content']}
{/volist}
{/block}
{block name="right"}
最新资讯:
{volist name="$news" id="new"}
<a href="/new/{$new['id']}">{$new['title']}</a><br/>
{/volist}
{/block}
{block name="footer"}
@Yogurt 版权所有
{/block}
 

6.包含文件

{include file="public/header" /} // 包含头部模版header
 

7.内置标签

volist标签通常用于查询数据集(select方法)的结果输出,通常模型的select方法返回的结果是一个二维数组,可以直接使用volist标签进行输出。 在控制器中首先对模版赋值:

$list = User::select();
$this->assign('list',$list);
 

在模版定义如下,循环输出用户的编号和姓名:

{volist name="$list" id="vo"}
{$vo['id']}:{$vo['name']}<br/>
{/volist}
 

循环标签


{foreach $data  item="$item"}
<p>{$item['id']} {$item['name']}</p>
{/foreach}

{foreach $data  as $key=>$vo}
<p>{$vo['id']} {$vo['name']}</p>
{/foreach}
 

IF标签

{loop name="$data" id='vo'}
{if ($vo['id'] == 15)}
<p style="color: red;">{$vo['id']} {$vo['name']}</p>
{else}
<p>{$vo['id']} {$vo['name']}</p>
{/if}
{/loop}
 

日志

1.日志写入

2.日志读取

杂项

1.缓存

Yogurt采用yogurt\Cache类提供缓存功能支持。

缓存支持采用驱动方式,所以缓存在使用之前,需要进行连接操作,也就是缓存初始化操作。

设置
return [
    // 缓存类型为File,支持file,redis
    'type'  =>  'file',
    // 全局缓存有效期(0为永久有效)
    'expire'=>  0,
    // 缓存前缀
    'prefix'=>  'yogurt',
    // 缓存目录
    'path'  =>  '../runtime/cache/',
    // redis缓存配置
    'redis' => [
        'host'	   => '127.0.0.1',
        'port'     => 6379,
        'password' => ''
    ]
];
 
使用

设置缓存(有效期一个小时)

Cache::set('name',$value,3600);
 

获取缓存

Cache::get('name');
 

删除缓存

Cache::rm('name');
 

清空缓存

Cache::clear();
 

2.Session

设置(有效期一个小时)

Session::set('name',$value,3600);
 

获取

Session::get('name');
 

删除

Session::delete('name');
 

清空

Session::clear();
 

3.Cookie

4.上传

前端页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="upload.html" method="post" enctype="multipart/form-data">
    <input type="file" name="image" id="">
    <input type="submit" value="上传">
</form>
</body>
</html>
 
public function upload()
{

    $file = Request::file('image')->validate(['size' => 1024, 'ext' => 'jpg,jpeg,png,gif,xls'])->rule('md5')->move('upload/');
    echo '<pre>';
    print_r($file->getError());
    echo '</pre>';
    echo '<pre>';
    print_r($file->getSaveName());
    echo '</pre>';
    exit();
}
 

中间件

1.定义

在应用目录下面middleware目录中定义控制器中间件

namespace app\middleware;


use Closure;

class CheckLogin
{

    /**
     * 响应处理请求
     * @param $request
     * @param \Closure $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // 请求响应前
        $response = $next($request);
        // 请求响应后
        return $response;
    }

}
 

2.使用

在控制器中定义中间件属性$middleware

namespace app\index\controller;

use yogurt\Controller;

class Base extends Controller
{
    /**
     * 中间件属性
     * @var string[] $middleware
     */
    protected $middleware = [
        'app\middleware\CheckLogin',
    ];

}
 

容器

1.使用

if (!app()->make('yogurt\Cache')->get('login')) {
    app()->make('yogurt\Cache')->set('login', 'yogurt', 10);
}
halt(app()->make('yogurt\Cache')->get('login'));
posted @ 2022-01-28 18:02  程序员小艺  阅读(208)  评论(0)    收藏  举报