nattapong99 / book

0 stars 0 forks source link

How to Use Data Transformers #11

Open nattapong99 opened 6 years ago

nattapong99 commented 6 years ago

How to Use Data Transformers

Data transformers ใช้เพื่อแปลงข้อมูลของ field ไปสู่รูปแบบที่ต้องการเพื่อนำแสดงไปยังฟอร์ม (และส่งข้อมูลกลับมาหลังจาก submit) ยกตัวอย่างเช่น DateType field แสดงผลเป็นรูปแบบ yyyy-MM-dd ผ่าน input textbox โดย data transformers สามารถแปลงค่า yyyy-MM-dd ของ DateTime field ไปเป็นรูปแบบ string เพื่อแสดงผลผ่านฟอร์ม และแปลงมันกลับไปเป็น DateTime object เมื่อ submit

เมื่อฟอร์ม field มีการกำหนด option เป็น inherit_data จะไม่สามารถใช้ Data Transformer สำหรับ field นั้นได้

Simple Example: Transforming String Tags from User Input to an Array

สมมติคุณมีฟอร์มดังภาพ

01

โดยภายใน tags เก็บข้อมูลเป็นอาร์เรย์ แต่ตอนแสดงให้ user ใช้งานจะแสดงผลเป็น string โดยให้ใส่ , คั่นไว้เพื่อความสะดวกในการใช้งานของ user

วิธีที่การใช้ data transformer เพื่อจัดการข้อมูลของ tags field ทำได้โดยใช้คลาส CallbackTransformer

02

ฟังก์ชัน CallbackTransformer ต้องการอาร์กิวเมนต์ 2 ตัว ตัวแรกแปลงค่า original ไปเป็นรูปแบบที่จะใช้แสดงผล ตัวที่สองจะแปลงข้อมูลที่ submit มาไปเป็นรูปแบบที่ต้องการใช้งานต่อในโค้ด

เมธอด addModelTransformer จะสามารถใช้ได้กับ object ใด ๆ ก็ตามที่ implements DataTransformerInterface ดังนั้นคุณจึงสามารถสร้างคลาสของคุณเองเอาไว้ใช้งาน แทนที่จะใช้เมธอดนี้ ไปใน logic ฟอร์มของคุณ

คุณสามารถเพิ่ม transformer ในขณะกำลังเพิ่ม field ได้ดัวยดังรูป

03

Harder Example: Transforming an Issue Number into an Issue Entity

สมมติคุณมีความสัมพันธ์ many-to-one ของ model Task ไป Issue (แต่ละ Task จะมี FK ของ Issue) การเพิ่ม listbox เพื่อแสดง issue ทั้งหมดอาจใช้เวลาในการโหลดข้อมูลนานเกินไป ดังนั้นอีกวิธีการนึงคือ คุณเพิ่ม textbox เพื่อให้ user ใส่เลข issue แทน เริ่มการกำหนด text field ดังรูป

04

จากรูปเมื่อ submit ข้อมูลมา property issue ของ model Task จะได้ข้อมูลมาเป็น string แล้วเราจะแปลงมันกลับไปเป็น object Issue ได้อย่างไร

Creating the Transformer

คุณอาจใช้ CallbackTransformer แบบตัวอย่างที่แล้วก็ได้ แต่ในตัวอย่างนี้จะซับซ้อนขึ้นมาหน่อยโดยใช้วิธีการสร้างคลาส transformer ขึ้นมาใหม่ โดยเอาไว้ใช้กับคลาส TaskType

สร้างคลาส IssueToNumberTransformer ไว้สำหรับทำหน้าที่ในการแปลงข้อมูล issue number ไปเป็น Issue object และแปลงกลับ

// src/AppBundle/Form/DataTransformer/IssueToNumberTransformer.php
namespace AppBundle\Form\DataTransformer;

use AppBundle\Entity\Issue;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;

class IssueToNumberTransformer implements DataTransformerInterface
{
    private $em;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    /**
     * Transforms an object (issue) to a string (number).
     *
     * @param  Issue|null $issue
     * @return string
     */
    public function transform($issue)
    {
        if (null === $issue) {
            return '';
        }

        return $issue->getId();
    }

    /**
     * Transforms a string (number) to an object (issue).
     *
     * @param  string $issueNumber
     * @return Issue|null
     * @throws TransformationFailedException if object (issue) is not found.
     */
    public function reverseTransform($issueNumber)
    {
        // no issue number? It's optional, so that's ok
        if (!$issueNumber) {
            return;
        }

        $issue = $this->em
            ->getRepository(Issue::class)
            // query for the issue with this id
            ->find($issueNumber)
        ;

        if (null === $issue) {
            // causes a validation error
            // this message is not shown to the user
            // see the invalid_message option
            throw new TransformationFailedException(sprintf(
                'An issue with number "%s" does not exist!',
                $issueNumber
            ));
        }

        return $issue;
    }
}

คล้ายกับตัวอย่างแรก คือการแปลงข้อมูลมีสองทิศทางโดยเมธอด tranform() จะแปลงข้อมูลในโค้ดไปเป็นรูปแบบที่สามารถนำไปแสดงผลในฟอร์มได้ (เช่น แปลงจาก Issue object เป็น id ในรูปแบบ string) ส่วนเมธอด reverseTransform() จะแปลงข้อมูลที่มาจากการ submit ไปเป็นรูปแบบตามที่เราต้องการ (เช่น แปลง id string กลับไปเป็น Issue object)

หากต้องการดูการเกิด validation error ให้ใช้ TransformationFailedException แต่ข้อความที่คุณส่งผ่าน exception นี้จะไม่ปรากฏแก่ผู้ใช้ คุณสามารถตั้งค่าข้อความนั้นด้วยตัวเลือก invalid_message (ดูด้านล่าง)

เมื่อมีการส่ง null ไปยังเมธอด transform() ตัว transformer ควรจะคืนค่าเทียบเท่ากับ type ที่เปลี่ยนไป (เช่น 0 เป็น integers หรือ 0.0 เป็น floats)

Using the Transformer

ขั้นต่อไป คุณจำเป็นต้องใช้ IssueToNumberTransformer object ภายใน TaskType โดยเพิ่มไปยัง issue field ซึ่งทำได้โดยเพิ่มเมธอด __construct() ดังตัวอย่าง

05

ที่นี้สามารถใช้ TaskType ได้ดังตัวอย่าง

06

เสร็จสิ้น ตอนนี้ user สามารถใส่ issue number ไปใน text field ได้แล้วและก็สามารถแปลงข้อมูลกลับไปเป็น Issue object ได้เช่นกัน นั่นหมายความว่า หากการ submit ข้อมูลจากฟอร์มสำเร็จ Form component จะส่งผ่าน Issue object จริง ๆ ไปยัง Task::setIssue() แทนการส่ง issue number ไป

ถ้าหาก Issue นั้นไม่มีอยู่ form error จะถูกสร้างขึ้นและแสดงข้อความ error message ที่เราได้ตั้งไว้ผ่าน invalid_message field option

ข้อควรระวังในการใช้ transfomers จากตัวอย่างข้างล่างเป็นตัวอย่างที่ผิด เพราะตัว transformer จะถูกนำไปใช้กับฟอร์มทั้งหมด แทนที่จะใช้เพียงแค่ field ใด field หนึ่ง 07

Creating a Reusable issue_selector Field

จากตัวอย่างข้างบน คุณได้ใช้ transformer กับ text field ธรรมดา แต่ถ้าหากคุณมีการใช้ transformation มากขึ้น มันจะดีกว่าไหมที่สร้าง custom field type ให้ทำงานอัตโนมัติ

เริ่มแรก สร้าง custom field type ขึ้นมา

08

จากตัวอย่างข้างบนจะแสดงผลเป็น text field (getParent()) และ data transformer จะทำงานอัตโนมัติ อีกทั้งการกำหนดค่า invalid_message option ก็ทำได้สะดวกยิ่งขึ้น

โดยตราบใดที่คุณใช้ autowire และ autoconfigure คุณสามารถเริ่มต้นใช้ฟอร์มได้ทันที

09

About Model and View Transformers

จากตัวอย่างข้างบน เป็นการ transformer ในลักษณะ "model" transformer ในความเป็นจริง transformers มีสองประเภทที่แตกต่างกันและมีข้อมูลพื้นฐานที่แตกต่างกันอีกสามประเภท

10

ในฟอร์มหนึ่ง ข้อมูลสามประเภทได้แก่

  1. Model data - นี่คือข้อมูลในรูปแบบที่ใช้ใน application ของคุณ (เช่น Issue object) ถ้าคุณเรียกใช้ Form::getData() หรือ Form::setData() แสดงว่าคุณกำลังทำงานอยู่กับ "model" data
  2. Norm Data - นี่คือ normalized version ของข้อมูลคุณโดยทั่วไปแล้วจะเหมือนกับ "model" data ของคุณ ไม่ได้ใช้กันทั่วไป
  3. View Data - นี่คือรูปแบบที่ใช้ในการกรอกข้อมูลในฟอร์ม รวมไปถึงข้อมูลที่ user submit มา โดยเมื่อคุณเรียกใช้ Form::submit($data) ตัวแปร $data นี้อยู่ในรูปแบบ "view" data

โดย transformers สองประเภทที่แตกต่างกันนี้ จะช่วยแปลงข้อมูลจากแต่ละประเภทข้อมูล

Model transformers:

View Transformer:

ซึ่งการใช้ transformer นั้นก็ขึ้นอยู่กับสถานการณ์ที่คุณเจอ

โดยการใช้ view transformer ให้เรียกใช้ addViewTransformer()

So why Use the Model Transformer?

ในตัวอย่างนี้ text field คาดหวังรูปแบบธรรมดา คือในรูปแบบ "norm" และ "view" ด้วยเหตุนี้ประเภทของ transformer ที่ควรจะเป็นคือ "model" transformer (ซึ่งแปลง to/from รูปแบบ norm - string issue number - ไปเป็นรูปแบบ model - Issue object)

ความแตกต่างระหว่างประเภทของ transformer เป็นเรื่องที่ละเอียดอ่อน โดยคุณควรคำนึงเสมอว่า "norm" data สำหรับ field นั้น ๆ ควรจะเป็นยังไง ตัวอย่างเช่น "norm" data สำหรับ text field คือ string แต่สำหรับ DateTime object ควรเป็น date field 01

SOURCE