======================================================================
 PSGI::Handy Tutorial (tut/)
 សៀវភៅណែនាំ                                            [KM] ភាសាខ្មែរ
======================================================================

 tut/ គឺជាមេរៀនបង្រៀនផ្លូវការរបស់ PSGI::Handy៖ វគ្គសិក្សាប្រាំមួយជំពូកនិងម្ភៃបួនជំហានដែលបង្កើតកម្មវិធីបណ្ដាញពីដំបូង ដោយគ្មានប្រអប់ខ្មៅណាមួយចាប់ផ្ដើមពីការឆ្លើយតប HTTP សាមញ្ញបំផុត រហូតដល់កម្មវិធី CRUD ពេញលេញ។ជំហាននីមួយៗគឺជាកម្មវិធី Perl ឯករាជ្យ និងអាចដំណើរការបាន។ stackទាំងមូលគឺ Perl សុទ្ធ និងគ្មានភាពអាស្រ័យ៖ PSGI::Handy (framework),HTTP::Handy (server), HP::Handy (គំរូ) និង DB::Handy(មូលដ្ឋានទិន្នន័យ)។

[ របៀបដំណើរការ ]

  ដំណើរការម្ដងមួយជំហានពីខាងក្នុងថត tut/ ឧទាហរណ៍  perl01_step01_text.pl  ។ កម្មវិធីនីមួយៗចាប់ផ្ដើម server ហើយបោះពុម្ព URLរបស់វា; បើក http://127.0.0.1:8080/ ក្នុង browser ហើយចុច Ctrl-Cដើម្បីបញ្ឈប់។ ថត templates/ និង data.csvត្រូវតែអាចចូលដំណើរការបានពីថតបច្ចុប្បន្នដូច្នេះតែងតែចាប់ផ្ដើមកម្មវិធីពីខាងក្នុង tut/។

[ ក្បាលរួម ]

  រាល់ឯកសារ .pl បើកដោយក្បាលដូចគ្នា៖ use 5.00503; use strict; blockBEGIN តូចមួយដែលផ្ដល់ warnings stub គ្មានដំណើរការនៅលើ Perl ចាស់ជាង5.6; បន្ទាប់មក use warnings; local $^W = 1;  ។ កម្មវិធីប្រើ barewordfilehandle និង open ពីរអាគុយម៉ង់ ដើម្បីឱ្យពួកវានៅតែត្រឹមត្រូវរហូតដល់Perl 5.005_03។ ក្បាលនេះដូចគ្នាក្នុងគ្រប់ជំហានហើយមិនត្រូវបានធ្វើម្ដងទៀតខាងក្រោមទេ។

----------------------------------------------------------------------
 ឯកសារ root
----------------------------------------------------------------------

  00_README.txt
      ទិដ្ឋភាពទូទៅនៃវគ្គសិក្សា៖ គ្រប់ជំពូកនិងជំហានជាមួយគោលដៅសិក្សារបស់វា។ អានវាមុនគេ ដើម្បីមើលលំហូរទាំងមូលមុនពេលបើកកម្មវិធីណាមួយ។

  data.csv
      ទិន្នន័យគំរូសម្រាប់ជំពូក 4 (ជំហាន CSV)៖ បីជួរបំបែកដោយសញ្ញាក្បៀសid,name,age។ ជំហាន 12, 14 និង 15 សរសេរឯកសារនេះឡើងវិញដូច្នេះរក្សាទុកច្បាប់ចម្លងបម្រុង ប្រសិនបើអ្នកចង់កំណត់វាឡើងវិញ។1,Alice,20 / 2,Bob,22 / 3,Charlie,25

----------------------------------------------------------------------
 ជំពូក 1 — មូលដ្ឋាននៃការឆ្លើយតប HTTP (ពីឋិតិវន្តទៅថាមវន្ត)
----------------------------------------------------------------------

  ការផ្លាស់ប្ដូរសាមញ្ញបំផុតរវាង browser និង server ដោយគ្មាន templateengine ដើម្បីយល់ពីរូបរាងនៃការឆ្លើយតប HTTP។

  01_step01_text.pl
      ត្រឡប់អត្ថបទធម្មតា '.' សម្រាប់ GET / តាមរយៈ $c->text()។កម្មវិធីតូចបំផុត៖ browser ទទួលតួអក្សរឆៅ (text/plain)។

  01_step02_html.pl
      ត្រឡប់ '<a>.' ជា text/html តាមរយៈ $c->html()។បៃដូចគ្នានឹងជំហានទី 1 ប៉ុន្តែ Content-Type ប្រាប់ browserឱ្យចាត់ទុកវាជា markup។ បង្ហាញភាពខុសគ្នារវាងអត្ថបទ និង HTML។

  01_step03_dyn_text.pl
      ត្រឡប់ពេលវេលាបច្ចុប្បន្នជាអត្ថបទធម្មតា អានឡើងវិញរាល់សំណើដោយscalar localtime()។ រសជាតិដំបូងនៃលទ្ធផលថាមវន្ត៖ការឆ្លើយតបផ្លាស់ប្ដូររាល់ពេល។

  01_step04_dyn_html.pl
      រុំពេលវេលាថាមវន្តក្នុង markup <h1>/<p> ហើយត្រឡប់ជា HTML។ការបង្កើតអេក្រង់ដោយការផ្គុំ string ពីអថេរ។

----------------------------------------------------------------------
 ជំពូក 2 — ការបញ្ចូលរបស់អ្នកប្រើ និងស្ថានភាព (form និងការបែកមែក)
----------------------------------------------------------------------

  ការផ្ញើទិន្នន័យពី browser ទៅ server ហើយបែកមែកតាមអ្វីដែលមកដល់។

  02_step05_form.pl
      GET / បង្ហាញ form HTML (ប្រអប់អត្ថបទ និងប៊ូតុង submit) ដែល POSTទៅ /echo។ មិនទាន់មាន handler សម្រាប់ការដាក់ស្នើទេ;ជំហាននេះគ្រាន់តែអំពី markup របស់ form។

  02_step06_echo.pl
      បន្ថែម handler POST /echo។ វាអាន field 'message' ដោយ$c->param('message') ហើយឆ្លុះវាត្រឡប់ក្នុងការឆ្លើយតប HTML៖វដ្ដមូលដ្ឋានពីសំណើទៅការឆ្លើយតប។

  02_step07_auth.pl
      ការចូលអប្បបរមា។ POST /login អាន 'username' និង 'password'ហើយប្រើ if ដើម្បីបែកមែក៖ admin/secret ទៅដល់ទំព័រជោគជ័យឯផ្សេងទៀតទៅទំព័របរាជ័យ។ គ្រាប់ពូជនៃការផ្ទៀងផ្ទាត់និងលំហូរតាមលក្ខខណ្ឌ។

----------------------------------------------------------------------
 ជំពូក 3 — ការបំបែក View (ណែនាំ HP::Handy)
----------------------------------------------------------------------

  ឈប់សរសេរ HTML ខាងក្នុងកម្មវិធី; ផ្លាស់ទីវាទៅឯកសារ template។HP::Handy បង្ហាញ render_file()/render_string()ដូច្នេះវាត្រូវបានភ្ជាប់ទៅ PSGI::Handy តាមរយៈ CODE renderer តូចមួយដែលmap ឈ្មោះ template ទៅ render_file()។ block rendererនោះដូចគ្នាក្នុងគ្រប់ជំហានដែលមាន template។

  03_step08_template.pl
      render templates/step08.html ឋិតិវន្តដោយគ្មានអថេរ។ ឥឡូវ HTMLរស់នៅក្នុងឯកសារផ្ទាល់ខ្លួន ដាច់ដោយឡែកពីតក្កវិជ្ជា។

  03_step09_template_var.pl
      បញ្ជូន { current_time => ... } ទៅ templates/step09.htmlដែលបោះពុម្ពវាជាមួយ placeholder {{ current_time }}។ទិន្នន័យហូរពីកម្មវិធីទៅ template។

  03_step10_template_form.pl
      សរសេរ echo នៃជំហានទី 6 ឡើងវិញដោយប្រើ template ពីរ៖step10_form.html សម្រាប់ការបញ្ចូល និង step10_result.htmlសម្រាប់លទ្ធផល។ តក្កវិជ្ជា handler ក្លាយជាតូចណាស់។

----------------------------------------------------------------------
 ជំពូក 4 — File I/O និងភាពស្ថិតស្ថេរ CSV (មូលដ្ឋាន CRUD)
----------------------------------------------------------------------

  អនុវត្ត Create/Read/Update/Delete ប្រឆាំងនឹងឯកសារ CSV ធម្មតាដោយគ្មានមូលដ្ឋានទិន្នន័យ។ ការដឹងពីតម្លៃនៃការដោះស្រាយទិន្នន័យដោយដៃជំរុញឱ្យផ្លាស់ទីទៅមូលដ្ឋានទិន្នន័យក្នុងជំពូក 5។

  04_step11_csv_readall.pl
      Read All. បើក data.csv បំបែកបន្ទាត់នីមួយៗដោយសញ្ញាក្បៀសទៅជា hash{ id, name, age } ហើយ render បញ្ជីជាតារាង HTML(step11_list.html)។

  04_step12_csv_create.pl
      Create. POST /add បន្ថែមមួយបន្ទាត់ 'id,name,age' ទៅ data.csvក្នុង mode បន្ថែម (>>) បន្ទាប់មកបង្ហាញទំព័របញ្ចប់។

  04_step13_csv_readone.pl
      Read One. route /user/:id ចាប់យក id; កម្មវិធី scan data.csvរកជួរដែលត្រូវគ្នា ហើយបង្ហាញព័ត៌មានលម្អិតរបស់វា(step13_detail.html) ឬត្រឡប់ 404 ប្រសិនបើរកមិនឃើញ។

  04_step14_csv_update.pl
      Update. ដើម្បីផ្លាស់ប្ដូរមួយជួរ វាអានគ្រប់បន្ទាត់ជំនួសជួរដែលត្រូវគ្នា ហើយសរសេរឯកសារទាំងមូលឡើងវិញ៖ កិច្ចការ'អានទាំងអស់ ប្ដូរ សរសេរទាំងអស់' ដែលមូលដ្ឋានទិន្នន័យនឹងលុបចេញ។

  04_step15_csv_delete.pl
      Delete. ការសរសេរឯកសារទាំងមូលឡើងវិញដូចគ្នាប៉ុន្តែជួរដែលត្រូវគ្នាត្រូវបានរំលងជំនួសឱ្យការជំនួស។

----------------------------------------------------------------------
 ជំពូក 5 — ណែនាំ Model (ផ្លាស់ទីទៅ DB::Handy)
----------------------------------------------------------------------

  ជំនួស CSV ដោយ DB::Handy។ handle ត្រូវបានបង្កើតដោយDB::Handy->connect('data','app',{...}) ហើយ inject តាមរយៈ db => $dbhបន្ទាប់មកចូលដំណើរការ ក្នុង handler ជា $c->db។ routine _bootstrap()បង្កើត និងបណ្ដុះតារាង 'users' មួយដងដូច្នេះឧទាហរណ៍នីមួយៗដំណើរការដោយឯករាជ្យ។

  05_step16_db_readall.pl
      Read All. selectall_arrayref(..., { Slice => {} }) ទាញរាល់ជួរusers ជា hash សម្រាប់ template បញ្ជី (step16_list.html)។លែងមានការ parse ឯកសារដោយដៃទៀតហើយ។

  05_step17_db_create.pl
      Create. POST /add បញ្ចូលមួយជួរដោយ INSERT ដែលមាន placeholderបន្ទាប់មក redirect ទៅ / (បញ្ជី)៖ លំនាំ post-then-redirectស្តង់ដារ។

  05_step18_db_readone.pl
      Read One. /user/:id ទាញមួយជួរតាមកូនសោចម្បងដោយselectrow_hashref() ឬត្រឡប់ 404។ ការស្វែងរកកូនសោដោយផ្ទាល់ជំនួសឱ្យការ scan គ្រប់ជួរ។

  05_step19_db_update.pl
      Update. GET /user/:id/edit បង្ហាញ form កែសម្រួល; POST ដំណើរការUPDATE ... WHERE id=? ហើយ redirect ទៅទំព័រលម្អិត។គ្មានការសរសេរឯកសារទាំងមូលឡើងវិញក្នុង memory ទេ។

  05_step20_db_delete.pl
      Delete. DELETE ... WHERE id=? ត្រូវបានដំណើរការ បន្ទាប់មកredirect ទៅ /។ route ផ្លាស់ប្ដូរស្ថានភាពគឺ POST តែប៉ុណ្ណោះដើម្បីជៀសវាងការលុបដោយចៃដន្យពី GET។

----------------------------------------------------------------------
 ជំពូក 6 — ទិន្នន័យទំនាក់ទំនង និងកម្មវិធីពិតប្រាកដ
----------------------------------------------------------------------

  តារាងពាក់ព័ន្ធពីរ (users និង depts) ក្នុងមូលដ្ឋានទិន្នន័យទីពីរ'app2'។ JOIN ត្រូវបានធ្វើដោយដៃក្នុង Perl ដើម្បីកុំឱ្យវាជាប្រអប់ខ្មៅបន្ទាប់មកអ្វីៗទាំងអស់ត្រូវបានផ្គុំទៅជាកម្មវិធីពេញលេញមួយ។

  06_step21_db_join_list.pl
      JOIN List. អានតារាងទាំងពីរ បង្កើតការស្វែងរក dept_id => nameភ្ជាប់ dept_name ទៅអ្នកប្រើនីមួយៗ (JOIN សរសេរដោយដៃ)ហើយរាយបញ្ជីពួកវា (step21_join_list.html)។

  06_step22_db_join_detail.pl
      JOIN Read One. ទាញអ្នកប្រើម្នាក់បន្ទាប់មកស្វែងរកនាយកដ្ឋានតែមួយរបស់អ្នកប្រើនោះហើយភ្ជាប់ឈ្មោះរបស់វាសម្រាប់ទំព័រលម្អិត(step22_join_detail.html)។

  06_step23_db_form_select.pl
      form Select. ផ្ទុក master នាយកដ្ឋាន ហើយ render វាជា dropdown<select> (step23_form_select.html)ដូច្នេះអ្នកប្រើជ្រើសរើសនាយកដ្ឋានជំនួសឱ្យការវាយ id។

  06_step24_fullstack_app.pl
      កម្មវិធីពេញលេញក្នុងឯកសារតែមួយ៖ list, detail, add, edit និងdelete លើទិន្នន័យ users+depts ជាមួយឈ្មោះនាយកដ្ឋានដែលភ្ជាប់dropdown select, post-then-redirect គ្រប់កន្លែង និងជំនួយ_next_id() (DB::Handy គ្មាន auto-increment)។ ប្រើ app_list.htmlនិង app_form.html។

----------------------------------------------------------------------
 គំរូ (templates/)
----------------------------------------------------------------------

  template HP::Handy ប្រើ syntax ស្រដៀង Jinja2៖ {{ var }}បោះពុម្ពតម្លៃ (ផ្លូវចំណុចដូចជា {{ user.name }} ចូលទៅក្នុង hash), {%for x in list %} ... {% endfor %} loop ហើយ {% if ... %} បែកមែក។renderer ត្រូវបានបង្កើតដោយ auto_escape => 1ដូច្នេះតម្លៃដែលបោះពុម្ពត្រូវបាន HTML-escape។

  templates/step08.html
      ទំព័រឋិតិវន្តពេញលេញដែលប្រើដោយជំហានទី 8; គ្មាន placeholder។

  templates/step09.html
      បោះពុម្ពពេលវេលា server តាមរយៈ {{ current_time }} (ជំហានទី 9)។

  templates/step10_form.html
      form បញ្ចូលសម្រាប់ echo នៃជំហានទី 10; POST 'message' ទៅ /echo។

  templates/step10_result.html
      បង្ហាញ {{ message }} ដែលឆ្លុះសម្រាប់ជំហានទី 10។

  templates/step11_list.html
      loop លើ {{ users }} ដើម្បីគូរតារាង CSV (ជំហានទី 11)។

  templates/step12_form.html
      form បន្ថែម (id, name, age) សម្រាប់ជំហានទី 12។

  templates/step12_result.html
      ទំព័របញ្ជាក់ "Added" នៃជំហានទី 12៖ បង្ហាញ {{ name }} និងតំណត្រឡប់។

  templates/step13_list.html
      សន្ទស្សន៍នៃជំហានទី 13៖ អ្នកប្រើប្រាស់នីមួយៗភ្ជាប់ទៅទំព័រលម្អិត /user/:id របស់ខ្លួន។

  templates/step13_detail.html
      បង្ហាញ id/name/age របស់អ្នកប្រើម្នាក់សម្រាប់ជំហានទី 13។

  templates/step14_list.html
      ទំព័រនៃជំហានទី 14៖ ជួរបច្ចុប្បន្ន និងទម្រង់ធ្វើបច្ចុប្បន្នភាពដែល POST ទៅ /update។

  templates/step15_list.html
      ទំព័រនៃជំហានទី 15៖ ជួរបច្ចុប្បន្ន និងទម្រង់លុបដែល POST ទៅ /delete។

  templates/step16_list.html
      បញ្ជីអ្នកប្រើ DB សាមញ្ញសម្រាប់ជំហានទី 16 (មិនទាន់មានតំណ)។

  templates/step17_form.html
      form បន្ថែមអ្នកប្រើសម្រាប់ជំហានទី 17។

  templates/step17_list.html
      បញ្ជីជំហានទី 17 ដែលមានតំណ "Add New User" ទៅ /add។

  templates/step18_list.html
      បញ្ជីជំហានទី 18៖ ឈ្មោះនីមួយៗភ្ជាប់ទៅលម្អិត /user/:id របស់ខ្លួន។

  templates/step18_detail.html
      ព័ត៌មានលម្អិតអ្នកប្រើសាមញ្ញសម្រាប់ជំហានទី 18 ដែលមានតំណត្រឡប់។

  templates/step19_list.html
      បញ្ជីជំហានទី 19៖ ឈ្មោះនីមួយៗភ្ជាប់ទៅទំព័រលម្អិតរបស់ខ្លួន។

  templates/step19_detail.html
      ព័ត៌មានលម្អិតអ្នកប្រើនៃជំហានទី 19 ដែលមានតំណ Edit និងតំណត្រឡប់។

  templates/step19_edit.html
      form កែសម្រួល (name, age) សម្រាប់ជំហានទី 19; POST ទៅ/user/:id/edit។

  templates/step20_list.html
      បញ្ជីជំហានទី 20 ដែលមានប៊ូតុង Delete បែប POST ដែលត្រូវការការបញ្ជាក់ នៅជួរនីមួយៗ។

  templates/step21_join_list.html
      បញ្ជីបុគ្គលិកជាមួយ column {{ user.dept_name }} ដែលបាន join(ជំហានទី 21)។

  templates/step22_join_list.html
      សន្ទស្សន៍បុគ្គលិកនៃជំហានទី 22៖ ឈ្មោះនីមួយៗភ្ជាប់ទៅទំព័រលម្អិតរបស់ខ្លួន។

  templates/step22_join_detail.html
      ព័ត៌មានលម្អិតបុគ្គលិកដែលបង្ហាញឈ្មោះនាយកដ្ឋាន (ជំហានទី 22)។

  templates/step23_list.html
      បញ្ជីបុគ្គលិកជំហានទី 23 ដែលមានតំណ "Add New Employee" ទៅ /add។

  templates/step23_form_select.html
      form បន្ថែមជាមួយ <select> នាយកដ្ឋានដែលបង្កើតពី {{ depts }}(ជំហានទី 23)។

  templates/app_list.html
      បញ្ជីបុគ្គលិករបស់កម្មវិធីពេញលេញ (ជំហានទី 24)៖ ID,ឈ្មោះដែលភ្ជាប់, នាយកដ្ឋាន បូករួមនឹង Edit និង POST Deleteការពារដោយការបញ្ជាក់ក្នុងជួរនីមួយៗ និង link Add។

  templates/app_form.html
      form បន្ថែម/កែសម្រួលរួមរបស់កម្មវិធីពេញលេញ (ជំហានទី 24)៖ គោលដៅ {{action }} ប្ដូររវាងការបង្កើត និងការធ្វើបច្ចុប្បន្នភាព ហើយ<select> នាយកដ្ឋានជ្រើសរើស {{ user.dept_id }} ជាមុនតាមរយៈ {% if%}។

----------------------------------------------------------------------
 ឯកសារយោង
----------------------------------------------------------------------

  PSGI::Handy និងផ្នែកដែលនៅសល់នៃ stack Handy នៅលើ MetaCPANនិងលក្ខណៈបច្ចេកទេស PSGI៖

    https://metacpan.org/dist/PSGI-Handy
    https://metacpan.org/dist/HTTP-Handy
    https://metacpan.org/dist/HP-Handy
    https://metacpan.org/dist/DB-Handy
    https://github.com/plack/psgi-specs/blob/master/PSGI.pod

======================================================================
