一个简单的小型PHP MVC框架骨架,封装了许多包围有强大安全层的功能。
MinIPHP是一个非常简单的应用程序,可用于小型项目,有助于了解PHP MVC骨架,知道如何进行身份验证和授权,加密数据并应用安全概念,消毒和验证,进行AJAX调用等等。
这不是一个完整的框架,也不是一个非常基本的框架,但并不复杂。您可以在任何项目中轻松安装,理解和使用它。
缩进了删除框架的复杂性。我不是从头开始发明的路由,身份验证,授权,管理用户会话和cookie等的东西,但是,它们是在其他框架中已经实现的概念的汇总,但是,以一种简单的方式构建的概念,因此,您可以理解它,并将其进一步了解。
如果您需要构建更大的应用程序,并利用框架中可用的大多数功能,则可以看到Cakephp,Laravel,Symphony。
无论哪种方式,重要的是要了解PHP MVC骨架,并知道如何进行身份验证和授权,了解安全问题以及如何使用该框架来击败以及如何构建自己的应用程序。
完整的文档也可以在此处找到 - 由GitHub自动页面生成器创建。
现场演示可以在这里使用。实时演示是针对本节本框架之上构建的演示应用程序。感谢@everterstraat。
某些功能Migh在演示中不起作用。
通过作曲家安装
composer install
每当您向应用程序提出请求时,它都会直接使用index.php公共文件夹。因此,如果提出请求: http://localhost/miniPHP/User/update/412 。这将被拆分并翻译成
实际上,HTACCESS将所有内容分解为http://localhost/miniPHP ,并将其添加到URL中作为querystring参数。因此,此请求将转换为: http://localhost/miniPHP?url='User/update/412' 。
然后, App类(在splitUrl()内部,将查询字符串$_GET['url']分为控制器,操作方法以及对操作方法的任何传递的参数。
在App类,Inside run()中,它将从控制器类实例化对象,并进行调用操作方法,如果存在,则通过任何参数。
在App类Intantiating Controller对象之后,它将调用$this->controller->startupProcess()方法,这又将触发3个连续的事件/方法:
initialize() :使用它加载组件beforeAction() :在调用控制器的操作方法之前执行任何逻辑操作triggerComponents() :加载组件的触发启动()方法不应覆盖Controller类的构造函数,相反,您可以在扩展类中覆盖initialize() & beforeAction()方法。
在重制的启动过程完成工作之后,将调用请求的操作方法,并将通过参数(如果有)。
组件是中间的。它们提供可重复使用的逻辑,以用作控制器的一部分。在组件内实现了身份验证,授权,篡改和验证CSRF令牌。
最好将这些逻辑从控制器类中删除,并在这些组件中保留所有各种任务和验证。
每个组件都从称为Component的基本/超级类继承。每个都有一个定义的任务。有两个组件,一个用于身份验证和授权的auth ,另一个用于其他安全问题的安全性。
它们非常易于处理,并且将在控制器构造函数内部称为。
用户有正确的凭据吗?
AuthComponent会处理用户会话。
您有权访问还是执行X操作? AUTH组件负责每个控制器的授权。因此,每个控制器都应实现isAuthorized()方法。您需要做的是返回boolean值。
因此,例如,为了检查当前用户是否是管理员,您将做类似的事情:
// AdminController
public function isAuthorized (){
$ role = Session:: getUserRole ();
if ( isset ( $ role ) && $ role === " admin " ){
return true ;
}
return false ;
}如果您想进一步应用并应用一些权限规则,则有一个强大的类,称为Permission负责定义权限规则。此类允许您定义“允许谁在当前控制器上执行特定的操作方法”。
因此,例如,为了允许管理员对注释执行任何操作,而普通用户只能编辑其注释:
// NotesController
public function isAuthorized (){
$ action = $ this -> request -> param ( ' action ' );
$ role = Session:: getUserRole ();
$ resource = " notes " ;
// only for admins
// they are allowed to perform all actions on $resource
Permission:: allow ( ' admin ' , $ resource , [ ' * ' ]);
// for normal users, they can edit only if the current user is the owner
Permission:: allow ( ' user ' , $ resource , [ ' edit ' ], ' owner ' );
$ noteId = $ this -> request -> data ( " note_id " );
$ config = [
" user_id " => Session:: getUserId (),
" table " => " notes " ,
" id " => $ noteId
];
// providing the current user's role, $resource, action method, and some configuration data
// Permission class will check based on rules defined above and return boolean value
return Permission:: check ( $ role , $ resource , $ action , $ config );
}现在,您可以根据用户的角色,资源和每个操作方法检查授权。
SecurityComponent负责各种安全任务和验证。
限制请求方法很重要。例如,如果您有一种接受表单值的操作方法,则仅接受发布请求。 Ajax的想法相同,获取,.. etc。您可以在beforeAction()方法中执行此操作。
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ actions = [ ' create ' , ' delete ' ];
$ this -> Security -> requireAjax ( $ actions );
$ this -> Security -> requirePost ( $ actions );
}另外,如果您需要通过有安全连接的所有请求,则可以配置整个控制器或特定操作,以将所有请求重定向到HTTPS而不是HTTP。
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ actions = [ ' create ' , ' delete ' ]; // specific action methods
$ actions = [ ' * ' ]; // all action methods
$ this -> Security -> requireSecure ( $ actions );
}它检查并验证请求是否来自同一域。尽管它们可以伪造,但最好将它们作为我们安全层的一部分。
验证提交的表格来自邮政请求。此方法的陷阱是您需要定义预期的表单字段,或者将随后请求发送的数据。
默认情况下,框架将在提出发布请求时验证是否篡改表单,并确保将CSRF令牌与表单字段传递。在这种情况下,如果您没有通过CSRF令牌,它将被视为安全线程。
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ action = $ this -> request -> param ( ' action ' );
$ actions = [ ' create ' , ' delete ' ];
$ this -> Security -> requireAjax ( $ actions );
$ this -> Security -> requirePost ( $ actions );
switch ( $ action ){
case " create " :
$ this -> Security -> config ( " form " , [ ' fields ' => [ ' note_text ' ]]);
break ;
case " delete " :
// If you want to disable validation for form tampering
// $this->Security->config("validateForm", false);
$ this -> Security -> config ( " form " , [ ' fields ' => [ ' note_id ' ]]);
break ;
}
}CSRF令牌对于验证提交的表格很重要,并确保它们不会伪造。黑客可以欺骗用户向网站提出请求,或单击链接,依此类推。
它们在一定持续时间内有效(> = 1天),然后将其再生并存储在用户的会话中。
默认情况下禁用CSRF验证。如果要验证CSRF代币,则如下示例所示,为true分配validateCsrfToken 。当请求是发布并启用表单篡改时,CSRF验证将被迫。
现在,您无需在每个请求中手动验证CSRF令牌。安全组件将在请求中验证令牌与会话中存储的令牌。
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ action = $ this -> request -> param ( ' action ' );
$ actions = [ ' index ' ];
$ this -> Security -> requireGet ( $ actions );
switch ( $ action ){
case " index " :
$ this -> Security -> config ( " validateCsrfToken " , true );
break ;
}
}CSRF令牌是每个会话生成的。您可以添加隐藏的表单字段,也可以在URL中作为查询参数添加。
形式
<input type="hidden" name="csrf_token" value="<?= Session::generateCsrfToken(); ?>" />
URL
<a href="<?= PUBLIC_ROOT . "?csrf_token=" . urlencode(Session::generateCsrfToken()); ?>">Link</a>
JavaScript
您还可以将CSRF令牌分配给JavaScript变量。
<script>config = <?= json_encode(Session::generateCsrfToken()); ?>;</script>
index.php 。有时,您需要对这些组件进行控制,例如当想要在没有身份验证或授权的情况下具有控制器或启用安全组件。这可以通过在控制器类中的Override initialize()方法来完成,并且只需加载所需的组件。
示例1 :不要加载任何组件,没有身份验证,授权或安全验证。
public function initialize (){
$ this -> loadComponents ([]);
}示例2 :加载安全性和auth组件,但不要进行身份验证和授权,以防万一要在操作方法中使用auth组件。 LoginController是如何无需登录的用户访问页面的示例。
public function initialize (){
$ this -> loadComponents ([
' Auth ' ,
' Security '
]);
}示例3 :加载安全性和AUTH组件,并验证当前控制器的用户并授权。这是核心/控制器类中的默认行为
public function initialize (){
$ this -> loadComponents ([
' Auth ' => [
' authenticate ' => [ ' User ' ],
' authorize ' => [ ' Controller ' ]
],
' Security '
]);
}在操作方法中,您可以调用模型以获取一些数据和/或渲染页面内部浏览夹文件夹
// NotesController
public function index (){
// render full page with layout(header and footer)
$ this -> view -> renderWithLayouts (Config:: get ( ' VIEWS_PATH ' ) . " layout/default/ " , Config:: get ( ' VIEWS_PATH ' ) . ' notes/index.php ' );
// render page without layout
$ this -> view -> render (Config:: get ( ' VIEWS_PATH ' ) . ' notes/note.php ' );
// get the rendered page
$ html = $ this -> view -> render (Config:: get ( ' VIEWS_PATH ' ) . ' notes/note.php ' );
// render a json view
$ this -> view -> renderJson ( array ( " data " => $ html ));
}在MVC中,该模型表示信息(数据)和业务规则;该视图包含用户界面的元素,例如文本,表单输入;控制器管理模型和视图之间的通信。来源
在模型类中实现了所有创建,删除,更新和验证的操作。
// NotesController
public function create (){
// get content of note submitted to a form
// then pass the content along with the current user to Note class
$ content = $ this -> request -> data ( " note_text " );
$ note = $ this -> note -> create (Session:: getUserId (), $ content );
if (! $ note ){
$ this -> view -> renderErrors ( $ this -> note -> errors ());
} else {
return $ this -> redirector -> root ( " Notes " );
}
}在注释模型中
// Notes Model
public function create ( $ userId , $ content ){
// using validation class(see below)
$ validation = new Validation ();
if (! $ validation -> validate ([ ' Content ' => [ $ content , " required|minLen(4)|maxLen(300) " ]])) {
$ this -> errors = $ validation -> errors ();
return false ;
}
// using database class to insert new note
$ database = Database:: openConnection ();
$ query = " INSERT INTO notes (user_id, content) VALUES (:user_id, :content) " ;
$ database -> prepare ( $ query );
$ database -> bindValue ( ' :user_id ' , $ userId );
$ database -> bindValue ( ' :content ' , $ content );
$ database -> execute ();
if ( $ database -> countRows () !== 1 ){
throw new Exception ( " Couldn't create note " );
}
return true ;
}使用框架,您可能会进行登录,注册和注销。这些操作是在App/Model/Login & App/Controllers/LoginController中实现的。在大多数情况下,您无需修改与登录操作相关的任何内容,只需了解框架的行为即可。
请注意,如果您没有SSL,则最好希望在客户端手动加密数据,如果是的话,请阅读此内容。
每当用户寄存器时,将与加密用户ID串联的令牌发送电子邮件。这个令牌将在24小时后过期。最好将这些代币到期,并在注册电子邮件过期时重复使用。
密码使用PHP v5.5中的最新算法进行哈希。
$ hashedPassword = password_hash ( $ password , PASSWORD_DEFAULT , array ( ' cost ' => Config:: get ( ' HASH_COST_FACTOR ' )));如果用户忘记了密码,他可以还原。同样的代币的想法也在这里。
此外,如果用户在一定持续时间内超过了遗忘的密码尝试(5)(> = 10分钟),则阻止某些持续时间(> = 10分钟)。
限制蛮力攻击是黑客尝试所有可能的输入组合,直到找到正确的密码为止。
解决方案:
验证码在防止自动登录方面特别有效。使用CAPTCHA一个很棒的PHP验证码库。
阻止IP地址是最后考虑的解决方案。如果相同的IP使用不同的凭据(> = 10)多次登录,则IP地址将被阻止。
PHP数据对象(PDO)用于准备和执行数据库查询。在Database类中,有多种方法可以隐藏复杂性,并让您实例化数据库对象,准备,绑定和执行几行。
SELECT, INSERT, UPDATE, DELETE足以容纳用户Admin类中提到。utf8mb4 。utf8 Charset仅存储由一到三个字节组成的UTF-8编码符号。但是,它不能用于具有四个字节的符号。utf8 。但是,如果您想升级到utf8mb4 ,请遵循以下链接:utf8mb4 Encryption类负责数据的加密和解密。加密应用于cookie,用户ID,发布ID,..ETC。加密的字符串经过身份验证,每次加密时都不同。
验证是一个用于验证用户输入的小库。所有验证规则都在Validation类中。
$ validation = new Validation ();
// there are default error messages for each rule
// but, you still can define your custom error message
$ validation -> addRuleMessage ( " emailUnique " , " The email you entered is already exists " );
if (! $ validation -> validate ([
" User Name " => [ $ name , " required|alphaNumWithSpaces|minLen(4)|maxLen(30) " ],
" Email " => [ $ email , " required|email|emailUnique|maxLen(50) " ],
' Password ' => [ $ password , " required|equals( " . $ confirmPassword . " )|minLen(6)|password " ],
' Password Confirmation ' => [ $ confirmPassword , ' required ' ]])) {
var_dump ( $ validation -> errors ());
}Handler类负责处理所有例外和错误。它将使用Logger来记录错误。默认情况下,错误报告将关闭,因为每个错误都会被记录并保存在app/logs/log.txt中。
如果遇到错误或异常,该应用程序将显示系统内部错误(500)。
您可以记录任何内容并将其保存到App/log/log.txt的地方。您可以编写任何失败,错误,异常或任何其他恶意动作或攻击。
Logger:: log ( " COOKIE " , self :: $ userId . " is trying to login using invalid cookie " , __FILE__ , __LINE__ );电子邮件是使用phpmailer通过SMTP发送的,SMTP是另一个用于发送电子邮件的库。您不应使用PHP的mail()函数。
在应用程序/配置中,有两个文件,一个用于主应用程序配置的config.php ,另一个用于JavaScript,称为JavaScript.php 。然后,JavaScript配置将被分配给footer.php中的JavaScript变量。
为了发送请求并收到响应,您可以取决于Ajax的电话。该框架在很大程度上取决于AJAX执行操作的请求,但是,您仍然可以使用小调整来为普通请求做同样的事情。
配置对象被分配给footer.php中的键值对。这些键值对可以使用Config::setJsConfig('key', "value"); ,然后将其分配给配置对象。
AJAX一个名称空间,该空间具有两个用于发送AJAX请求的主要功能。一个用于普通的AJAX调用,另一个用于上传文件。
助手一个具有多种功能的名称空间显示错误,序列化,重定向,encodehtml等
应用一个名称空间,用于使当前页面的整个JavaScript事件归化
事件一个用于声明所有可能发生的事件的名称空间,例如用户单击链接以创建,删除或更新时。
为了展示如何在现实生活中使用该框架,该框架随附用于管理用户配置文件管理,仪表板,新闻提要,上传和下载文件,帖子和评论,分页,管理面板,管理系统备份,Notificatons,Notificatons,Reports Bugs等功能。
步骤:
在应用程序/config/config.php中编辑配置文件和凭据
在_安装目录中执行SQL查询按顺序执行
登录
电子邮件设置
您需要在应用程序/config/config.php中配置SMTP帐户数据。但是,如果您没有SMTP帐户,则使用Logger将电子邮件保存在App/logs/log.txt中。
为此,在核心/电子邮件中,请注释$mail->Send()和uncomment Logger::log("EMAIL", $mail->Body);
每个用户都可以更改他的姓名,电子邮件,密码。另外上传配置文件图片(即最初分配给default.png)。
每当用户要求更改他的电子邮件时,都会将通知发送给用户的旧电子邮件以及新电子邮件。
发送给旧电子邮件的通知使用户有机会撤销电子邮件更改,而发送给新电子邮件的通知要求确认。用户仍然可以使用他的旧电子邮件登录,直到他确认更改为止。
这是在UserController中完成的,方法是在Methods updateProfileInfo() , revokeEmail()和updateEmail()中完成。在大多数情况下,您无需修改这些方法的行为。
您可以上传和下载文件。
file_uploads设置为trueupload_max_filesize, max_file_uploads, post_max_size将新闻提要视为Twitter中的推文,以及在Github打开问题时的帖子中。
它们在此框架的顶部实现。
管理员可以在普通用户无法执行的情况下执行操作。他们可以删除,编辑,创建任何新闻源,发布或评论。他们还可以控制所有用户配置文件,创建和还原备份。
只有管理员才能访问所有注册用户。他们可以删除,编辑他们的信息。
在大多数情况下,您需要为系统创建备份,并随时随地还原它们。
这是通过使用mySqlDump创建和还原备份来完成的。所有备份将存储在应用程序/备份中。
您是否在Facebook上看到了红色通知,或者在Twitter上看到了蓝色通知?同样的想法也在这里。但是,它是使用触发器实现的。触发器是在_安装/Triggers.sql中定义的。
因此,每当用户创建新的新闻源,发布或上传文件时,这都会为所有其他用户增加计数,并将在导航栏中显示红色通知。
用户可以报告错误,功能和增强功能。提交表格后,将发送电子邮件至app/config/config.php中定义的ADMIN_EMAIL
假设您想构建一个简单的待办器应用程序。在这里,我将逐步介绍如何使用和没有AJAX调用的框架创建一个TODO应用程序。
(1)如果您遵循上面的安装设置步骤,则创建初始用户帐户时应该没有任何问题。
(2)创建一个具有ID users表
CREATE TABLE ` todo ` (
` id ` int ( 11 ) NOT NULL AUTO_INCREMENT,
` user_id ` int ( 11 ) NOT NULL ,
` content ` varchar ( 512 ) NOT NULL ,
PRIMARY KEY ( ` id ` ),
FOREIGN KEY ( ` user_id ` ) REFERENCES ` users ` ( ` id ` ) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_general_ci;(3)创建todocontroller
创建一个名为TodoController.php的文件在应用程序/控制器内部
class TodoController extends Controller{
// override this method to perform any logic before calling action method as explained above
public function beforeAction (){
parent :: beforeAction ();
// define the actions in this Controller
$ action = $ this -> request -> param ( ' action ' );
// restrict the request to action methods
// $this->Security->requireAjax(['create', 'delete']);
$ this -> Security -> requirePost ([ ' create ' , ' delete ' ]);
// define the expected form fields for every action if exist
switch ( $ action ){
case " create " :
// you can exclude form fields if you don't care if they were sent with form fields or not
$ this -> Security -> config ( " form " , [ ' fields ' => [ ' content ' ]]);
break ;
case " delete " :
// If you want to disable validation for form tampering
// $this->Security->config("validateForm", false);
$ this -> Security -> config ( " form " , [ ' fields ' => [ ' todo_id ' ]]);
break ;
}
}
public function index (){
$ this -> view -> renderWithLayouts (Config:: get ( ' VIEWS_PATH ' ) . " layout/todo/ " , Config:: get ( ' VIEWS_PATH ' ) . ' todo/index.php ' );
}
public function create (){
$ content = $ this -> request -> data ( " content " );
$ todo = $ this -> todo -> create (Session:: getUserId (), $ content );
if (! $ todo ){
// in case of normal post request
Session:: set ( ' errors ' , $ this -> todo -> errors ());
return $ this -> redirector -> root ( " Todo " );
// in case of ajax
// $this->view->renderErrors($this->todo->errors());
} else {
// in case of normal post request
Session:: set ( ' success ' , " Todo has been created " );
return $ this -> redirector -> root ( " Todo " );
// in case of ajax
// $this->view->renderJson(array("success" => "Todo has been created"));
}
}
public function delete (){
$ todoId = Encryption:: decryptIdWithDash ( $ this -> request -> data ( " todo_id " ));
$ this -> todo -> delete ( $ todoId );
// in case of normal post request
Session:: set ( ' success ' , " Todo has been deleted " );
return $ this -> redirector -> root ( " Todo " );
// in case of ajax
// $this->view->renderJson(array("success" => "Todo has been deleted"));
}
public function isAuthorized (){
$ action = $ this -> request -> param ( ' action ' );
$ role = Session:: getUserRole ();
$ resource = " todo " ;
// only for admins
Permission:: allow ( ' admin ' , $ resource , [ ' * ' ]);
// only for normal users
Permission:: allow ( ' user ' , $ resource , [ ' delete ' ], ' owner ' );
$ todoId = $ this -> request -> data ( " todo_id " );
if (! empty ( $ todoId )){
$ todoId = Encryption:: decryptIdWithDash ( $ todoId );
}
$ config = [
" user_id " => Session:: getUserId (),
" table " => " todo " ,
" id " => $ todoId ];
return Permission:: check ( $ role , $ resource , $ action , $ config );
}
} (4)创建Note Model类,称为Todo.php在应用程序/模型中
class Todo extends Model{
public function getAll (){
$ database = Database:: openConnection ();
$ query = " SELECT todo.id AS id, users.id AS user_id, users.name AS user_name, todo.content " ;
$ query .= " FROM users, todo " ;
$ query .= " WHERE users.id = todo.user_id " ;
$ database -> prepare ( $ query );
$ database -> execute ();
$ todo = $ database -> fetchAllAssociative ();
return $ todo ;
}
public function create ( $ userId , $ content ){
// using validation class
$ validation = new Validation ();
if (! $ validation -> validate ([ ' Content ' => [ $ content , " required|minLen(4)|maxLen(300) " ]])) {
$ this -> errors = $ validation -> errors ();
return false ;
}
// using database class to insert new todo
$ database = Database:: openConnection ();
$ query = " INSERT INTO todo (user_id, content) VALUES (:user_id, :content) " ;
$ database -> prepare ( $ query );
$ database -> bindValue ( ' :user_id ' , $ userId );
$ database -> bindValue ( ' :content ' , $ content );
$ database -> execute ();
if ( $ database -> countRows () !== 1 ){
throw new Exception ( " Couldn't create todo " );
}
return true ;
}
public function delete ( $ id ){
$ database = Database:: openConnection ();
$ database -> deleteById ( " todo " , $ id );
if ( $ database -> countRows () !== 1 ){
throw new Exception ( " Couldn't delete todo " );
}
}
}(5)内部视图/
(a)创建header.php & footer.php内部视图/布局/todo
<! DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf- 8 ">
<meta http-equiv="X- UA -Compatible" content=" IE =edge">
<meta name="viewport" content="width=device-width, initial-scale= 1 ">
<meta name="description" content="mini PHP ">
<meta name="author" content="mini PHP ">
<title>mini PHP </title>
<!-- Stylesheets -->
<link rel="stylesheet" href=" <?= PUBLIC_ROOT ; ?> css/bootstrap.min.css">
<link rel="stylesheet" href=" <?= PUBLIC_ROOT ; ?> css/sb-admin-2.css">
<link rel="stylesheet" href=" <?= PUBLIC_ROOT ; ?> css/font-awesome.min.css" rel="stylesheet" type="text/css">
<!-- Styles for ToDo Application -->
<style>
.todo_container{
width:80%;
margin: 0 auto;
margin-top: 5%
}
#todo-list li{
list-style-type: none;
border: 1px solid #e7e7e7;
padding: 3px;
margin: 3px;
}
#todo-list li:hover{
background-color: #eee;
}
form button{
float:right;
margin: 3px;
}
form:after{
content: '';
display: block;
clear: both;
}
</style>
</head>
<body> <!-- footer -->
<script src="https: //ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!--<script src=" <?= PUBLIC_ROOT ; ?> js/jquery.min.js"></script>-->
<script src=" <?= PUBLIC_ROOT ; ?> js/bootstrap.min.js"></script>
<script src=" <?= PUBLIC_ROOT ; ?> js/sb-admin-2.js"></script>
<script src=" <?= PUBLIC_ROOT ; ?> js/main.js"></script>
<!-- Assign CSRF Token to JS variable -->
<?php Config:: setJsConfig ( ' csrfToken ' , Session:: generateCsrfToken ()); ?>
<!-- Assign all configration variables -->
<script>config = <?= json_encode (Config:: getJsConfig ()); ?> ;</script>
<!-- Run the application -->
<script>$(document).ready(app.init());</script>
<?php Database:: closeConnection (); ?>
</body>
</html> (b)内部视图/创建将具有index.php文件夹,其中将包含我们的待办事项列表。
<div class="todo_container">
<h2> TODO Application</h2>
<!-- in case of normal post request -->
<form action= " <?= PUBLIC_ROOT . " Todo/create " ?> " method="post">
<label>Content <span class="text-danger " >*</span></label>
<textarea name= " content" class ="form-control " required placeholder= " What are you thinking? " ></textarea>
<input type='hidden' name = "csrf_token" value = " <?= Session:: generateCsrfToken (); ?> ">
<button type="submit" name="submit" value="submit" class="btn btn-success">Create</button>
</form>
<!-- in case of ajax request
<form action= "#" id="form-create-todo" method="post">
<label>Content <span class="text-danger">*</span></label>
<textarea name="content" class="form-control" required placeholder="What are you thinking?"></textarea>
<button type="submit" name="submit" value="submit" class="btn btn-success">Create</button>
</form>
-->
<br>
<?php
// display success or error messages in session
if (! empty (Session:: get ( ' success ' ))){
echo $ this -> renderSuccess (Session:: getAndDestroy ( ' success ' ));
} else if (! empty (Session:: get ( ' errors ' ))){
echo $ this -> renderErrors (Session:: getAndDestroy ( ' errors ' ));
}
?>
<br><hr><br>
<ul id="todo-list">
<?php
$ todoData = $ this -> controller -> todo -> getAll ();
foreach ( $ todoData as $ todo ){
?>
<li>
<p> <?= $ this -> autoLinks ( $ this -> encodeHTMLWithBR ( $ todo [ " content " ])); ?> </p>
<!-- in case of normal post request -->
<form action= " <?= PUBLIC_ROOT . " Todo/delete " ?> " method="post">
<input type='hidden' name= "todo_id" value=" <?= " todo- " . Encryption:: encryptId ( $ todo [ " id " ]); ?> ">
<input type='hidden' name = "csrf_token" value = " <?= Session:: generateCsrfToken (); ?> ">
<button type="submit" name="submit" value="submit" class="btn btn-xs btn-danger">Delete</button>
</form>
<!-- in case of ajax request
<form class="form-delete-todo" action= "#" method="post">
<input type='hidden' name= "todo_id" value=" <?= " todo- " . Encryption:: encryptId ( $ todo [ " id " ]); ?> ">
<button type="submit" name="submit" value="submit" class="btn btn-xs btn-danger">Delete</button>
</form>
-->
</li>
<?php } ?>
</ul>
</div>(6)JavaScript代码发送AJAX呼叫,并处理响应
// first, we need to initialize the todo events whenever the application initalized
// the app.init() is called in footer.php, see views/layout/todo/footer.php
var app = {
init : function ( ) {
events . todo . init ( ) ;
}
} ;
// inside var events = {....} make a new key called "todo"
var events = {
// ....
todo : {
init : function ( ) {
events . todo . create ( ) ;
events . todo . delete ( ) ;
} ,
create : function ( ) {
$ ( "#form-create-todo" ) . submit ( function ( e ) {
e . preventDefault ( ) ;
ajax . send ( "Todo/create" , helpers . serialize ( this ) , createTodoCallBack , "#form-create-todo" ) ;
} ) ;
function createTodoCallBack ( PHPData ) {
if ( helpers . validateData ( PHPData , "#form-create-todo" , "after" , "default" , "success" ) ) {
alert ( PHPData . success + " refresh the page to see the results" ) ;
}
}
} ,
delete : function ( ) {
$ ( "#todo-list form.form-delete-todo" ) . submit ( function ( e ) {
e . preventDefault ( ) ;
if ( ! confirm ( "Are you sure?" ) ) { return ; }
var cur_todo = $ ( this ) . parent ( ) ;
ajax . send ( "Todo/delete" , helpers . serialize ( this ) , deleteTodoCallBack , cur_todo ) ;
function deleteTodoCallBack ( PHPData ) {
if ( helpers . validateData ( PHPData , cur_todo , "after" , "default" , "success" ) ) {
$ ( cur_todo ) . remove ( ) ;
alert ( PHPData . success ) ;
}
}
} ) ;
}
}
}我在学习期间的空闲时间里写了这个脚本。这是免费的,无薪的。我之所以这样说,是因为我看到许多开发人员对任何软件都很粗鲁,而且他们的行为确实令人沮丧。我不知道为什么?!每个人都倾向于抱怨,并说苛刻的话。我确实接受反馈,但是,以一种良好而尊重的方式。
在线购买许多其他脚本,可以做同样的事情(如果不是更少),他们的作者从中赚了很多钱,但是,我选择将其公开,适合所有人。
如果您学到了一些东西,或者我节省了您的时间,请通过传播这个词来支持该项目。
通过创建新问题,在github上发送拉动请求来做出贡献,或者您可以发送电子邮件至:[email protected]
根据MIT许可建造。