Небольшой, простой скелет PHP MVC Framework, который инкапсулирует множество функций, окруженных мощными уровнями безопасности.
MiniPHP - это очень простое приложение, полезное для небольших проектов, помогает понять скелет PHP MVC, знать, как аутентифицировать и авторизовать, шифровать данные и применять концепции безопасности, дезинфицировать и валидацию, делать вызовы AJAX и многое другое.
Это не полная рамка, ни очень простой, но это не сложно. Вы можете легко установить, понимать и использовать его в любом из ваших проектов.
Это отступает, чтобы удалить сложность рамок. Такие вещи, как маршрутизация, аутентификация, авторизация, управление пользовательским сеансом и файлы cookie, и т. Д. - это не то, что я изобрел с царапины, однако они представляют собой агрегацию концепций, уже реализованных в других структурах, но, встроенные гораздо более простым способом, так что вы можете понять это и взять его на дальнейшие.
Если вам нужно построить более широкое применение и воспользоваться преимуществами большинства функций, доступных в рамках, вы можете увидеть CakePhp, Laravel, Symphony.
В любом случае, важно понимать скелет PHP MVC и знать, как аутентифицировать и авторизовать, узнать о проблемах безопасности и как вы можете победить и как построить собственное приложение, используя структуру.
Полная документация также может быть найдена здесь, созданная GitHub Automatic Generator.
Живая демонстрация доступна здесь. Живая демонстрация предназначена для демонстрационного приложения, созданного на вершине этой структуры в этом разделе. Спасибо @Everterstraat.
Некоторые функции не работают в демонстрации.
Установите через композитор
composer install
Всякий раз, когда вы делаете запрос на приложение, он будет направлен на index.php внутри публичной папки. Итак, если вы делаете запрос: http://localhost/miniPHP/User/update/412 . Это будет разделено и переведено на
На самом деле, HTACCESS разбивает все, что происходит после http://localhost/miniPHP и добавляет его в URL в качестве аргумента QueryString. Таким образом, этот запрос будет преобразован в: http://localhost/miniPHP?url='User/update/412' .
Затем класс App Inside splitUrl() разделяет строку запроса $_GET['url'] на контроллер, метод действия и любые принятые аргументы к методу действия.
В классе App , Inside run() , он создаст экземпляр объекта из класса контроллера и сделает метод призыв к действию, передавая любые аргументы, если они существуют.
После объекта контроллера класса App он вызовет $this->controller->startupProcess() , который, в свою очередь, запустит 3 последовательных события/методы:
initialize() : используйте его для загрузки компонентовbeforeAction() : выполните любые логические действия перед вызовом метода действия контроллераtriggerComponents() : Trigger Startup () Метод загруженных компонентов Конструктор класса 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 заботится о различных задачах безопасности и проверке.
Важно ограничить методы запроса. В качестве примера, если у вас есть метод действия, который принимает значения формы, поэтому будет принят только запрос POST. Та же самая идея для Ajax, Get, ..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 );
}Он проверяет и проверяет, если запрос поступает из того же домена. Хотя они могут быть фальсифицированы, хорошо сохранить их как часть наших слоев безопасности.
Проверьте отправленную форму, поступающую из запроса POST. Подводя этого метода - вам необходимо определить ожидаемую поля формы или данные, которые будут отправлены с помощью запроса POST.
По умолчанию фреймворк будет проверять для подделки формы при выполнении запроса на почту, и это убедится, что токен 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, назначьте validateCsrfToken true , как показано в примере ниже. Валидация 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 в общедоступной корневой папке. Иногда вам нужно иметь контроль над этими компонентами, например, когда вы хотите иметь контроллер без аутентификации или авторизации, или включен компонент безопасности. Это можно сделать с помощью метода переопределения initialize() внутри вашего класса контроллера и загружать только необходимые компоненты.
Пример 1 : не загружайте какую -либо компонент, нет аутентификации или авторизации или проверки безопасности.
public function initialize (){
$ this -> loadComponents ([]);
}Пример 2 : Загрузите безопасность и компонент AUTH, но не аутентифицируйте и авторизуйте, на случай, если вы хотите использовать компонент AUTH внутри методов действия. LoginController-это пример того, как получить доступ к странице без требуемого пользователя .
public function initialize (){
$ this -> loadComponents ([
' Auth ' ,
' Security '
]);
}Пример 3 : Загрузите безопасность и компонент AUTH, а также аутентифицируйте пользователя и авторизуйте текущий контроллер. Это поведение по умолчанию в классе Core/Controller
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/Models/Login & App/Controllers/LoginController . В большинстве ситуаций вам не нужно будет изменять что -либо, связанное с действиями входа в систему, просто понять поведение структуры.
Обратите внимание, что если у вас нет SSL, вам лучше захотеть шифровать данные вручную на стороне клиента, если это так, прочитайте это, а также это.
Всякий раз, когда пользователь регистрируется, электронное письмо будет отправлено с токеном, объединенным с зашифрованным идентификатором пользователя. Этот токен будет истек через 24 часа. Гораздо лучше истекать эти жетоны и повторно использовать зарегистрированное электронное письмо, если они истек.
Пароли хэшируются с использованием последних алгоритмов в PHP v5.5
$ hashedPassword = password_hash ( $ password , PASSWORD_DEFAULT , array ( ' cost ' => Config:: get ( ' HASH_COST_FACTOR ' )));Если пользователь забыл свой пароль, он может его восстановить. Та же самая идея токенов с истекшим сроком ходит сюда.
Кроме того, блокируйте пользователя для определенной продолжительности (> = 10 минут), если он превысил количество попыток забытых паролей (5) в течение определенной продолжительности (> = 10 минут).
Атаки грубого усилия с дросселем-это когда хакер пробует всю возможную комбинацию ввода, пока не найдет правильный пароль.
Решение:
Капчи особенно эффективны в предотвращении автоматических логин. Использование Captcha потрясающая библиотека PHP Captcha.
Блокирование IP -адресов - последнее решение, о котором можно подумать. IP -адрес будет заблокирован, если один и тот же IP не смог входить в систему несколько раз, используя различные учетные данные (> = 10).
Объекты данных PHP (PDO) используются для подготовки и выполнения запросов базы данных. Внутри класса Database существуют различные методы, которые скрывают сложность и позволяют вам создавать создание объекта базы данных, подготовки, связывания и выполнения в нескольких строках.
SELECT, INSERT, UPDATE, DELETE достаточно для пользователейAdmin .utf8mb4 на уровне базы данных.utf8 Charset хранит только кодированные символы UTF-8, которые состоят из одного до трех байтов. Но это не может для символов с четырьмя байтами.utf8 . Но если вы хотите обновить до utf8mb4 , перейдите по этим ссылкам:utf8mb4 Класс Encryption отвечает за шифрование и дешифрование данных. Шифрование применяется к таким вещам, как файлы cookie, идентификатор пользователя, идентификатор post, ..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 отвечает за обработку всех исключений и ошибок. Он будет использовать журнал для регистрации ошибок. Отчетность об ошибках отключается по умолчанию, потому что каждая ошибка будет регистрирована и сохраняется в 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, другую библиотеку для отправки электронных писем. Вы не должны использовать функцию mail() PHP.
В приложении/конфигурации есть два файла, один из которых называется config.php для основных конфигураций приложения, и другой для JavaScript с именем javascript.php . Конфигурации JavaScript будут затем назначены переменной JavaScript в вашем файле cool.php .
Чтобы отправить запрос и получить ответ, вы можете зависеть от вызовов Ajax для этого. Эта структура в значительной степени зависит от запросов AJAX для выполнения действий, но вы все равно можете сделать то же самое для обычных запросов с небольшими изменениями.
Объект конфигурации назначен парам ключевых значений в cool.php. Эти пары клавиш можно добавить в код на стороне сервера с помощью Config::setJsConfig('key', "value"); , который будет назначен тогда для объекта конфигурации .
Ajax A -пространство имен, которое имеет две основные функции для отправки запроса AJAX. Один для обычных вызовов Ajax, а другой для загрузки файлов.
Помогатели пространства имен, в котором есть разнообразные функции, отображают ошибки, сериализовать, перенаправить, encodehtml и т. Д.
Приложение пространство имен, которое используется для инаилизации всех событий JavaScript для текущей страницы
События Пространство имен, которое используется для объявления всех событий, которые могут произойти, например, когда пользователь нажимает на ссылку, чтобы создать, удалить или обновить.
Чтобы показать, как использовать структуру в реальной ситуации, фреймворк поставляется с реализацией для таких функций, как управление управлением профилем пользователя, панель инструментов, новости, загрузку и загрузку файлов, публикации и комментариев, лиц, панель администратора, управление резервными копиями системы, уведомлениями, ошибки отчетов, ... и т. Д.
Шаги:
Редактировать файл конфигурации в app/config/config.php с вашими учетными данными
Выполнить запросы SQL в каталоге _ установки в порядке
Авторизоваться
Настройка электронной почты
Вам необходимо настроить данные вашей учетной записи SMTP в App/config/config.php . Но , если у вас нет учетной записи SMTP, то вы сохраняете электронные письма в App/logs/log.txt с помощью logger.
Для этого, в ядре/электронной почте, комментарий $mail->Send() и endmommorge Logger::log("EMAIL", $mail->Body);
Каждый пользователь может изменить свое имя, электронную почту, пароль. Также загрузите изображение профиля (т.е. изначально назначено на default.png).
Всякий раз, когда пользователь просит изменить свою электронную почту, уведомление будет отправлено на старую электронную почту пользователя и новое.
Уведомление, отправленное на старую электронную почту, дает пользователю возможность отменить изменение электронной почты, в то время как уведомление, отправленное на новую электронную почту, запрашивает подтверждение. Пользователь все еще может войти в систему со своей старой электронной почтой, пока не подтвердит изменение.
Это делается в UserController , в методах updateProfileInfo() , revokeEmail() и updateEmail() . В большинстве ситуаций вам не нужно будет изменять поведение этих методов.
Вы можете загружать и загружать файлы.
file_uploads в trueupload_max_filesize, max_file_uploads, post_max_sizeПодумайте о новостях как твиты в Твиттере, а также в таких постах, как когда вы открываете проблему в GitHub.
Они реализованы на вершине этой структуры.
Администраторы могут выполнять действия, где нормальные пользователи не могут. Они могут удалить, редактировать, создавать любую новостную ленту, публикацию или комментарий. Также они имеют контроль над всеми профилями пользователей, создают и восстанавливают резервные копии.
Только администраторы имеют доступ, чтобы увидеть всех зарегистрированных пользователей. Они могут удалить, отредактировать свою информацию.
В большинстве ситуаций вам нужно будет создать резервные копии для системы и восстановить их, когда захотите.
Это делается с помощью MySQldump для создания и восстановления резервных копий. Все резервные копии будут храниться в приложениях/резервном копировании .
Вы видели красные уведомления на Facebook или синие в Твиттере? Та же идея здесь. Но вместо этого он реализован с использованием триггеров. Триггеры определены в _ установке/triggers.sql .
Таким образом, всякий раз, когда пользователь создает новую новостную ленту, публиковать или загружать файл, это увеличит количество других пользователей и отобразит красное уведомление в панели навигации.
Пользователи могут сообщать об ошибках, функциях и улучшениях. После того, как они отправили форму, электронное письмо будет отправлено в ADMIN_EMAIL , определенное в APP/config/config.php
Допустим, вы хотите построить простое приложение Todo. Здесь я пойду шаг за шагом, как создать приложение TODO, используя Framework с и без вызовов AJAX.
(1) Если вы выполняете шаги установки установки выше, у вас не должно быть никаких проблем с созданием начальных учетных записей пользователей.
(2) Создайте таблицу с идентификатором как int, контент varchar, user_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) Создать класс модели примечания, вызванный 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) Внутри видов/
(а) Создать 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> (б) Внутри видов/ Создайте папку TODO, которая будет иметь index.php , который будет содержать наш список TODO.
<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.