nattapong99 / book

0 stars 0 forks source link

Symfony Forms #3

Open nattapong99 opened 6 years ago

nattapong99 commented 6 years ago

Form

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

Creating a Simple Form

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

01

คลาสนี้เป็น “PHP-object แบบเก่า“ ไม่มีอะไรเกี่ยวข้องกับ Symfony หรือไลบรารี่ใด ๆ เป็นเพียงแค่ PHP object ธรรมดาที่สามารถแก้ไขปัญหาภายในแอพพลิเคชันของคุณได้โดยตรง ( เช่นต้องการแสดงงานในแอพพลิเคชันของคุณ) โดยในตอนท้ายของบทความนี้ คุณจะสามารถส่งข้อมูลไปยัง Task instance (ผ่านฟอร์ม HTML) ตรวจสอบความถูกต้องของข้อมูลและจัดเก็บในฐานข้อมูล

Building the Form

เมื่อสร้างคลาส Task เสร็จแล้ว ขั้นตอนต่อมาคือการสร้างและแสดงผลแบบฟอร์ม HTML ใน Symfony การดำเนินการนี้ทำได้โดยการสร้างอ็อบเจ็กต์ของฟอร์มและแสดงผลในเทมเพลต ตอนนี้ทั้งหมดนี้สามารถทำได้จากภายใน controller

02

ตัวอย่างนี้ได้แสดงวิธีสร้างแบบฟอร์มโดยตรงจากภายใน controller ซึ่งในหัวข้อ "Creating Form Classes” คุณจะได้เรียนรู้วิธีสร้างฟอร์มในลักษณะ standalone class ซึ่งจะทำให้ฟอร์มที่คุณสร้างสามารถนำกลับมาใช้ได้ใหม่

การสร้างฟอร์มใช้โค้ดค่อนข้างน้อยเนื่องจากอ็อบเจ็กต์ของ Symfony สร้างขึ้นด้วย "form builder" ซึ่งวัตถุประสงค์ของ form builder คือช่วยให้งานในการสร้างฟอร์มน้อยลงและสามารถเขียนฟอร์มได้ง่ายขึ้น

ในตัวอย่างนี้ คุณได้เพิ่ม 2 fields ไปในฟอร์มคือ task และ dueDate ซึ่งต้องตรงกับ properties task และ dueDate ของคลาส Task คุณได้กำหนดประเภท “type” (เช่น TextType และ DataType) ไว้ด้วยชื่อคลาส เหนือสิ่งอื่นใดจะกำหนดว่าจะให้แท็ก HTML form ใดแสดงผลสำหรับฟิลด์นั้น

และสุดท้าย คุณได้เพิ่มปุ่ม submit เพื่อใช้ส่งข้อมูลไปยังเซิฟเวอร์

Symfony ยังมีฟอร์ม built-in type หลายชนิด ดูได้ในหัวข้อ Built-in Field Types

Rendering the Form

เมื่อฟอร์มได้สร้างขึ้นแล้ว ขั้นตอนไปคือการแสดงผลฟอร์ม ทำได้โดยการส่ง object view ไปยังเทมแพลต (สังเกตุที่ $form->createView() จาก controller ข้างบน) และใช้ form helper function

03

ในตัวอย่างนี้ สมมติคุณส่งข้อมูลจากฟอร์มแบบ POST request และให้แสดงผลที่ URL เดิม คุณจะได้เรียนรู้วิธีการ
เปลี่ยน request method และ URL ต่อ ๆ ไป

ต้องใช้คำสั่ง 3 บรรทัดนี้ในการทำให้ฟอร์มแสดงผลได้สมบูรณ์

form_start(form)

คำสั่งเริ่มต้นฟอร์ม โดยรวมถึงแอตทริบิวต์ enctype เมื่อใช้การอัพโหลดไฟล์

form_widget(form)

แสดงผล field ของฟอร์มทั้งหมด ซึ่งรวมข้อความ การ validate และ error message

form_end(form)

เป็นคำสั่งที่บอกว่าการแสดงผลของฟอร์มสิ้นสุด

จะเห็นว่าเป็นเรื่องง่าย แต่ยังไม่ยืดหยุ่นพอ ซึ่งโดยปกติคุณจะต้องการควบคุมการแสดงผลของฟอร์มที่แยกเป็นส่วน ๆ ซึ่งคุณสามารถเรียนรู้ได้ในหัวข้อ “How to Control the Rendering of a Form”

ก่อนที่จะไปต่อ สังเกตการแสดงผลของ task input field ว่ามีค่าของ task property จาก $task object (เช่น “Write a blog post”) ซึ่งงานแรกของฟอร์มคือ การนำข้อมูลออกจาก object และแปลงมันให้อยู่รูปแบบที่เหมาะสมสำหรับนำไปแสดงผลในฟอร์ม HTML

ระบบฟอร์มจะมีความฉลาดเพียงพอที่เข้าถึงค่าของ protected task property ผ่านทางเมธอด getTask()
และ setTask() ของคลาส Task ยกเว้นว่าเป็น public property จำเป็นต้องมีเมธอด getter และ 
setter เพื่อให้คอมโพเนนต์ฟอร์มสามารถรับและใส่ข้อมูลลงใน property ได้ สำหรับ boolean property 
คุณสามารถใช้เมธอด isser หรือ hasser (เช่น isPublished() หรือ hasReminder()) แทนการใช้
getter (เช่น getPublished หรือ getReminder())

Handling Form Submissions

โดยค่าเริ่มต้น ฟอร์มจะส่งข้อมูลแบบ POST request ส่งกลับไปยัง controller เดิมเพื่อจะนำไปแสดงผล

โดยงานที่สองของฟอร์มคือ การแปลงข้อมูลที่ผู้ใช้ส่งกลับมายัง property ของ object เพื่อให้การทำงานเกิดขึ้น ข้อมูลที่ส่งมาจากผู้ใช้ต้องถูกเขียนลงใน Form object ดูตัวอย่างจาก controller ข้างล่าง

04

ควรระวังเรื่องการใช้เมธอด createView() ควรเรียกหลังจากเมธอด handRequest มิเช่นนั้นการเปลี่ยนแปลง
ที่เกิดขึ้นในเหตุการณ์ SUBMIT จะไม่ถูกใช้กับการแสดงผล (เช่นข้อผิดพลาดในการตรวจสอบ)

โดยทั่วไป controller จะมี 3 รูปแบบที่เป็นไปได้ในการจัดการกับฟอร์ม

  1. เมื่อเริ่มต้นโหลดหน้าเพจ ฟอร์มถูกสร้างขึ้นและแสดงผล เมธอด handleRequest() จะรู้ว่าฟอร์มยังไม่ถูก submit ทำให้เมธอด isSubmitted() ส่งค่า false กลับมาถ้าฟอร์มยังไม่ถูก submit
  2. เมื่อ user submit form เมธอด handleRequest() จะรู้และจะส่งข้อมูลที่ submit มาไปยัง property task และ dueDate ของ $task object จากนั้น object จะถูก validate ถ้าข้อมูลไม่ถูกต้อง เมธอด isValid() ส่งค่า false และฟอร์มจะแสดงผลอีกครั้งพร้อมด้วยข้อความ validate error
  3. เมื่อ user submit form ด้วยข้อมูลที่ถูกต้อง ข้อมูลจะถูกเขียนกลับมายังฟอร์มแต่ครั้งนี้เมธอด isValid() จะส่งค่า true ทำให้เราสามารถที่จะทำงานกับ object นี้ได้ (เช่น จัดเก็บลงฐานข้อมูล) ก่อนที่จะ redirect หน้าเพจอื่น ๆ
การ redirecting หลังจาก submit ฟอร์มสำเร็จ เป็นการป้องกันไม่ให้ผู้ใช้กดปุ่ม Refresh บนบราวเซอร์
มันจะทำให้เป็นการ re-post data

ถ้าคุณต้องการการควบคุมที่มากขึ้นสำหรับการ submit ฟอร์มหรือการส่งผ่านข้อมูล คุณสามารถใช้เมธอด submit() อ่านเพิ่มเติมได้หัวข้อ Calling Form::submit() manually

Form Validation

ในหัวข้อก่อนหน้านี้ คุณได้เรียนรู้วิธีการ submit ฟอร์มกับข้อมูลที่ถูกต้องหรือไม่ถูกต้อง ใน Symfony การ validate จะถูกปรับใช้กับ object (เช่น Task) ในอีกความหมายนึงก็คือ คำถามไม่ใช่ว่าฟอร์มถูกต้องหรือไม่ แต่เป็นคำถามที่ว่าข้อมูลของ $task object ถูกต้องหรือไม่หลังจากที่ฟอร์ม submit ส่งข้อมูลมา สามารถเรียกใช้ $form->isValid() เพื่อเช็คว่าข้อมูล $task object ถูกต้องหรือไม่

การ validation สามารถทำได้โดยการกำหนดกฎ (เรียกว่า constraints) เพิ่มไปใน class เพื่อให้เห็นการทำงาน ในตัวอย่างนี้จะเพิ่มการตรวจสอบ task field ห้ามเป็นค่าว่าง และ dueDate field ห้ามเป็นค่าว่างและข้อมูลต้องเป็น DateTime object

05

หลังจากนี้เมื่อคุณ submit ฟอร์มด้วยข้อมูลที่ไม่ถูกต้อง คุณจะเจอการแสดงของ error แสดงออกมาจากฟอร์ม

HTML5 Validation

บราวเซอร์จำนวนมากสามารถใช้การ validate ฝั่ง client side ได้ โดยการ validate ที่เป็นคำสั่งพื้นฐานที่สุดคือการเพิ่มแอตทริบิว required ใน field สำหรับบราวเซอร์ที่สนับสนุน HTML5 จากผลลัพธ์นี้ บราวเซอร์แจ้งเตือนแสดงข้อความออกมาหากผู้ใช้กด submit โดยที่ field ของฟอร์มไม่ได้ใส่ข้อมูลมา

อย่างไรก็ตามเราสามารถปิดการ validate โดยการเพิ่มแอตทริบิว novalidate ลงใน form tag หรือเพิ่ม formnovalidate ที่ submit tag ซึ่งจะทำในกรณีที่เราต้องการทดสอบการ validate ทางฝั่ง server-side

06

Built-in Field Types

ใน Symfony จะมาพร้อมกับ field type มาตรฐานที่ครอบคลุมรูปแบบ form พื้นฐานและ data type ต่าง ๆ

Text Fields¶

•   TextType
•   TextareaType
•   EmailType
•   IntegerType
•   MoneyType
•   NumberType
•   PasswordType
•   PercentType
•   SearchType
•   UrlType
•   RangeType

Choice Fields¶

•   ChoiceType
•   EntityType
•   CountryType
•   LanguageType
•   LocaleType
•   TimezoneType
•   CurrencyType

Date and Time Fields¶

•   DateType
•   DateIntervalType
•   DateTimeType
•   TimeType
•   BirthdayType

Other Fields¶

•   CheckboxType
•   FileType
•   RadioType

Field Groups¶

•   CollectionType
•   RepeatedType

Hidden Fields¶

•   HiddenType

Buttons¶

•   ButtonType
•   ResetType
•   SubmitType

Base Fields¶

•   FormType

Field Type Options

แต่ละ field type จะมีจำนวนของ option ให้ใช้สำหรับการ configure ตัวอย่างเช่น ณ ตอนนี้ duaDate field แสดงผลโดยใช้ 3 select boxes อย่างไรก็ตาม DateType สามารถกำหนดค่าให้แสดงผลเป็น text box ช่องเดียวได้

07

โดยแต่ละ field type จะมีตัวเลือก option ให้ใช้แตกต่างกัน โดยมี option หลายชนิดที่ต้องใช้เฉพาะเจาะจงกับ field type ชนิดนั้น ๆ

The required Option

option ที่พื้นฐานที่สุดคือ requied option ซึ่งสามารถใช้ได้กับทุก field type โดยค่าเริ่มต้น required option จะถูกตั้งค่าไว้เป็น true นั่นหมายความว่าทุกบราวเซอร์ที่รองรับ HTML5 จะทำการ validate ฝั่ง client ถ้า field ไม่ได้ใส่ข้อมูล ถ้าไม่ต้องการให้มันทำงาน ทำได้โดยปิด HTML5 validation หรือตั้งค่า required option เป็น false

08

นอกจากนี้โปรดทราบว่าการตั้งค่า required option เป็น true จะไม่ส่งผลให้มีการตรวจสอบความถูกต้องฝั่ง server side กล่าวอีกนัยหนึ่งถ้าผู้ใช้ส่งค่าว่างสำหรับฟิลด์ (เช่นกับเบราเซอร์รุ่นเก่าหรือ web service) มันจะถือว่าเป็นค่าที่ถูกต้องเว้นแต่คุณจะใช้ validate constraint NotBlank หรือ NotNull ของ Symfony

Field Type Guessing

เมื่อคุณได้เพิ่มการ validate ไปในคลาส Task ตัว Symfony จะรู้เกี่ยวกับ field ของคุณซึ่งมันสามารถคาดเดาได้ว่าประเภท field ของคุณเป็นอะไรและตั้งค่าให้ ในตัวอย่างนี้ Symfony คาดเดาจาก validate rules ของ task field ว่าใช้เป็น TextType และ dueDate field ควรเป็น DateType

09

การคาดเดาจะทำงานเมื่อคุณละเว้นการส่ง argument ตัวที่สองของเมธอด add() (หรือส่งเป็นค่า null ไปแทน) แต่ถ้ามี array option คุณสามารถทำได้โดยส่งมันไปใน argument ตัวที่สาม (ดังตัวอย่าง dueDate ข้างบน)

ถ้าฟอร์มของคุณมีการระบุใช้ validate group ตัว field type guesser จะยังพิจารณา validate 
constraints ทั้งหมดเวลาที่คาดเดาจาก field type (รวมไปถึง constraints ที่ไม่ได้กำหนดไว้ใน 
validate group ก็จะถูกนำมาใช้ด้วย)

Field Type Options Guessing

นอกจากการคาดเดา type ของ field แล้ว ตัว Symfony ยังสามารถคาดเดาค่าที่ถูกต้องของ option ของ field ได้ด้วย

เมื่อมีการตั้งค่า option เหล่านี้ field จะแสดงผลด้วยแอตทริบิวต์ HTML พิเศษที่ทำให้ HTML5 มีการ 
validate ฝั่ง client-side อย่างไรก็ตาม มันไม่ได้สร้าง constraints ด้าน server-side ที่เหมือนกัน
(เช่น Assert \ Length) และแม้ว่าคุณจะเพิ่มการ validate ด้าน server-side ด้วยตนเอง แต่ field 
type option เหล่านี้จะสามารถคาดเดาได้จากข้อมูลดังกล่าว

Required option สามารถคาดเดาได้ตาม validate rule (เช่น field NotBlank หรือ NotNull หรือไม่) หรือใน Doctrine (เช่น field nullable หรือไม่) นี่เป็นประโยชน์อย่างยิ่งเนื่องจากการ validate ฝั่ง client-side ของคุณจะตรงกับ validate rule ของคุณโดยอัตโนมัติ

Maxlenght ถ้าฟิลด์บางฟิลด์เป็นการจัดเรียงข้อความ maxlength option attribute สามารถคาดเดาได้จาก validate constraint (ถ้าใช้ Length หรือ Range) หรือจาก Doctrine (ผ่าน field length)

โดย field option เหล่านี้จะถูกคาดเดาไว้เฉพาะในกรณีที่คุณใช้ Symfony เพื่อคาดเดา field type 
(เช่นละเว้นการส่งค่าหรือส่งค่า null เป็นอาร์กิวเมนต์ที่สองของเมธอด add())

ถ้าคุณต้องการเปลี่ยนแปลงค่าของการคาดเดา คุณสามารถส่ง option ไปใน option field array

10

Creating Form Classes

จากที่ผ่านมา จะเห็นว่าฟอร์มสามารถสร้างได้โดยตรงใน controller อย่างไรก็ตามทางที่ดีกว่าคือการสร้างฟอร์มแยกไว้ต่างหาก ให้เป็น standalone PHP class ซึ่งจะช่วยให้สามารถนำกลับมาใช้ได้ใหม่ในทุก ๆ ที่ของแอพพลิเคชั่นของคุณ สร้าง new class สำหรับการสร้างฟอร์ม task

11

โดย class ใหม่นี้ข้างในจะมีสิ่งที่จำเป็นสำหรับการสร้างฟอร์ม Task โดยสามารถสร้าง form object ได้อย่างรวดเร็วใน controller

12

การสร้างฟอร์ม logic ไว้ใน class ของมันเอง นั้นจะทำให้เราสามารถนำมันไปใช้ใหม่ได้ง่ายในทุก ๆ ที่ของโปรเจ็ค ซึ่งเป็นวิธีที่ดีสำหรับการสร้างฟอร์ม

Setting the data_class

ทุกฟอร์มจำเป็นต้องทราบชื่อของ class ที่เก็บข้อมูลพื้นฐานไว้ (เช่น AppBundle\Entity\Task) โดยปกติแล้วนี่เป็นเพียงการคาดเดา object ที่ส่งผ่านไปยังอาร์กิวเมนต์ที่สองไปที่ createForm() (เช่น $task) โดยทั่วไปคุณควรระบุตัวเลือก data_class อย่างชัดเจนโดยเพิ่มข้อมูลต่อไปนี้ลงใน form type class

13

เมื่อแม็ปฟอร์มกับอ็อบเจ็กต์ ฟิลด์ทั้งหมดจะถูกแมป ฟิลด์ใด ๆ ในแบบฟอร์มที่ไม่มีอยู่ในออบเจกต์ที่แมป จะทำให้เกิดข้อผิดพลาดแสดงออกมา

ในกรณีที่คุณต้องการเพิ่มฟิลด์ขึ้นมาในฟอร์ม (เช่น “คุณยอมรับเงื่อนไขหรือไม่” เป็น checkbox) ซึ่งไม่ได้แม็ปกับ object คุณจำเป็นที่จะต้องกำหนดค่า mapped option เป็น false

14

นอกจากนี้ หากมี field ใด ๆ ในฟอร์มที่ไม่ได้รวมอยู่ในข้อมูลที่ submit มา ข้อมูลของ field เหล่านี้จะถูกกำหนดค่าเป็น null

การเข้าถึงข้อมูลของ field ใน controller ทำได้โดย

15

นอกจากนี้การแก้ไขข้อมูลที่ไม่ได้แม็ปสามารถทำได้โดย

16

Final Thoughts

เมื่อกำลังสร้างฟอร์ม จำไว้ว่าเป้าหมายแรกของฟอร์มคือ การแปลงข้อมูลจาก object (Task) ไปแสดงยัง HTML ฟอร์มเพื่อให้ผู้ใช้สามารถแก้ไขข้อมูลได้ เป้าหมายต่อมาของฟอร์มคือ การนำข้อมูลที่ผู้ใช้ส่งกลับมาแปลงกลับไปสู่ object

source