pinahq / framework
Pina2 框架。
Requires
- php: >=5.4.0
- league/climate: 3.2.*
- league/csv: ^8.1
- monolog/monolog: 1.23.*
- phpmailer/phpmailer: 5.2.*
- pinahq/sql-parser: 0.3
- psr/container: ^1.0
- smarty/smarty: 2.6.*
Requires (Dev)
- phpunit/phpunit: 6.*
- dev-master
- 0.7.1
- 0.7
- 0.6.7
- 0.6.6
- 0.6.5
- 0.6.4
- 0.6.3
- 0.6.2
- 0.6.1
- 0.6
- 0.5.8
- 0.5.7
- 0.5.6
- 0.5.5
- 0.5.4
- 0.5.3
- 0.5.2
- 0.5.1
- 0.5.0
- 0.4.26
- 0.4.25
- 0.4.24
- 0.4.23
- 0.4.22
- 0.4.21
- 0.4.20
- 0.4.19
- 0.4.18
- 0.4.17
- 0.4.16
- 0.4.15
- 0.4.14
- 0.4.13
- 0.4.12
- 0.4.11
- 0.4.10
- 0.4.9
- 0.4.8
- 0.4.7
- 0.4.6
- 0.4.5
- 0.4.4
- 0.4.3
- 0.4.2
- 0.4.1
- 0.4
- 0.3.32
- 0.3.31
- 0.3.30
- 0.3.29
- 0.3.28
- 0.3.27
- 0.3.26
- 0.3.25
- 0.3.24
- 0.3.23
- 0.3.22
- 0.3.21
- 0.3.20
- 0.3.19
- 0.3.18
- 0.3.17
- 0.3.16
- 0.3.15
- 0.3.14
- 0.3.13
- 0.3.12
- 0.3.11
- 0.3.10
- 0.3.9
- 0.3.8
- 0.3.7
- 0.3.6
- 0.3.5
- 0.3.4
- 0.3.3
- 0.3.2
- 0.3.1
- 0.3
- 0.2.38
- 0.2.37
- 0.2.36
- 0.2.35
- 0.2.34
- 0.2.33
- 0.2.32
- 0.2.31
- 0.2.30
- 0.2.29
- 0.2.28
- 0.2.27
- 0.2.26
- 0.2.25
- 0.2.24
- 0.2.23
- 0.2.21
- 0.2.20
- 0.2.19
- 0.2.18
- 0.2.17
- 0.2.16
- 0.2.15
- 0.2.14
- 0.2.13
- 0.2.12
- 0.2.11
- 0.2.10
- 0.2.9
- 0.2.8
- 0.2.7
- 0.2.6
- 0.2.5
- 0.2.4
- 0.2.3
- 0.2.2
- 0.2.1
- 0.2.0
- 0.1.14
- 0.1.13
- 0.1.12
- 0.1.11
- 0.1.10
- 0.1.9
- 0.1.8
- 0.1.7
- 0.1.6
- 0.1.5
- 0.1.4
- 0.1.3
- 0.1.2
- 0.1.1
- 0.1
- 0.0.22
- dev-feature-two-step-view
- dev-feature-foreign-key
- dev-release-0.2
This package is auto-updated.
Last update: 2024-09-23 11:10:02 UTC
README
简介
Pina 框架 - 用于开发 RESTful、无状态应用程序的 PHP 框架,内置队列(gearman)支持和异步事件处理。在数据库操作层面,Pina 支持基于模型类自动生成表结构和触发器(作为迁移的替代方案)。
我们的策略
遵循 PSR 标准,在原则上不影响性能和框架理念的情况下,采用与 Laravel 相似的方法和类名命名。
我们的理念
促进在 PHP 上开发高效的无状态异步 RESTful 应用程序。
基于以下几个基本立场
- 数据库结构和触发器 - 应与项目中的其他业务逻辑一起存储在代码库中,并自动生成。
- 路由基础 - 明确地将 RESTful 资源“集合/元素/集合...”映射到控制器,允许放弃大型的路由表。
- 模块化,使用命名空间(namespace)进行绑定。
- 广泛使用队列和后台进程来处理耗时的操作(目前基于 gearman)。
Pina 框架不是孤立存在的,它是在几个具有复杂业务逻辑的实际项目中成长起来的。希望我们能一起将它发展到新的质量水平。
安装
Pina 不保证未来支持低于 PHP7 的版本,但目前在 PHP 5.6+ 上可以正常工作。
需要 PHP 扩展
- mysqli
- mbstring
- xml
要开始使用,需要下载应用程序的 bootstrap 版本(即将在 github 和 composer 中提供),通过 composer 更新依赖项,设置正确的网站地址和数据库配置,并运行 php pinacli.php system.update 以更新数据库。
配置
在 config 文件夹中包含配置文件。每个文件都是一个返回关联数组的 PHP 文件。使用 \Pina\Config 类来访问设置。
从 config/app.php 文件获取所有设置
\Pina\Config::load(‘app’);
获取用于连接数据库的用户
\Pina\Config::get(‘db’, ‘user’);
文件夹结构
app 文件夹
这里在 default 文件夹中包含您的应用程序,分为 Layout、Modules、Skin 子文件夹。Layout 包含页面结构的模板,Modules 包含控制器、模板和模块类,Skin 包含通用的视觉元素模板。
为什么所有内容都存储在 default 文件夹中?Pina 支持主题模板的覆盖。主题存储在与 default 同一层的文件夹中。因此,您始终在 default 中有原始模板,而在模板文件夹中有覆盖的模板。
bootstrap 文件夹
包含启动框架所需的一些标准文件
config 文件夹
包含配置文件
public 文件夹
在此存储 index.php 文件,它处理所有 HTTP 请求,也存储静态文件(css、js、图片)。
tests 文件夹
用于单元测试的文件夹
var 文件夹
存储编译后的模板版本、临时文件和日志的文件夹
错误处理和日志记录
...
模块化
应用程序被拆分为模块。每个模块是 Pina\Modules 内部的特定命名空间(namespace)。例如 Pina\Modules\Catalog。
通常模块的代码位于 app/default/Modules 内的文件夹中(因为默认情况下 PSR-4 自动加载已设置为这种方式,这不会妨碍您通过 composer 加载新的模块)。
模块的关键元素是位于模块命名空间内的Module类。系统在初始化模块时加载它,并且它指示模块的确切位置。
模块类实现了Pina\ModuleInterface接口。因此,它至少需要定义四个方法
- getPath() - 返回模块目录的路径
- getNamespace() - 返回模块命名空间的名字
- getTitle() - 返回模块的名字
- boot() - 每次应用程序启动时都会执行。
此外,模块类还可以实现针对不同启动模式的方法。例如,方法http()
会在每次启动HTTP请求处理时被调用。而方法cli()
会在每次从命令行处理命令时被调用。这些方法应返回适用于其应用程序类型的路由配置。
此外,通常在模块类中还会在模块命名空间中声明函数。例如,本地化函数__()
例如,声明模块类
<?php
namespace Pina\Modules\Images;
use Pina\ModuleInterface;
use Pina\Event;
class Module implements ModuleInterface
{
public function getPath()
{
return __DIR__;
}
public function getNamespace()
{
return __NAMESPACE__;
}
public function getTitle()
{
return 'Images';
}
public function boot()
{
Event::subscribe($this, 'image.resize');
}
public function http()
{
return [
'cp/images',
'images',
];
}
public function cli()
{
return [
'image',
];
}
}
function __($string)
{
return \Pina\Language::translate($string, __NAMESPACE__);
}
在这里,在boot()
方法中注册了事件处理程序以处理事件'image.resize',在http()
方法中注册了处理程序以处理集合images和cp/images,而在cli()
方法中注册了命令空间image。
除了属于模块命名空间的类之外,模块中还包含几个文件夹
- 文件夹http包含用于处理HTTP请求的控制器和视图。
- 文件夹helpers包含模块的自定义smarty函数。
- 文件夹events包含事件处理程序。
- 文件夹cli包含命令行控制器的文件夹。
- 文件夹lang用于存储应用程序的本地化文件。
HTTP层
路由
为了提高路由效率并摆脱传统的地址掩码与控制器相关联的路由表,在Pina中存在一系列对系统URL结构的限制。不过,如果您不满意这个系统,那么框架允许您注册自己的调度器并自行处理任意地址,这对于CMS系统非常有用。但这将在另一个部分中说明。
让我们回到应该覆盖应用程序大部分需求的基本路由。
Pina要求您根据REST方法组织URL结构。也就是说,根据“集合/元素”的原则组织URL地址。例如,书籍列表的地址是/books,ID为5的书籍的地址是/books/5。用户alex的书籍的地址是/users/alex/books。系统直观易懂,但有重要限制:您不能通过跳过元素来在集合内部设置集合。也就是说,地址/users/books会被解释为集合users中的用户books。
这个限制使得可以明确地将多个集合映射到控制器,从而忽略元素部分。例如,对资源/users/alex/books的GET请求将由项目文件夹/users/books中的控制器组处理。
请注意,每个资源结构元素对应一组控制器。以下将解释为什么我们会这样做。
HTTP协议支持不同的请求方法,但通常使用GET、POST、PUT、DELETE,因为它们非常适合与典型的CRUD操作相对应
如果将HTTP方法视为CRUD操作,并将它们与“集合/元素”术语中的资源相对应,则会得到以下解释
因此,可以在一个集合上设置各种操作。通常,在其他框架中,这些操作由一个类控制器的单个方法设置。但我们注意到,这些方法实际上相互之间没有关联,因为它们实现的是不同的逻辑,并且我们将它们分成几个典型控制器,每个控制器对应一种操作类型。我们将这些控制器命名为通常命名的类控制器方法,并将它们放在处理操作的集合对应的文件夹中。
请注意,整体上,控制器由HTTP方法定义。
但GET请求有一个例外(特殊方法create)。因此,对于GET请求,系统区分
- 集合(index.php),
- 元素(show.php),
- 以及用于输入创建新元素信息的资源(create.php)。
这将在嵌套集合中如何工作
控制器存储在模块根目录下的http文件夹中。由于可能有多个模块,因此需要确定哪个模块负责哪个集合。为此,模块只需要在模块的http类方法中确认对集合的所有权即可。
class Module implements ModuleInterface
{
...
public function http()
{
//здесь список коллекций, за которые отвечает модуль
return [
'users',
];
}
模块的http方法在每次应用程序启动时都会启动,并在处理HTTP请求的开始时作为工作结果传递它所处理的集合(控制器)列表。
因此,用户模块可以拥有用户集合,并且默认拥有所有嵌套集合,但另一个模块(例如,书籍模块)可以声明对用户书籍集合的所有权。
class Module implements ModuleInterface
{
...
public function http()
{
return [
'users/books',
];
}
这种方法使路由简单快捷。
控制器
因此,我们已经了解了控制器文件应该放在哪里以处理特定的URL地址。现在,让我们谈谈这种控制器是如何工作的。
Pina控制器的主要思想是,对于同一个控制器,可以使用多个不同的视图。因此,控制器只是接收输入参数,并在输出时发送计算结果。通常,控制器不需要关心这些结果如何被解释以及如何显示。
为了实现这些目标,控制器与Pina\Request和Pina\Response类一起工作。
获取参数的方式如下:Request::input('id');
控制器通过return语句返回结果。
return [
'resources' => $resources,
'paging' => $paging->fetch(),
];
在这种情况下,控制器将传递数据,这些数据将根据选择的表现形式进行解释。但是,控制器也可以以某种特定格式返回数据。例如,JSON。
return Response::ok()->json($resource);
假设我们的控制器基于参数‘post_id’应从数据库中获取博客记录并将其作为结果返回。
$postId = Request::input(‘post_id’);
$post = PostGateway::instance()->find($postId);
return $post;
让我们使问题更复杂。现在,如果记录未找到,则需要返回404页面。
$postId = Request::input(‘post_id’);
$post = PostGateway::instance()->find($postId);
if (empty($post)) {
return Response::notFound();
}
return $post;
如果没有传递必需的参数post_id,则需要返回400 bad request错误代码。
$postId = Request::input(‘post_id’);
if (empty($postId)) {
return Response::badRequest();
}
$post = PostGateway::instance()->find($postId);
if (empty($post)) {
return Response::notFound();
}
return $post;
通常,控制器应返回数据或实现Pina\ResponseInterface接口的类的实例。
为了在Pina\Request类中访问请求参数,有一组方法。
- 获取所有参数的数组:
Request::all();
- 获取特定参数:
Request::input('name');
- 获取特定参数,如果不存在,则使用默认值:
Request::input('name', 'default');
- 获取某些参数:
Request::only('key1', 'key2', 'key3');
或Request::only(['key1', 'key2', 'key3']);
- 获取所有参数除了某些参数:
Request::except('key1', 'key2', 'key3');
或Request::except(['key1', 'key2', 'key3']);
- 获取某些参数,如果它们存在:
Request::intersect('key1', 'key2', 'key3');
或Request::intersect(['key1', 'key2', 'key3']);
您可能已经注意到,这些方法接口与Laravel中使用的接口非常相似。确实如此。
验证
……
嵌套请求。
控制器可以处理外部HTTP请求,也可以处理来自视图或其他控制器的内部调用。这允许使用控制器来处理HTML页面的单独块,直接从模板化器中连接和断开它们。
视图(View)
控制器可能有几种不同的视图类型。目前,在自动检测级别支持两种主要类型:JSON视图和HTML视图。但您也可以使用return语句和具体的Pina\ResponseInterface接口实例返回任何视图。
要获取JSON视图,只需使用Accept: application/json或Accept: text/json标头调用资源。在这种情况下,作为请求结果传递的数据将打包为JSON对象。总的来说,设计控制器时,应该确保其工作结果与对其的请求相关联。即使主要的表现形式是HTML而不是JSON,也要单独考虑其结果。
用于构建HTML表示的模板引擎是Smarty。模板位于与控制器相同的文件夹中,名称相同(但扩展名不同:tpl,而不是php)。一个控制器可能有多个HTML表示。表示的选择由display参数控制。
例如
通常,POST、PUT和DELETE请求不实现HTML表示,只是将客户端重定向到所需地址。在这种情况下,控制器必须返回一个包含重定向地址的Response类实例。
return Response::found(App::link('resources/:resource_id', ['resource_id' => $resourceId]));
模板
Smarty是一个功能强大的模板引擎,您可以在Smarty的官方网站上了解其基本功能,而我将介绍我们用于基于Pina构建应用程序的主要技巧和Smarty函数。
父模板(布局)
在app/default/Layout文件夹中存储父模板,其中包含查询结果的绘制。默认情况下使用main.tpl模板,但您可以通过smarty函数在特定显示中更改父模板。
{extends layout=”single”}
在这种情况下将使用父模板single.tpl
将绘制结果作为变量{$content}传递给父模板。
通常,简单地将查询结果的绘制插入父模板的某个位置是不够的。例如,我们除了中心部分还需要填充标题和title标签。
父模板可能看起来像这样
main.tpl
<html>
<head>
<title>{place name=”title”}</title>
</head>
<body>
<h1>{place name=”title”}</h1>
<article>{$content}</article>
</body>
</html>
要单独将内容传递给标题区域,在子模板中需要使用smarty函数{content}
show.tpl
{content name=title}{$post.post_title}{/content}
<p>{$post.post_text}</p>
在显示中使用其他控制器
您可以通过smarty函数{module}从显示中访问其他控制器。
例如
{module get="books/:book_id" book_id=$book.book_id}
在这种情况下,将执行GET请求"books/:book_id",其中:book_id将被变量$book.book_id的值替换。结果将以HTML形式绘制,并将其插入到调用位置。
可以向这个smarty函数添加额外的参数,包括用于选择特定显示的display参数。
一个好的策略是创建简单的控制器,并在需要时在模板中将它们组合在一起。
Pina允许您在已有准备好的数据的情况下使用另一个控制器的显示,而无需调用该控制器本身。为此,可以使用smarty函数{view}
{view get="books/:book_id" book=$book}
在这种情况下,变量book将被传递到模板中,就像它被控制器计算出来一样。
链接
用于构建页面间链接的是smarty函数{link}
{link get="books/:book_id" book_id=$book.book_id}
此示例将形成指向资源books/:book_id的链接,其中:book_id将被$book.book_id的值替换。无法在链接模板中替换的参数将作为普通GET参数添加。
{link get="books/:book_id" book_id=5 display=edit}
将变成链接/books/5?display=edit
链接上下文
链接模板中的值来自链接参数。如果参数中没有要替换的元素,则使用上下文中的值。上下文可以是全局的也可以是局部的。
全局上下文通过指令设置(通常在模块类的方法http()中调用)
Route::context('language', 'ru');
现在,在所有{link get="panel/:language/books"}链接中,:language部分将被'ru'替换,除非参数中指定了其他值。全局上下文仅适用于替换。
为了设置局部上下文,存在一个块状smarty函数{link_context},它为所有嵌套在其内的链接添加自己的参数,即使没有替换。
{link_context author_id=$filter.author_id sort=$sort}
<ul class="nav">
{section loop=$pages name=page}
<li><a href="{link get="books" page=$page}"></a></li>
{/section}
</ul>
{/link}
每个{link_context}调用内的链接都将获得author_id和sort参数。
自定义smarty函数
模块的自定义smarty函数可以放在模块的helpers文件夹中。
本地化
在php中使用函数
__("Оригинальная строка на перевод")
在模板中使用块状smarty函数{t}或修改器|t
{t}Оригинальная строка на перевод{/t}
{some_function v="Оригинальная строка перевод"|t}
数据库
数据库连接设置
负责数据库连接设置的配置由config/db.php文件管理。例如
return array(
'default' => array(
'host' => 'localhost',
'port' => '3306',
'user' => 'root',
'pass' => 'root',
'base' => 'pina2',
'charset' => 'utf8',
)
);
DB类
DB类连接到数据库并执行基本查询。类的方法通常接收已准备好的查询,并主要管理结果格式。
使用