Export table sang PDF, tưởng chừng dễ mà khó không tưởng

debug Apr 09, 2021

Vào một ngày đẹp trời, trăng thanh gió mát. Tôi được tham gia vào một dự án trên nền tảng Zend framwork 1, tưởng chừng đơn giản sau khi họp kickoff như thêm phần này, sửa phần kia và một phần khá quan trọng đó là xử lý export dữ liệu sang dạng PDF. Nhưng câu chuyện không suôn sẻ như tôi nghĩ, anh chị em cùng theo dõi nhé


Dường như như việc export PDF khá là quen thuộc với các anh chị em phải không. Không, mọi thứ thật sự không phải màu hường cánh sen mà dần chuyển sang màu bùn sạm sau khi tôi gặp phải trường hợp này.

Tôi bắt đầu sử dụng thư viện TCPDF cho dự án lần này, anh chị em đừng hỏi tại sao lại dùng, cũng do cơ duyên sau khi tìm kiếm các thư viện hỗ trợ export PDF và hỗ trợ tiếng Nhật thì bé này được tôi lựa chọn, và cũng thấy git của bé cũng khá nhiều "sờ ta" tận (2.8k) lận. 😎

Câu hỏi: Với phần export dữ liệu và hiển thị dạng table lên PDF, anh chị em sẽ định làm như thế nào? (suy nghĩ 30s đã nào)

Đa số các anh chị em đều sẽ nghĩ tới cách làm là xuất dữ liệu đó ra 1 view HTML xong rồi từ view đó, chúng ta sẽ chuyển đổi thành dạng PDF phải không

html-to-pdf

👌OK, cách làm này khá là hoàn hảo đấy, và chính tôi cũng bắt đầu công việc với cách làm như vậy 😙. Sau vài chục tiếng (chia đều cho mấy ngày làm việc😄) thành quả cũng khá là được của nó.

File pdf ngon, hiển thị dữ liệu ngọt, mọi thứ tưởng chừng như ngọt nước (甘いー水) và kết thúc dự án tại đây.

Nhưng ôi không, điều tồi tệ đầu tiên xảy ra

Khách báo khi xuất với dữ liệu lớn thì quá trình bị timeout vì xử lý quá lâu, sau một hồi điều tra tôi đã phát hiện là quá trình render từ HTML sang PDF xử lý khá lâu khi file view chứa một lượng HTML quá lớn, dẫn tới làm thời gian xử lý lâu.

html-to-pdf-timeout

🤔 Vậy khi không làm việc với view như vậy, thì tạo file như nào bây giờ ? 🤔

May thay, thư viện TCPDF có hỗ trợ tạo Cell, sau khi ngồi suy nghĩ tôi nảy ra ý tưởng đó là thay vì cố vẽ cả 1 table ra thì sẽ ghép các ô nhỏ nhỏ để thành một table lớn như hình bên dưới.

Đây là đoạn code:

$pdf->Cell(<chiều cao>, <chiều rộng>, <văn bản>)

Đây là ý tưởng:

each_cell_as_table

Tôi đã phải thử nghiệm để xem với dữ liệu lớn tốc độ xử lý ra sao. Tuyêt vời, với dữ liệu lớn tốc độ xử lý được cải thiện đáng kể với khoảng 50 - 100 trang PDF được render.

Tôi bắt tay vào xử lý lại để hiển thị dữ liệu theo cách này, cũng mất khá nhiều thời gian để viết lại như vậy, và kết quả mà đạt được cũng không tồi, Tốc độ export file PDF diễn ra khá nhanh, nhanh hơn nhiều so với việc sử dụng view để render thành PDF. Nghĩ trong đầu, quả này xong rồi nhé. 🤑🤑

Nhưng không, vấn đề tiếp theo xảy ra.

Khách báo lại báo về, các text dài quá sẽ bị vượt ra ngoài các ô:

text_too_long

Lại lật lại vụ án, tìm hiểu thủ phạm thì việc sử dụng $pdf->Cell sẽ hiển thị một đoạn text mà không thể xuống dòng mà phải dùng $pdf->MultiCell. Vậy là một vấn đề nữa lại được giải quyết.

Nào tưởng chừng mọi thứ sẽ trở nên hoàn hảo, cuộc đời sẽ màu hường từ đây...

Tương lai lại vút bay, bug lại đổ về

over_page

Lần này, khách hàng báo phần hiển thị dữ liệu bị tràn sang trang khác, và yêu cầu phải sửa chữa lại sao cho mọi thứ không được vượt quá từng trang.

Phải làm sao đây, phần này có thể coi là phần khó nhất trong các bug vừa rồi, gặp bug chúa rồi đây. (Haha, game ư, gặp trùm cuối là phá đảo đó 🎮🎮🎮)

Sau thời gian vò đầu, bứt tai, đêm không ăn ngày không ngủ thì cuối cùng tôi cũng tìm ra giải pháp

calculate
(1), (2), (3) chính là các trường hợp từng hàng cell tiếp theo sẽ được ghi vào PDF

Giải pháp của tôi là gì:

Trước mỗi lần ghi từng hàng cell sẽ kiểm tra xem, hàng cell này có bị vượt quá độ dài của trang hiện tại hoặc nằm trong khoảng không gian được cho phép (space left), nếu vượt quá thì ngay lúc này ngắt trang và sang trang mới ngay lập tức.

Nhìn vào hình trên tôi phải tính toán với độ cao của "từng hàng cell tiếp theo", với (1) và (2) thì xử lý vẫn vẽ hàng đó vào trang PDF hiện tại vì vẫn nằm trong chiều cao của trang

Nhưng với trường hợp (3), hàng tiếp theo nếu vẽ vào sẽ bị quá chiều cao của trang, vậy nên trường hợp này cần ngắt trang và vẽ sang trang mới

Bingo, phá đảo. Cuối cùng thì vấn đề khó nhất cũng được giải quyết, con bug chúa này tốn khá nhiều thời gian và suy nghĩ của tôi và đó chính là con bug làm tôi phải đi làm ngày Chủ nhật đầu tiên trong sự nghiệp của mình.

Kết luận:

Anh chị em có thể đọc bài này trong vòng 1 đến 2 phút, nhưng khi bắt tay làm thì tôi đã mất khá nhiều thời gian cho việc xử lý export này, nên có thể coi đây là một chia sẻ khá thú vị của Tôi tới anh chị em.

Tôi hy vọng, với bài chia sẻ kinh nghiệm diệt bug chúa này, mong anh chị em sẽ bổ sung chút kinh nghiệm vào vốn kiến thức của mình, và không phải sử dụng tới ngày Chủ nhật nhé :v

Cuối cùng, cảm ơn anh chị em đã lướt qua bài viết của tôi, hẹn gặp lại ở các bài chia sẻ tiếp theo.
Nếu có gì thắc mắc hoặc chưa hiểu rõ, hãy liên lạc với tôi, luôn luôn lắng nghe, sẵn sàng chia sẻ dù đúng hay sai 💕💕💕.

Ký tên:
Người trong cuộc

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.