12/08/2018, 15:57

Bài toán header của trang khi xuất file pdf

Tình hình vừa rồi trong dự án mình có gặp một yêu cầu hơi kì kì từ phía khách hàng, giải pháp thì cũng đã tạm gọi là có nhưng thực sự vẫn chưa hoàn hảo cho lắm. Mình xin giới thiệu ở đây để mọi người cùng thảo luận và góp ý. Bài toán Bài toán cụ thể là như thế này. Ta có đối tượng product gồm ...

Tình hình vừa rồi trong dự án mình có gặp một yêu cầu hơi kì kì từ phía khách hàng, giải pháp thì cũng đã tạm gọi là có nhưng thực sự vẫn chưa hoàn hảo cho lắm. Mình xin giới thiệu ở đây để mọi người cùng thảo luận và góp ý.

Bài toán

Bài toán cụ thể là như thế này. Ta có đối tượng product gồm các thông tin cơ bản và comments, và cần phải xuất file báo cáo dạng pdf với phần header của các trang là khác nhau. Cấu trúc file pdf yêu cầu như sau:

Các thông tin cơ bản của product sẽ hiển thị ở phần Content 1, còn danh sách comment sẽ hiển thị ở Content 2.

Nếu như phần Content 1 và Content 2 ngắn, chỉ nằm trên một trang thì không có vấn đề gì ở đây. Ta sẽ sử dụng thư viện dompdf để xuất file pdf từ template html. Tạo class PdfConverter như sau:

namespace AppServices;

use DompdfDompdf;
use DompdfOptions;
use IlluminateSupportFacadesStorage;

class PdfConverter {
    protected $view;
    protected $data;
    protected $filename;

    public function __construct($view = ', $data = null, $filename = ') {
        $this->view = $view;
        $this->data = $data;
        $this->filename = $filename;
    }

    public function save() {
        $dompdf = new Dompdf;
        $html = view($this->view, $this->data)->render();
        $options = new Options;

        $options->setIsPhpEnabled(true);
        $options->setIsRemoteEnabled(true);
        $options->setIsFontSubsettingEnabled(true);
        $options->setIsHtml5ParserEnabled(true);

        $dompdf->setOptions($options);
        $dompdf->setPaper('A4', 'portrait');
        $dompdf->loadHtml($html);
        $dompdf->render();
        Storage::disk('local')->put($this->filename, $dompdf->output());
    }

    public function setView($view) {
        $this->view = $view;

        return $this;
    }

    public function setData($data) {
        $this->data = $data;

        return $this;
    }

    public function setFilename($filename) {
        $this->filename = $filename;

        return $this;
    }
}

Sau đó tạo file template report.blade.html:

<div class="main">
    <div class="header">
        Header 1
    </div>
    <div class="conent content_1">
        {{ $product->info }}
    </div>
    <div class="page-break"></div>
    <div class="header">
        Header 2
    </div>
    <div class="conent content_2">
		@foreach $product->comments as $comment
			{{ $comment->content }}
		@endforeach
    </div>
</div>

Cuối cùng là sử dụng class PdfConverter và template report.blade.html để xuất file pdf:

$this->pdfConverter
         ->setView('report.blade.html')
         ->setData($product)
         ->setFilename('report.pdf')
         ->save();

Nếu số lượng comment của chúng ta nhỏ phần Content 2 chỉ nằm trên một trang thì không có vấn đề gì xảy ra. Nhưng nếu ta có nhiều comment thì sao, khi đó phần Content 2 sẽ hiển thị sang trang 3, 4, 5... của report. Và khách hàng yêu cầu các trang 3, 4, 5... này cũng có header giống như của trang 2. Như vậy cách làm cũ không giải quyết được vấn đề nhỉ, vì các trang mới kia đâu có header. Tìm cách giải quyết khác thôi.

Hướng giải quyết

Ta sẽ sử dụng thư viện Setasign/FPDI để xử lý vấn đề này. Đây là thư viện cho phép chúng ta sử dụng một file pdf có sẽ làm template để từ đó có thể thao tác chỉnh sửa trên đó theo ý muốn. Với bài toán ở trên, thứ tự các bước làm là như sau:

  1. Tạo file pdf giống như ở trên, nhưng không có phần comment
  2. Sử dụng file pdf làm template
  3. Đọc trang 1 từ template vào
  4. Đọc trang 2 từ template vào
  5. Bổ sung thêm lần lượt từng comment vào trang 2. Trong quá trình bổ sung comment, chiều dài trang 2 vượt quá chiều dài tờ giấy A4 thì tạo một trang mới giống trang 2 của template (có phần header) rồi sau đó tiếp tục thêm comment vào trang mới này.

Lý thuyết là như thế còn sau đây là chi tiết             </div>
            
            <div class=

0