Kerangka kerangka kerja PHP MVC yang kecil dan sederhana yang merangkum banyak fitur yang dikelilingi dengan lapisan keamanan yang kuat.
Miniphp adalah aplikasi yang sangat sederhana, berguna untuk proyek -proyek kecil, membantu memahami kerangka PHP MVC, tahu cara mengotentikasi dan mengotorisasi, mengenkripsi data dan menerapkan konsep keamanan, sanitasi dan validasi, melakukan panggilan AJAX dan banyak lagi.
Ini bukan kerangka kerja penuh, atau yang sangat mendasar tetapi tidak rumit. Anda dapat dengan mudah menginstal, memahami, dan menggunakannya di salah satu proyek Anda.
Ini indentasi untuk menghapus kompleksitas kerangka kerja. Hal -hal seperti perutean, otentikasi, otorisasi, mengelola sesi pengguna dan cookie, dan sebagainya bukanlah sesuatu yang saya ciptakan dari awal, namun, mereka adalah agregasi konsep yang sudah diimplementasikan dalam kerangka kerja lain, tetapi, dibangun dengan cara yang jauh lebih sederhana, jadi, Anda dapat memahaminya, dan mengambilnya lebih jauh.
Jika Anda perlu membangun aplikasi yang lebih besar, dan mengambil keuntungan dari sebagian besar fitur yang tersedia dalam kerangka kerja, Anda dapat melihat CakePhp, Laravel, Symphony.
Either way, penting untuk memahami kerangka PHP MVC, dan tahu cara mengautentikasi dan mengotorisasi, belajar tentang masalah keamanan dan bagaimana Anda bisa mengalahkan, dan bagaimana membangun aplikasi Anda sendiri menggunakan kerangka kerja.
Dokumentasi lengkap juga dapat ditemukan di sini - dibuat oleh Ghithub Page Generator Page.
Demo langsung tersedia di sini. Demo langsung adalah untuk aplikasi demo yang dibangun di atas kerangka ini di bagian ini. Terima kasih untuk @everterstraat.
Beberapa fitur tidak berfungsi dalam demo.
Instal melalui komposer
composer install
Setiap kali Anda mengajukan permintaan ke aplikasi, itu akan diarahkan ke index.php di dalam folder publik. Jadi, jika Anda membuat permintaan: http://localhost/miniPHP/User/update/412 . Ini akan dipisahkan dan diterjemahkan ke dalam
Faktanya, HTACCESS membagi semuanya datang setelah http://localhost/miniPHP dan menambahkannya ke URL sebagai argumen querystring. Jadi, permintaan ini akan dikonversi ke: http://localhost/miniPHP?url='User/update/412' .
Kemudian kelas App , di dalam splitUrl() , akan membagi string kueri $_GET['url'] menjadi controller, metode tindakan, dan setiap argumen yang diloloskan ke metode tindakan.
Di kelas App , di dalam run() , itu akan membuat instantiasi objek dari kelas pengontrol, dan membuat metode ajakan untuk bertindak, memberikan argumen apa pun jika ada.
Setelah kelas App menginterialisasi objek controller, ia akan menghubungi metode $this->controller->startupProcess() , yang pada gilirannya akan memicu 3 peristiwa/metode berturut-turut:
initialize() : Gunakan untuk memuat komponenbeforeAction() : Lakukan tindakan logika apa pun sebelum memanggil metode tindakan pengontroltriggerComponents() : pemicu startup () Metode komponen yang dimuat Kelas konstruktor Controller tidak boleh diganti, sebaliknya Anda dapat mengganti metode initialize() & beforeAction() dalam kelas yang diperluas.
Setelah proses startup dari konstruksi menyelesaikan tugasnya, maka, metode tindakan yang diminta akan dipanggil, dan argumen akan dilewati (jika ada).
Komponen adalah perangkat tengah. Mereka memberikan logika yang dapat digunakan kembali untuk digunakan sebagai bagian dari pengontrol. Otentikasi, otorisasi, perusakan bentuk, dan memvalidasi token CSRF diimplementasikan di dalam komponen.
Lebih baik menarik potongan -potongan logika ini dari kelas pengontrol, dan menyimpan semua tugas dan validasi di dalam komponen -komponen ini.
Setiap komponen mewarisi dari kelas dasar/super yang disebut Component . Masing -masing memiliki tugas yang ditentukan. Ada dua komponen, satu untuk dipanggil auth untuk otentikasi dan otorisasi, dan yang lainnya disebut keamanan untuk masalah keamanan lainnya.
Mereka sangat sederhana untuk ditangani, dan mereka akan dipanggil dalam konstruktor pengontrol.
Apakah pengguna memiliki kredensial yang tepat?
Authcomponent menangani sesi pengguna.
Apakah Anda memiliki hak untuk mengakses atau melakukan aksi x? Komponen auth menangani otorisasi untuk setiap pengontrol. Dengan demikian, setiap pengontrol harus menerapkan metode isAuthorized() . Yang perlu Anda lakukan adalah mengembalikan nilai boolean .
Jadi, misalnya, untuk memeriksa apakah pengguna saat ini adalah admin atau tidak, Anda akan melakukan sesuatu seperti ini:
// AdminController
public function isAuthorized (){
$ role = Session:: getUserRole ();
if ( isset ( $ role ) && $ role === " admin " ){
return true ;
}
return false ;
} Jika Anda ingin mengambilnya lebih lanjut dan menerapkan beberapa aturan izin, ada kelas yang kuat yang disebut Permission yang bertanggung jawab untuk menentukan aturan izin. Kelas ini memungkinkan Anda untuk mendefinisikan "siapa yang diizinkan untuk melakukan metode tindakan spesifik pada pengontrol saat ini".
Jadi, misalnya, untuk memungkinkan admin melakukan tindakan apa pun pada catatan, sementara pengguna normal hanya dapat mengedit catatan mereka:
// 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 );
}Sekarang, Anda dapat memeriksa otorisasi berdasarkan peran pengguna, sumber daya, dan untuk setiap metode tindakan.
Komponen keamanan menangani berbagai tugas keamanan dan validasi.
Penting untuk membatasi metode permintaan. Sebagai contoh, jika Anda memiliki metode tindakan yang menerima nilai formulir, jadi, hanya permintaan pasca yang akan diterima. Ide yang sama untuk Ajax, dapatkan, ..etc. Anda dapat melakukan ini di dalam metode beforeAction() .
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ actions = [ ' create ' , ' delete ' ];
$ this -> Security -> requireAjax ( $ actions );
$ this -> Security -> requirePost ( $ actions );
}Juga jika Anda memerlukan semua permintaan untuk melalui koneksi yang diamankan, Anda dapat mengonfigurasi seluruh pengontrol, atau tindakan spesifik untuk mengarahkan semua permintaan ke HTTPS alih -alih HTTP.
// NotesController
public function beforeAction (){
parent :: beforeAction ();
$ actions = [ ' create ' , ' delete ' ]; // specific action methods
$ actions = [ ' * ' ]; // all action methods
$ this -> Security -> requireSecure ( $ actions );
}Itu memeriksa & memvalidasi jika permintaan berasal dari domain yang sama. Meskipun mereka bisa dipalsukan, ada baiknya menjaga mereka sebagai bagian dari lapisan keamanan kita.
Validasi formulir yang dikirimkan berasal dari permintaan pos. Jebakan dari metode ini adalah Anda perlu menentukan bidang formulir yang diharapkan, atau data yang akan dikirim dengan permintaan pos.
Secara default, kerangka kerja akan memvalidasi untuk merusak formulir ketika permintaan pos dibuat, dan itu akan memastikan token CSRF dilewatkan dengan bidang formulir. Dalam situasi ini, jika Anda tidak melewati token CSRF, itu akan dianggap sebagai utas keamanan.
// 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 ;
}
}Token CSRF penting untuk memvalidasi formulir yang dikirimkan, dan untuk memastikan mereka tidak dipalsukan. Seorang peretas dapat menipu pengguna untuk membuat permintaan ke situs web, atau mengklik tautan, dan sebagainya.
Mereka berlaku untuk durasi tertentu (> = 1 hari), maka itu akan diregenerasi dan disimpan dalam sesi pengguna.
Validasi CSRF dinonaktifkan secara default. Jika Anda ingin memvalidasi token CSRF, maka tetapkan validateCsrfToken ke true seperti yang ditunjukkan pada contoh di bawah ini. Validasi CSRF akan dipaksakan saat permintaan adalah pos dan perusakan formulir diaktifkan.
Sekarang, Anda tidak perlu memverifikasi token CSRF secara manual pada setiap permintaan. Komponen keamanan akan memverifikasi token dalam permintaan versus token yang disimpan dalam sesi.
// 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 ;
}
}Token CSRF dihasilkan per sesi. Anda dapat menambahkan bidang formulir tersembunyi, atau di URL sebagai parameter kueri.
Membentuk
<input type="hidden" name="csrf_token" value="<?= Session::generateCsrfToken(); ?>" />
Url
<a href="<?= PUBLIC_ROOT . "?csrf_token=" . urlencode(Session::generateCsrfToken()); ?>">Link</a>
Javascript
Anda juga dapat menetapkan token CSRF ke variabel JavaScript.
<script>config = <?= json_encode(Session::generateCsrfToken()); ?>;</script>
index.php di folder root publik. Kadang -kadang Anda perlu memiliki kontrol pada komponen -komponen ini, seperti saat ingin memiliki pengontrol tanpa otentikasi atau otorisasi, atau komponen keamanan diaktifkan. Ini dapat dilakukan dengan mengesampingkan metode initialize() di dalam kelas pengontrol Anda, dan memuat hanya komponen yang diperlukan.
Contoh 1 : Jangan memuat komponen apa pun, tidak ada otentikasi atau otorisasi, atau validasi keamanan.
public function initialize (){
$ this -> loadComponents ([]);
}Contoh 2 : Muat keamanan, & komponen auth, tetapi jangan mengotentikasi dan mengotorisasi, untuk berjaga -jaga jika Anda ingin menggunakan komponen auth di dalam metode tindakan. Logincontroller adalah contoh tentang cara mengakses halaman tanpa memerlukan pengguna yang masuk .
public function initialize (){
$ this -> loadComponents ([
' Auth ' ,
' Security '
]);
}Contoh 3 : Muat Keamanan, & Komponen AUTH, dan otentikasi pengguna & otorisasi untuk pengontrol saat ini. Ini adalah perilaku default di kelas inti/pengontrol
public function initialize (){
$ this -> loadComponents ([
' Auth ' => [
' authenticate ' => [ ' User ' ],
' authorize ' => [ ' Controller ' ]
],
' Security '
]);
}Di dalam metode tindakan Anda dapat membuat panggilan untuk memodelkan untuk mendapatkan beberapa data, dan/atau membuat halaman di dalam folder tampilan
// 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 ));
}Dalam MVC, model mewakili informasi (data) dan aturan bisnis; Tampilan berisi elemen antarmuka pengguna seperti teks, membentuk input; dan pengontrol mengelola komunikasi antara model dan tampilan. Sumber
Semua operasi seperti membuat, menghapus, memperbarui, dan validasi diimplementasikan di kelas model.
// 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 " );
}
}Dalam Model Catatan
// 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 ;
}Menggunakan kerangka kerja, Anda mungkin akan melakukan login, mendaftar, dan logout. Tindakan ini diimplementasikan dalam aplikasi/model/login & aplikasi/pengontrol/logincontroller . Dalam sebagian besar situasi, Anda tidak perlu memodifikasi apa pun yang terkait dengan tindakan login, cukup pahami perilaku kerangka kerja.
Catatan Jika Anda tidak memiliki SSL, Anda sebaiknya mengenkripsi data secara manual di sisi klien, jika demikian, baca ini dan juga ini.
Setiap kali pengguna mendaftar, email akan dikirim dengan token yang digabungkan dengan ID pengguna terenkripsi. Token ini akan kedaluwarsa setelah 24 jam. Jauh lebih baik untuk kedaluwarsa token ini, dan menggunakan kembali email yang terdaftar jika mereka kedaluwarsa.
Kata sandi hash menggunakan algoritma terbaru dalam php v5.5
$ hashedPassword = password_hash ( $ password , PASSWORD_DEFAULT , array ( ' cost ' => Config:: get ( ' HASH_COST_FACTOR ' )));Jika pengguna lupa kata sandinya, ia dapat mengembalikannya. Gagasan yang sama tentang token yang sudah kadaluwarsa pergi ke sini.
Selain itu, blokir pengguna untuk durasi tertentu (> = 10 menit) jika ia melebihi jumlah upaya kata sandi yang terlupakan (5) selama durasi tertentu (> = 10 menit).
Throttling serangan brute-force adalah ketika seorang peretas mencoba semua kemungkinan kombinasi input sampai ia menemukan kata sandi yang benar.
Larutan:
Captcha sangat efektif dalam mencegah login otomatis. Menggunakan captcha perpustakaan php captcha yang luar biasa.
Memblokir alamat IP adalah solusi terakhir untuk dipikirkan. Alamat IP akan diblokir jika IP yang sama gagal masuk beberapa kali menggunakan kredensial yang berbeda (> = 10).
Objek Data PHP (PDO) digunakan untuk menyiapkan dan melaksanakan kueri basis data. Di dalam kelas Database , ada berbagai metode yang menyembunyikan kompleksitas dan memungkinkan Anda membuat objek database, mempersiapkan, mengikat, dan mengeksekusi dalam beberapa baris.
SELECT, INSERT, UPDATE, DELETE sudah cukup untuk penggunaAdmin .utf8mb4 pada tingkat database.utf8 Charset Only Store UTF-8 Simbol yang dikodekan yang terdiri dari satu hingga tiga byte. Tapi, itu tidak bisa untuk simbol dengan empat byte.utf8 . Tetapi, jika Anda ingin meningkatkan ke utf8mb4 , ikuti tautan ini:utf8mb4 Kelas Encryption bertanggung jawab untuk mengenkripsi dan dekripsi data. Enkripsi diterapkan pada hal -hal seperti cookie, ID pengguna, ID pos, ..etc. String terenkripsi diautentikasi dan berbeda setiap kali Anda mengenkripsi.
Validasi adalah pustaka kecil untuk memvalidasi input pengguna. Semua aturan validasi ada di dalam kelas 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 ());
} Kelas Handler bertanggung jawab untuk menangani semua pengecualian dan kesalahan. Ini akan menggunakan Logger untuk mencatat kesalahan. Pelaporan kesalahan dimatikan secara default, karena setiap kesalahan akan dicatat dan disimpan dalam aplikasi/log/log.txt .
Jika kesalahan yang ditemui atau pengecualian dilemparkan, aplikasi akan menunjukkan kesalahan sistem internal (500).
Tempat di mana Anda dapat mencatat apa pun dan menyimpannya ke aplikasi/log/log.txt . Anda dapat menulis kegagalan, kesalahan, pengecualian, atau tindakan atau serangan jahat lainnya.
Logger:: log ( " COOKIE " , self :: $ userId . " is trying to login using invalid cookie " , __FILE__ , __LINE__ ); Email dikirim menggunakan phpmailer melalui SMTP, perpustakaan lain untuk mengirim email. Anda tidak boleh menggunakan fungsi mail() dari PHP.
Di App/Config , ada dua file, satu yang disebut config.php untuk konfigurasi aplikasi utama, dan satu lagi untuk JavaScript yang disebut javascript.php . Konfigurasi JavaScript akan ditugaskan ke variabel JavaScript di footer.php Anda.
Untuk mengirim permintaan dan menerima tanggapan, Anda mungkin bergantung pada panggilan AJAX untuk melakukannya. Kerangka kerja ini sangat tergantung pada permintaan AJAX untuk melakukan tindakan, tetapi, Anda masih dapat melakukan hal yang sama untuk permintaan normal hanya dengan tweak kecil.
Objek konfigurasi ditetapkan untuk pasangan nilai kunci di footer.php. Pasangan nilai kunci ini dapat ditambahkan dalam kode sisi server menggunakan Config::setJsConfig('key', "value"); , yang akan ditetapkan kemudian untuk mengonfigurasi objek.
Ajax a namespace yang memiliki dua fungsi utama untuk mengirim permintaan Ajax. Satu untuk panggilan AJAX normal, dan satu lagi untuk mengunggah file.
Membantu Namespace yang memiliki berbagai fungsi menampilkan kesalahan, membuat serial, mengarahkan ulang, encodehtml, dan sebagainya
APP A namespace yang digunakan untuk menanamkan seluruh peristiwa JavaScript untuk halaman saat ini
Acara Namespace yang digunakan untuk mendeklarasikan semua peristiwa yang mungkin terjadi, seperti ketika pengguna mengklik tautan untuk membuat, menghapus atau memperbarui.
Untuk menunjukkan cara menggunakan kerangka kerja dalam situasi kehidupan nyata, kerangka kerja ini dilengkapi dengan implementasi untuk fitur-fitur seperti mengelola manajemen profil pengguna, dasbor, umpan berita, unggahan & unduhan file, posting & komentar, pagination, panel admin, mengelola cadangan sistem, pemberitahuan, bug laporan, ... dll.
Tangga:
Edit file konfigurasi di app/config/config.php dengan kredensial Anda
Jalankan kueri SQL di Direktori Instalasi _
Login
Pengaturan Email
Anda perlu mengkonfigurasi data akun SMTP Anda di app/config/config.php . Tetapi , jika Anda tidak memiliki akun SMTP, maka Anda menyimpan email di aplikasi/log/log.txt menggunakan Logger.
Untuk melakukan itu, dalam inti/email, komentar $mail->Send() & uncomment Logger::log("EMAIL", $mail->Body);
Setiap pengguna dapat mengubah nama, email, kata sandi. Juga unggah gambar profil (IE awalnya ditugaskan ke default.png).
Setiap kali pengguna meminta untuk mengubah emailnya, pemberitahuan akan dikirim ke email lama pengguna, dan yang baru.
Pemberitahuan yang dikirim ke email lama memberi pengguna kesempatan untuk mencabut perubahan email, sementara pemberitahuan yang dikirim ke email baru meminta konfirmasi. Pengguna masih bisa masuk dengan email lamanya sampai dia mengkonfirmasi perubahan.
Ini dilakukan di UserController , dalam Metode updateProfileInfo() , revokeEmail() , & updateEmail() . Dalam sebagian besar situasi, Anda tidak perlu memodifikasi perilaku metode ini.
Anda dapat mengunggah dan mengunduh file.
file_uploads ke trueupload_max_filesize, max_file_uploads, post_max_sizePikirkan umpan berita sebagai tweet di Twitter, dan di posting seperti saat Anda membuka masalah di GitHub.
Mereka diimplementasikan di atas kerangka kerja ini.
Admin dapat melakukan tindakan di mana pengguna normal tidak bisa. Mereka dapat menghapus, mengedit, membuat umpan berita, posting, atau komentar apa pun. Juga mereka memiliki kendali atas semua profil pengguna, membuat & mengembalikan cadangan.
Hanya admin yang memiliki akses untuk melihat semua pengguna terdaftar. Mereka dapat menghapus, mengedit info mereka.
Di sebagian besar situasi, Anda perlu membuat cadangan untuk sistem, dan mengembalikannya kapan pun Anda mau.
Ini dilakukan dengan menggunakan MySqldump untuk membuat dan mengembalikan cadangan. Semua cadangan akan disimpan di aplikasi/cadangan .
Apakah Anda melihat pemberitahuan merah di Facebook, atau yang biru di Twitter?. Ide yang sama ada di sini. Tapi, itu diterapkan menggunakan pemicu sebagai gantinya. Pemicu didefinisikan dalam _ Instalasi/Pemicu.sql .
Jadi, setiap kali pengguna membuat umpan berita baru, memposting, atau mengunggah file, ini akan menambah jumlah untuk semua pengguna lain, dan akan menampilkan pemberitahuan merah di bilah navigasi.
Pengguna dapat melaporkan bug, fitur & peningkatan. Setelah mereka mengirimkan formulir, email akan dikirim ke ADMIN_EMAIL yang ditentukan dalam app/config/config.php
Katakanlah Anda ingin membuat aplikasi TODO yang sederhana. Di sini, saya akan melangkah langkah demi langkah tentang cara membuat aplikasi Todo menggunakan kerangka kerja dengan & tanpa panggilan Ajax.
(1) Jika Anda mengikuti langkah pengaturan instalasi di atas, Anda seharusnya tidak memiliki masalah dengan membuat akun pengguna awal.
(2) Buat tabel dengan ID sebagai int, konten varchar, user_id sebagai kunci asing untuk tabel 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) Buat Todocontroller
Buat file yang disebut TodoController.php di dalam aplikasi/pengontrol
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) Buat kelas model note yang disebut Todo.php di aplikasi/model
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) Tampilan di dalam/
(a) Buat header.php & footer.php di dalam tampilan/tata letak/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) Di dalam tampilan/ membuat folder TODO yang akan memiliki index.php , yang akan berisi daftar TODO kami.
<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) Kode JavaScript untuk mengirim panggilan AJAX, dan menangani respons
// 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 ) ;
}
}
} ) ;
}
}
}Saya telah menulis skrip ini di waktu luang saya selama studi saya. Ini gratis, tidak dibayar. Saya mengatakan ini karena saya telah melihat banyak pengembang bertindak sangat kasar terhadap perangkat lunak apa pun, dan perilaku mereka benar -benar membuat frustrasi. Saya tidak tahu mengapa?! Semua orang cenderung mengeluh, dan mengucapkan kata -kata kasar. Saya menerima umpan baliknya, tetapi, dengan cara yang baik dan penuh hormat.
Ada banyak skrip lain secara online untuk pembelian yang melakukan hal yang sama (jika tidak kurang), dan penulisnya mendapatkan banyak uang darinya, tetapi, saya memilih untuk tetap publik, tersedia untuk semua orang.
Jika Anda mempelajari sesuatu, atau saya menghemat waktu Anda, harap dukung proyek dengan menyebarkan berita.
Berkontribusi dengan membuat masalah baru, mengirim permintaan tarik di github atau Anda dapat mengirim email di: [email protected]
Dibangun di bawah lisensi MIT.