======================================================================
 PSGI::Handy Tutorial (tut/)
 คู่มือบทเรียน                                           [TH] ภาษาไทย
======================================================================

 tut/ คือบทเรียนการสอนอย่างเป็นทางการของ PSGI::Handy:หลักสูตรหกบทยี่สิบสี่ขั้นตอน ที่สร้างเว็บแอปพลิเคชันตั้งแต่ศูนย์โดยไม่มีกล่องดำใดๆ เริ่มจากการตอบสนอง HTTP ที่เปลือยที่สุดไปจนถึงแอปพลิเคชัน CRUD ที่สมบูรณ์ แต่ละขั้นตอนเป็นโปรแกรม Perlที่เป็นอิสระและรันได้ สแต็กทั้งหมดเป็น Perlบริสุทธิ์และไม่มีการพึ่งพา: PSGI::Handy (เฟรมเวิร์ก) HTTP::Handy(เซิร์ฟเวอร์) HP::Handy (เทมเพลต) และ DB::Handy (ฐานข้อมูล)

[ วิธีการรัน ]

  รันทีละขั้นตอนจากภายในไดเรกทอรี tut/ ตัวอย่างเช่น  perl01_step01_text.pl  แต่ละโปรแกรมจะเริ่มเซิร์ฟเวอร์และพิมพ์ URL ของมันเปิด http://127.0.0.1:8080/ ในเบราว์เซอร์ และกด Ctrl-C เพื่อหยุดไดเรกทอรี templates/ และ data.csv ต้องเข้าถึงได้จากไดเรกทอรีปัจจุบันดังนั้นจึงควรเริ่มโปรแกรมจากภายใน tut/ เสมอ

[ ส่วนหัวร่วม ]

  ทุกไฟล์ .pl เปิดด้วยส่วนหัวเดียวกัน: use 5.00503; use strict; บล็อกBEGIN ขนาดเล็กที่จัดหา warnings stub แบบไม่ทำงานบน Perl ที่เก่ากว่า5.6 จากนั้น use warnings; local $^W = 1;  โปรแกรมใช้ barewordfilehandle และ open สองอาร์กิวเมนต์เพื่อให้ยังคงใช้งานได้ย้อนหลังไปถึง Perl 5.005_03ส่วนหัวนี้เหมือนกันในทุกขั้นตอนและไม่ได้ทำซ้ำด้านล่าง

----------------------------------------------------------------------
 ไฟล์ราก
----------------------------------------------------------------------

  00_README.txt
      ภาพรวมหลักสูตร: ทุกบทและขั้นตอนพร้อมเป้าหมายการเรียนรู้อ่านสิ่งนี้ก่อนเพื่อดูภาพรวมทั้งหมดก่อนเปิดโปรแกรมใดๆ

  data.csv
      ข้อมูลตัวอย่างสำหรับบทที่ 4 (ขั้นตอน CSV):สามแถวที่คั่นด้วยจุลภาคในรูปแบบ id,name,age ขั้นตอน 12, 14 และ15 เขียนทับไฟล์นี้ ดังนั้นเก็บข้อมูลสำรองไว้หากต้องการรีเซ็ต1,Alice,20 / 2,Bob,22 / 3,Charlie,25

----------------------------------------------------------------------
 บทที่ 1 — พื้นฐานการตอบสนอง HTTP (จากแบบคงที่สู่แบบไดนามิก)
----------------------------------------------------------------------

  การแลกเปลี่ยนที่ง่ายที่สุดระหว่างเบราว์เซอร์กับเซิร์ฟเวอร์โดยไม่มีเอนจินเทมเพลต เพื่อทำความเข้าใจรูปร่างของการตอบสนอง HTTP

  01_step01_text.pl
      ส่งคืนข้อความล้วน '.' สำหรับ GET / ผ่าน $c->text()แอปที่เล็กที่สุดที่เป็นไปได้: เบราว์เซอร์รับอักขระดิบ(text/plain)

  01_step02_html.pl
      ส่งคืน '<a>.' เป็น text/html ผ่าน $c->html()ไบต์เดียวกับขั้นตอนที่ 1 แต่ Content-Typeบอกเบราว์เซอร์ให้ถือว่าเป็นมาร์กอัปแสดงความแตกต่างระหว่างข้อความกับ HTML

  01_step03_dyn_text.pl
      ส่งคืนเวลาปัจจุบันเป็นข้อความล้วน อ่านใหม่ในทุกคำขอด้วย scalarlocaltime() รสชาติแรกของเอาต์พุตแบบไดนามิก:การตอบสนองเปลี่ยนแปลงทุกครั้ง

  01_step04_dyn_html.pl
      ห่อเวลาแบบไดนามิกในมาร์กอัป <h1>/<p> และส่งคืนเป็น HTMLการสร้างหน้าจอด้วยการประกอบสตริงจากตัวแปร

----------------------------------------------------------------------
 บทที่ 2 — อินพุตของผู้ใช้และสถานะ (ฟอร์มและการแตกแขนง)
----------------------------------------------------------------------

  การส่งข้อมูลจากเบราว์เซอร์ไปยังเซิร์ฟเวอร์และการแตกแขนงตามสิ่งที่มาถึง

  02_step05_form.pl
      GET / แสดงฟอร์ม HTML (กล่องข้อความและปุ่มส่ง) ที่ POST ไปยัง/echo ยังไม่มีตัวจัดการสำหรับการส่งขั้นตอนนี้เกี่ยวกับมาร์กอัปของฟอร์มเท่านั้น

  02_step06_echo.pl
      เพิ่มตัวจัดการ POST /echo มันอ่านฟิลด์ 'message' ด้วย$c->param('message') และสะท้อนกลับภายในการตอบสนอง HTML:วงจรพื้นฐานจากคำขอสู่การตอบสนอง

  02_step07_auth.pl
      การเข้าสู่ระบบขั้นต่ำ POST /login อ่าน 'username' และ 'password'และใช้ if เพื่อแตกแขนง: admin/secret ไปถึงหน้าสำเร็จอย่างอื่นไปหน้าล้มเหลวเมล็ดพันธุ์ของการยืนยันตัวตนและการไหลแบบมีเงื่อนไข

----------------------------------------------------------------------
 บทที่ 3 — การแยก View (แนะนำ HP::Handy)
----------------------------------------------------------------------

  หยุดเขียน HTML ภายในโปรแกรม ย้ายมันไปยังไฟล์เทมเพลต HP::Handyเปิดเผย render_file()/render_string() ดังนั้นมันจึงเชื่อมต่อกับPSGI::Handy ผ่าน CODE renderer ขนาดเล็กที่แมปชื่อเทมเพลตไปยังrender_file() บล็อก renderer นั้นเหมือนกันในทุกขั้นตอนที่มีเทมเพลต

  03_step08_template.pl
      เรนเดอร์ templates/step08.html แบบคงที่โดยไม่มีตัวแปร ตอนนี้HTML อยู่ในไฟล์ของตัวเอง แยกจากตรรกะ

  03_step09_template_var.pl
      ส่ง { current_time => ... } ไปยัง templates/step09.htmlซึ่งพิมพ์ด้วยตัวยึด {{ current_time }}ข้อมูลไหลจากโปรแกรมไปยังเทมเพลต

  03_step10_template_form.pl
      เขียนการสะท้อนของขั้นตอนที่ 6 ใหม่โดยใช้สองเทมเพลต:step10_form.html สำหรับอินพุต และ step10_result.htmlสำหรับผลลัพธ์ ตรรกะของตัวจัดการเล็กลงมาก

----------------------------------------------------------------------
 บทที่ 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 } และเรนเดอร์รายการเป็นตาราง HTML (step11_list.html)

  04_step12_csv_create.pl
      Create. POST /add เพิ่มหนึ่งบรรทัด 'id,name,age' ลงใน data.csvในโหมดเพิ่ม (>>) จากนั้นแสดงหน้าเสร็จสมบูรณ์

  04_step13_csv_readone.pl
      Read One. เส้นทาง /user/:id จับ id โปรแกรมสแกน 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 แฮนเดิลถูกสร้างด้วยDB::Handy->connect('data','app',{...}) และฉีดผ่าน db => $dbhจากนั้นเข้าถึงในตัวจัดการ เป็น $c->db รูทีน _bootstrap()สร้างและเพาะตาราง 'users' หนึ่งครั้งดังนั้นแต่ละตัวอย่างรันแบบสแตนด์อโลน

  05_step16_db_readall.pl
      Read All. selectall_arrayref(..., { Slice => {} }) ดึงทุกแถวusers เป็น hash สำหรับเทมเพลตรายการ (step16_list.html)ไม่ต้องแยกวิเคราะห์ไฟล์ด้วยตนเองอีกต่อไป

  05_step17_db_create.pl
      Create. POST /add แทรกหนึ่งแถวด้วย INSERT ที่มีตัวยึดจากนั้นเปลี่ยนเส้นทางไปยัง / (รายการ): รูปแบบ post-then-redirectมาตรฐาน

  05_step18_db_readone.pl
      Read One. /user/:id ดึงหนึ่งแถวตามคีย์หลักด้วยselectrow_hashref() หรือส่งคืน 404การค้นหาคีย์โดยตรงแทนการสแกนทุกแถว

  05_step19_db_update.pl
      Update. GET /user/:id/edit แสดงฟอร์มแก้ไข POST รัน UPDATE ...WHERE id=? และเปลี่ยนเส้นทางไปยังหน้ารายละเอียดไม่มีการเขียนทั้งไฟล์ใหม่ในหน่วยความจำ

  05_step20_db_delete.pl
      Delete. รัน DELETE ... WHERE id=? จากนั้นเปลี่ยนเส้นทางไปยัง /เส้นทางที่เปลี่ยนสถานะเป็น 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
      ฟอร์ม Select โหลดมาสเตอร์แผนกและเรนเดอร์เป็นดรอปดาวน์ <select>(step23_form_select.html) เพื่อให้ผู้ใช้เลือกแผนกแทนการพิมพ์ id

  06_step24_fullstack_app.pl
      แอปพลิเคชันที่สมบูรณ์ในไฟล์เดียว: list, detail, add, edit และdelete บนข้อมูล users+depts พร้อมชื่อแผนกที่แนบมา ดรอปดาวน์select, post-then-redirect ทุกที่ และตัวช่วย _next_id()(DB::Handy ไม่มี auto-increment) ใช้ app_list.html และapp_form.html

----------------------------------------------------------------------
 เทมเพลต (templates/)
----------------------------------------------------------------------

  เทมเพลต HP::Handy ใช้ไวยากรณ์คล้าย Jinja2: {{ var }} พิมพ์ค่า(เส้นทางแบบจุดเช่น {{ user.name }} เข้าถึงภายใน hash) {% for x inlist %} ... {% endfor %} วนซ้ำ และ {% if ... %} แตกแขนง rendererถูกสร้างด้วย auto_escape => 1 ดังนั้นค่าที่พิมพ์จึงถูก HTML-escape

  templates/step08.html
      หน้าคงที่ทั้งหมดที่ใช้โดยขั้นตอนที่ 8 ไม่มีตัวยึด

  templates/step09.html
      พิมพ์เวลาเซิร์ฟเวอร์ผ่าน {{ current_time }} (ขั้นตอนที่ 9)

  templates/step10_form.html
      ฟอร์มอินพุตสำหรับการสะท้อนของขั้นตอนที่ 10 POST 'message' ไปยัง/echo

  templates/step10_result.html
      แสดง {{ message }} ที่สะท้อนกลับสำหรับขั้นตอนที่ 10

  templates/step11_list.html
      วนซ้ำ {{ users }} เพื่อวาดตาราง CSV (ขั้นตอนที่ 11)

  templates/step12_form.html
      ฟอร์มเพิ่ม (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
      ฟอร์มเพิ่มผู้ใช้สำหรับขั้นตอนที่ 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 พร้อมลิงก์แก้ไขและลิงก์ย้อนกลับ

  templates/step19_edit.html
      ฟอร์มแก้ไข (name, age) สำหรับขั้นตอนที่ 19 POST ไปยัง/user/:id/edit

  templates/step20_list.html
      รายการของขั้นที่ 20 พร้อมปุ่มลบแบบ POST ที่ต้องยืนยันในแต่ละแถว

  templates/step21_join_list.html
      รายการพนักงานพร้อมคอลัมน์ {{ user.dept_name }} ที่เชื่อมแล้ว(ขั้นตอนที่ 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
      ฟอร์มเพิ่มพร้อม <select> แผนกที่สร้างจาก {{ depts }} (ขั้นตอนที่23)

  templates/app_list.html
      รายการพนักงานของแอปที่สมบูรณ์ (ขั้นตอนที่ 24): ID, ชื่อที่ลิงก์,แผนก, พร้อม Edit และ POST Delete ที่ป้องกันด้วยการยืนยันต่อแถวและลิงก์ Add

  templates/app_form.html
      ฟอร์มเพิ่ม/แก้ไขที่ใช้ร่วมกันของแอปที่สมบูรณ์ (ขั้นตอนที่ 24):เป้าหมาย {{ action }} สลับระหว่างการสร้างและการอัปเดต และ<select> แผนกเลือก {{ user.dept_id }} ไว้ล่วงหน้าผ่าน {% if %}

----------------------------------------------------------------------
 อ้างอิง
----------------------------------------------------------------------

  PSGI::Handy และส่วนที่เหลือของสแต็ก 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

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