Skip to main content

Setup ReportFrame trong code

Không chỉ setup ở csdl không, ta cũng cần setup trước một bảng đã lọc dữ liệu ở code để truyền vào khung báo cáo

Mở đầu

Setup ngắn nhất ta có thể viết mà không phải lo nghĩ nhiều (Note: $year trong đây lấy từ năm của bộ lọc)

$filterTableSql = "CREATE TEMPORARY TABLE filtered_data 
            /* Code tạo bảng ... */";

DB::statement($filterTableSql);

$frame = new ReportFrame("report_glm_24_s90h", null, ["yyyy" => $year]);
$frame->execute("filtered_data");
$data = $frame->returnData();

//...Xử lý nốt $data trả về và trả báo cáo

Nhìn tổng thể, khi tạo ReportFrame, hàm constructor của nó chỉ lấy 4 tham số sau (các dấu ? đứng trước nghĩa là nó null/không cần truyền vào cũng đc)

<?php
$frame = ReportFrame($tableName = <tên-bảng-lấy-khung-trong-csdl>,
    ?array $options = [<mảng-các-option-có-thể-dùng-để-tinh-chỉnh-ReportFrame>],
    ?array $parameters = [<các-tham-số-truyền-vào-cho-ReportFrame>],
    ?array $middlewares = [<các-middleware(các-func-custom)-giữa-các-quá-trình-chạy-trong-ReportFrame>]
)

Nội dung của page này sẽ đi vào một số option và middleware hay dùng. Đối với api đầy đủ xem mục API ở đây

Setup mảng $option

Logging

Mục tiêu: Xem các câu lệnh trước/sau khi ReportFrame query, giúp dev debug và cải thiện tính năng

Cách dùng: Thêm 1 trong các khóa sau vào mảng $options khi khởi tạo ReportFrame

  • LogAll: Bật tất cả tính năng log
  • LogQuery: In tất cả Query để Static = 0 (bao gồm cả cột lẫn where)
  • LogWhere: Tương tự như LogQuery nhưng chỉ bao gồm phần WHERE
  • LogUnion: Log toàn bộ đoạn UNION khi bật option DoUnion

Hành vi: Sử dụng Log::info(), mỗi hàng được in bao gồm cả tên ItemID đính kèm ở đầu

Lọc hàng

Mục tiêu: Khi dev muốn chia bảng frame gốc trên csdl thành các bảng con 

Cách dùng: Thêm khóa "FilterWhere" => "<điều-kiện-sql>" trong $options

Hành vi: Lưu ý rằng FilterWhere lọc trước khi tạo bảng tạm, có nghĩa là các tiền điều kiện từ hàng cha sẽ KHÔNG ĐƯỢC XỬ LÝ, khi sử dụng dev nên cân nhắc bao trọn cả hàng cha.

Lọc cột

Mục tiêu: Khi dev muốn setup các điều kiện cột

Cách dùng: Thêm khóa "ColumnConditions" với format sau

[
  //các option khác...
  "ColumnConditions" => [
    "<tên-cột>" => "điều-kiện-SQL",
    //v.v....

    //VD:
    "I1" => "CateNo = 111", //chọn loại có mã 111 theo 1 đặc tả nào đó
  ]
]

Hành vi: ColumnConditions có tính chất global trên toàn báo cáo, để phân nhỏ điều kiện cột, xem các phối hợp phía dưới

Setup động cột

Mục đích: Sinh động các ô và các cột dựa trên dữ liệu và điều kiện, cho phép tạo 1 bảng 2 chiều tùy ý điều kiện

Cách dùng: Thêm khóa "DynamicColumns" trong $options rồi trong đó, định nghĩa các cột theo format dưới đây

//Tên cột trong bảng truyền vào, bắt buộc phải có dạng DynCol_* để ReportFrame 
//có thể tối ưu trong quá trình xử lý
"<columnName>" => [
  //Bắt buộc phải có 3 khóa chính sau
  "Key" => <columnKey> //Lấy động cột nào trong bảng lọc trước $filteredData,
  "Condition" => <sqlCondition> // điều kiện SQL của cột động này
  "Name" => <columnName> //giá trị hiển thị của cột này 
  //(dùng cùng cột với Key ở trên hoặc dùng cột khác, có thể dùng công thức SQL)
  
  //Những cái dưới đâu có thêm vào hoặc không:
  //dành cho các báo cáo động cột nhóm (xem F102-1)
  "CreateGrouping" => [
    "Name" => <newColumnName>, //tên cột mới nhóm vào
    "Grouping" => <groupedByColumnNames>, //dùng (các) cột nào để nhóm giá trị 
      //(không để mặc định, nên dev cần viết lại Key nếu không thêm điều kiện grouping gì nữa
    "Method" => <aggregationMethod> //cách tính cột này nhóm theo phép gì: SUM, AVG, COUNT. (mặc định SUM)
    "IsLast" => <boolean> //nếu có khóa này trong mảng thì sẽ để cột mới ở cuối mỗi nhóm, 
                          //ngược lại thì sẽ mặc định là ở đầu mỗi nhóm
  ], 
]

Hành vi: Tạo bảng cross_entries (lưu các ô chứa dữ liệu) và cols_tbl (lưu các cột), khi trả về thì trả như sau

return
  [
    //...các dữ liệu trả về khác
    "cols" => DB::table('cols_tbl')->orderByRaw('TableID, Value, Norder')->get(),
    "cross_entries" => DB::table('cross_entries')->orderByRaw('TableID, ColumnKey, Norder')->get()
  ]

Hiện tại, bởi đang cố định sắp xếp trong cross_entries và cols, dev cần cân nhắc setup khi gặp bài toán yêu cầu cột phải sắp xếp theo một thứ tự đặc biệt nào đó

Có option "KeepDynColsTogether" nếu muốn đồng bộ các cột động với nhau (xem F02 trong SBS TT24)

Setup động báo cáo

Mục đích: Nhiều báo cáo yêu cầu phải hiển thị từng nhóm dữ liệu theo từng nhóm đối tượng nào đó, ReportFrame giản hóa bằng việc tự động thêm GROUP BY vào các câu lệnh query khi cần thiết, tính năng này integrate với các tính năng trên

Cách dùng: Dev chỉ cần thêm khóa sau trong ReportFrame

[
  //các option khác...
  "DynamicFor" => [
    //danh sách các id của đối tượng muốn động theo
    "ProjectID", //other...
  ]
]

Hành vi: Thêm các cột kể trong DynamicFor trong quá trình xử lý của ReportFrame

Thêm tham số cho ReportFrame

ReportFrame định nghĩa cho các tham số trong bảng tạm bằng %{tên-tham-số}, thí dụ trong 1 trong các RowCondition của bảng setup s90h tt24:

{
    "$>cascade": {
        "PostDate": {
            "$in": [
                "%yyyy-01-01",
                "%yyyy-01-04",
                "%yyyy-01-07",
                "%yyyy-01-10",
                "%yyyy-12-31"
            ]
        }
    }
}

Để có thể chuyển 1 hoặc nhiều tham số trong quá trình ReportFrame xử lý thì dev phải add tương ứng {tên-tham-số} trên vào mảng $parameters khi khởi tạo ReportFrame. VD như ở trên thì sẽ là

$frame = ReportFrame(
  "<tên-bảng-setup>",
  "<các-option>",
  [
    "yyyy" => $filter['Year'] //truyền năm từ bộ lọc vào
  ]

Hơn nữa, đối với trường hợp nhiều điều kiện cột khác nhau, có thể phối hợp với middlehook phía dưới và option "ColumnConditions" ở trên để tạo nhiều điều kiện cột.

Middleware hook

Giống với các thư viện/framework khác, ReportFrame cho phép gán các hook thực hiện 1 số câu lệnh custom từ dev giữa quá trình chạy trong ReportFrame

Cách sử dụng của nó đơn giản như sau:

[
  "tên-hook" => function() {
    //code của dev
  }
]

Danh sách các hook cho phép hiện tại

Tên Vị trí Mục đích
BeforeQuery Sau khi query cho phần chưa động cột (report_data) đã được tạo xong nhưng chưa được gọi để lấy dữ liệu. Chỉnh sửa query lần cuối trước khi gọi và lấy dữ liệu
BeforeDynColQuery Sau khi query cho phần động cột (cross_entries) đã được tạo xong nhưng chưa được gọi để lấy dữ liệu.

Dành cho động cột

Xử lý lần cuối query cho phần động cột trước khi lấy dữ liệu

BeforeCreateCols Trước khi query tạo bảng cột (cols_tbl) được chạy

Dành cho động cột

Xử lý trước khi tạo bảng, bắt các option phải xét cột dựa trên tham số

BeforeReturnData Trước khi trả về dữ liệu (trước cả đoạn sắp xếp, cũng như lọc bỏ 1 số cột thừa) Update xử lý 1 số cột theo điều kiện đặc biệt nào đó (VD: update STT, ...)