Sử dụng XForms để tạo công cụ kế toán, Phần 3: Phát triển chức năng quản lý tài sản

Xây dựng các biểu mẫu ngân sách và hoá đơn vào X-Trapolate

Loạt bài sáu phần này trình bày về cách làm thế nào tận dụng sức mạnh của XForms kết hợp với MySQL và PHP để hỗ trợ tiến trình tạo ra một công cụ kế toán trực tuyến được gọi là X-Trapolate. Tất cả công nghệ lập trình tốt đều xử lý một mảng các bài toán mà nó có thể giải quyết tốt. Loạt bài này nêu bật những vấn đề mà XForms giải quyết một cách hiệu quả, chẳng hạn như nhu cầu về những tính toán trực tuyến và tương tác cao. Phần 3 của loạt bài viết sáu phần này giải thích cách phát huy năng lực của XForms trong sự kết hợp với PHP và MySQL để tạo một vài công cụ giúp tương tác với dữ liệu nghiệp vụ hàng ngày. Các biểu mẫu (form) ngân sách và hoá đơn chúng ta sẽ xây dựng thành X-Trapolate trong bài viết này, sẽ giải thích một vài tính năng rất mạnh của XForms, đồng thời cũng nêu bật giới hạn khi mà sự phát triển XForms trở lên khó khăn.

Stony Yakovac, Kỹ sư phần mềm, 自由职业者

Stony Yakovac là một kỹ sư và là một nhà viết sách tự do, ông đang sống tại Lava Hot Springs, bang Idaho, Hoa Kỳ. Ông làm cho rất nhiều dự án thuộc nhiều lĩnh vực, bao gồm các thiết kế về phần mềm và phần cứng số



10 07 2009

Trước khi bắt đầu

Bài viết này dành cho các nhà phá triển ứng dụng Web nghiên cứu công nghệ xử lý biểu mẫu XForms. Bài viết này là phần thứ ba của loạt bài viết sáu phần phát triển một bộ công cụ thanh toán cho một nghiệp vụ. Bài viết ngay trước đây đã giới thiệu một vài tính năng cơ bản của XForms có trong các khái niệm biểu mẫu HTML truyền thống.

Về bài viết này

Hầu như tất cả các nghiệp vụ có lợi nhuận đều cài đặt một mặt nào đó của quản lý thanh toán. Nghiệp vụ trong ví dụ làm rõ chức năng đó cho bài viết này (ta sẽ tạo một công cụ ngân sách cho X-Trapolate) tiến hành với giả thiết là khách hàng đưa ra đơn hàng chứa các món tách riêng và mỗi món đến từ một gian hàng riêng biệt. Mỗi đơn hàng đến từ một hoá đơn duy nhất và tiền lời từ mỗi món của một gian hàng trực tiếp góp phần vào lợi nhuận của gian hàng đó. Mỗi gian hàng cũng mua các thiết bị mà góp phần vào phí tổn của gian hàng đó. Quá trình lập ngân sách cho phép nhà quản lý tạo và huỷ các gian hàng, gian hàng con để thay đổi lợi nhuận dự trù, phí tổn dự trù của mỗi gian hàng hay để so sánh lợi nhuận và phí tổn dự trù đối với tổng hiện tại được xác định từ các đơn hàng và phí tổn đã ghi chép lại.

Công cụ thanh toán, cũng sẽ được tạo ra trong bài viết này, với mục đích là một cơ cấu cho phép nhân viên thanh toán phát sinh ra hoá đơn cho các tài khoản của khách hàng, in biên lai, hay gửi tài khoản vào các tập hợp. Cơ sở dữ liệu của các giao dịch đầu tiên được nhắc đến trong bài viết trước của loạt bài viết này, sau khi nâng cấp, chứa thông tin đầy đủ để thanh toán và có thể được truy vấn cho thông tin đó. Ngoài ra, số nhận dạng liên kết các tài khoản với từng đơn hàng với số tiền phải trả.

Về loạt bài viết này

Mục đích của loạt bài viết này là giải thích cách sử dụng XForms trong quá trình phát triển các ứng dụng Web thực tế và để hướng dẫn người đọc các sử dụng XForms.

  • Phần 1 là sự giới thiệu tới toàn bộ loạt bài viết tổng quan hoá tất cả các bộ phận của kết quả cuối cùng và những khía cạnh nào có trong mỗi phần trong đặc tả XForms.
  • Phần 2 trình bày về đăng nhập và quản lý tài khoản.
  • Phần 3 trình bày việc phát triển những biểu mẫu gắn liền với việc quản lý tài khoản.
  • Phần 4 tiếp tục trình bày về việc quản lý tài khoản và việc báo cáo những khía cạnh tính toán khác nhau của một nghiệp vụ.
  • Phần 5 trình bày việc quản lý nợ và những cải tiến.
  • Phần 6 kết thúc loạt bài viết với bản tóm tắt các công cụ phát triển, và một vài đề nghị để cải tiến và tiếp tục làm việc với bộ công cụ.

Các điều kiện tiên quyết

Bài viết này sử dụng cơ sở dữ liệu MySQL để lưu trữ và tham chiếu. Các lệnh SQL cần thiết xuất hiện trong suốt bài viết, nhưng cũng đòi hỏi kinh nghiệm làm việc với MySQL. PHPMyAdmin cung cấp truy cập tương đương cho phép cấu hình cơ sở dữ liệu MySQL và xem các mục từ một giao diện thực đơn đồ hoạ.

Mặc dù mục đích của loạt bài viết là để rèn luyện người đọc về các ứng dụng của XForms, một vài kiến thức nền tảng là cần thiết. Có một vài bài viết và loạt bài giới thiệu rất tốt về XForms có từ developerWorks (xem Tài nguyên). XForms được xây dựng trên XML, và, do đó, một kiến thức căn bản về XML cũng phải được trang bị trước.

Các công nghệ và khái niệm khác cũng có thể tham gia, nhưng chúng sẽ có phạm vi hạn chế hơn và sẽ không ảnh hưởng tới khả năng nắm bắt chủ đề của người đọc. Một vài phần mềm cũng sẽ được đòi hỏi:

  • Một trình duyệt có khả năng hiển thị XForms, như là Firefox 2.0.1.
  • Một máy chủ Web với PHP được kích hoạt, như là WAMP
  • Một máy chủ SQL, MySQL, cũng là một phần của gói WAMP trong trường hợp này.

Tạo biểu mẫu ngân sách

Công cụ ngân sách có ý định đưa ra một giao diện người dùng để một người quản lý có thể hiểu và phát triển một kế hoạch ngân sách cho nghiệp vụ như một mục đích lâu dài. Phần này trình bày cho bạn cách thức để tạo một công cụ ngân sách sử dụng XForms với sự giúp đỡ của MySQL và PHP.

Khái quát

Khi người sử dụng lần đầu tiên nhìn thấy biểu mẫu ngân sách, nó hiện ra như ở Hình 1.

Hình 1. Trạng thái đầu tiên của biểu mẫu ngân sách
Trạng thái đầu tiên của biểu mẫu ngân sách

Một vài tính năng của XForms mà bài viết này chưa được nhắc đến xuất hiện trong hình này. Hộp ngày tháng là một tính năng mới, nó đóng vai trò giống như một hộp xổ xuống (drop-down box), nhưng người dùng có thể thay vào đó chọn một ngày trong bảng lịch hiện ra, như được thể hiện trong Hình 2.

Hình 2. Bảng lịch XForms để lựa chọn ngày
Bảng lịch XForms để lựa chọn ngày

Tiếp đó, bạn sẽ thấy các để viết mã kiểu dữ liệu ngày tháng và lựa chọn lịch.

Kiểu dữ liệu ngày tháng và lựa chọn lịch

Như đã vừa được đề cập, XForms cung cấp sự độc lập của mô hình dữ liệu đối với sự thể hiện như là một tính năng then chốt. Trình duyệt thực tế sẽ chọn một cách thể thiện mà có ý nghĩa đối với trình duyệt đó. Nó có thể cho rằng rằng định nghĩa đầu vào của ngày tháng có thể chứa một vài widget mới và riêng biệt cho phép nó thể hiện như một hộp chọn ngày tháng. Ví dụ 1 thể hiện đoạn mã cho các hộp ngày tháng.

Ví dụ 1. Định nghĩa hộp lựa chọn ngày tháng
   <xforms:input ref="instance('acctToolBdgtInst')/startDate"> 
      <xforms:label>Start Date</xforms:label>
   </xforms:input>

Chú ý rằng sự lựa chọn ngày tháng giống hệt như định nghĩa tên người sử dụng trong Phần 2. Trình duyệt nhận các thông tin để xử lý hộp này khác so với ở vị trí khác. Cơ chế để tạo hộp lựa chọn ngày tháng xuất hiện trong một khai báo bind tại khai báo mô hình XForms. Khai báo bind đang được nói đến nằm trong Ví dụ 2.

Ví dụ 2. định nghĩa một kiểu dữ liệu ngày tháng
<xforms:bind nodeset="instance('acctToolBdgtInst')/startDate" type="xsd:date"/>

Thuộc tính kiểu áp dụng cho nút startDate trong trường hợp (instance) của acctToolBdgtInst sử dụng khai báo. Khai báo của nút startDate như một xsd:date cho phép nó hiển thị như một hộp lựa chọn lịch. Trình duyệt thực hiện chức năng quyết định cách thức để hiển thị mục nhập thông tin, nhưng kiểu của nó sẽ chi phối quyết định này. Rất ít kiểu dữ liệu thay đổi sự kết xuất của các điều khiển biểu mẫu trên trình duyệt, nhưng một số kiểu dữ liệu có áp dụng. Khi công nghệ XForms phổ biến và trở lên hoàn thiện, các trình duyệt có thể thích nghi để đối xử đặc biệt với nhiều kiểu dữ liệu hơn. Trình duyệt đòi hỏi một mẩu thông tin then chốt khác cho phép thuộc tính kiểu mang bất kì ý nghĩa nào. Điểm này có thể khiến người sử dụng mới của XForms thất vọng. Đoạn mã đặt trong Ví dụ 3 định nghĩa các không gian tên được sử dụng trong một tài liệu XHTML. Sau một vài phép thử cho tới khi đúng, sự kết hợp riêng biệt này của khai báo không gian tên xsdxsd:date cho phép Firefox tạo ra một mục bảng lịch.

Ví dụ 3. định nghĩa một kiểu dữ liệu ngày tháng
<html
   xmlns="http://www.w3.org/1999/xhtml" xmlns:my="http://example.com/my"
   xmlns:xforms="http://www.w3.org/2002/xforms" 
   xmlns:ev="http://www.w3.org/2001/xml-events"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:xhtml="http://www.w3.org/1999/xhtml"
>

Tiếp đó, bạn sẽ khởi tạo một dữ liệu cá thể của biểu mẫu (form).

Khởi tạo dữ liệu cá thể cho XForms

Một tính năng ít rõ ràng hơn được thể hiện trong Hình 2 là thực thể hộp ngày tháng xuất hiện với ngày hiện tại trong nó. Cho dù tồn tại một vài lựa chọn để đặt giá trị vào các nút XML, hầu hết các lựa chọn đều không áp dụng tốt cho hành vi mong muốn trong trường hợp này, nghĩa là tải ngày hiện tại vào trong nút. Phần 2 giải thích một vài phương pháp như là đặt trực tiếp một giá trị vào khai báo XML của cấu trúc dữ liệu. Điều đó, cho thấy cấu hình nút đó thành một giá trị cố định khi khởi động và, khi ứng dụng bắt đầu ngày hôm sau, ngày tháng đó sẽ không còn là ngày hiện tại. Gắn và sử dụng thuộc tính calculate cho phép lập trình viên có thể chỉ thị trình duyệt đặt một giá trị cho nút; nhưng trong trường hợp này, gắn nút với now() sẽ sinh ra một điều khiển mà người dùng thực hiện một phép tính, người dùng ứng dụng chắc chắn không hiểu được điều này.

Sử dụng sự kiện XForms-ready

Nút chỉ cần một giá trị chứa trong nó tại thời điểm tải trang chứ sau đó thì không, trừ khi người dùng đặt giá trị. Đoạn mã trong Ví dụ 4 thể hiện mã XForms thực hiện hoạt động đó. Phần 2 giới thiệu một sự kiện gắn cho một trigger. Sự kiện DOMActivate xuất hiện bên trong khai báo của trigger trong biểu mẫu đăng nhập Ngân sách sử dụng một sự kiện khác khai báo ở vị trí khác. Sự kiện xforms-ready xảy ra khi trình duyệt đã tải hoàn thành cấu trúc XForms, nhưng trước khi quyền điều khiển được chuyển cho người dùng. Một thành tố khác trong xử lý XForms, thành tố setvalue sẽ hiện ra ở đây.

Ví dụ 4. Đặt giá trị ngày tháng tại sự kiện XForms-ready
    <xforms:action ev:event="xforms-ready">
       <xforms:setvalue ref="instance('acctToolBdgtInst')/startDate" 
                        value="now()"/>
      <xforms:setvalue ref="instance('acctToolBdgtInst')/endDate" 
                       value="now()"/>
    </xforms:action>
  </xforms:model>
</head>

Thành tố setvalue định giá trị thuộc tính và áp đặt giá trị đó vào nút chỉ ra bởi thuộc tính ref. Một hiện diện khác của setvalue tồn tại, nhưng với khả năng đặc biệt cho phép ứng dụng của XPath tính giá trị hàm now(), hàm sẽ trả về ngày tháng hiện tại. Đoạn mã nhỏ sau đây được trình bày trong Ví dụ 5 trích từ biểu mẫu thanh toán và giải thích ứng dụng của hàm XPath substring để cắt kết quả của now chỉ giữ lại ngày.

Ví dụ 5. Cắt now chỉ giữ lại ngày
<xforms:setvalue ref="instance('acctToolBillInst')/endDate"                       
                 value="substring(now(),1,10)"/>

Kiểm tra tình hợp lệ của dữ liệu và các kiểu dữ liệu XML Schema

Lý do biểu mẫu thanh toán đòi hỏi kết quả đã chỉnh sửa từ now đến từ kiểu dữ liệu đã định nghĩa cho trường. Kiểu dữ liệu XML Schema date thực hiện nhiệm vụ như một ràng buộc XML Schema cho dữ liệu trong trường đó. Firefox ước lượng tính hợp lệ của dữ liệu khi một sự kiện submit (gửi đơn) xảy ra. Bằng cách giữ nguyên kết quả không chỉnh sửa của now trong hộp ngày tháng, quá trình gửi đơn không phàn nàn về việc không thể hợp lệ hoá dữ liệu. Không may, khi điều đó xảy ra, đôi khi trình duyệt trả lời với một thông báo "Not able to validate," nhưng những lúc khác trình duyệt hoàn toàn không trả lời, để mặc người dùng nhấn nút lại nhiều lần và thắc mắc điều gì có thể đang xảy ra.

Biểu mẫu ngân sách không đặc biệt đòi hỏi ngày tháng được cắt bởi vì nó không sử dụng cá thể với ngày tháng trong nó để gửi đơn. Thực tế này được thể hiện trong Ví dụ 6. Nâng cấp câu truy vấn để sử dụng ngày tháng như các ràng buộc cho truy vấn đòi hỏi cá thể dữ liệu như một đối số, và do đó, đòi hỏi tính hợp lệ của XML Schema cho ngày bắt đầu và ngày kết thúc, đã được khai báo là xsd:date. Đối số hiện tại cho phương thức gửi đơn load_budget là một cá thể departmentsInst. Nút đối số, acctToolBdgtInst, chứa startDate và endDate, cả hai đều bị ràng buộc bởi xsd:date, đòi hỏi cắt giờ khỏi kết quả của now() trước khi phương thức gửi đơn được thực hiện.

Ví dụ 6. Tránh kiểm tra tính hợp lệ của XML Schema
      <xforms:instance id="acctToolBdgtInst">
         <formui xmlns="">
            <startDate/>
            <endDate/>
      ...
      </xforms:instance>
      ...
      <xforms:submission id="load_budget" 
                         action="get_budget.php" 
                         method="get"
                         replace="instance" 
                         instance="departmentsInst"
                         ref="instance('departmentsInst')" />
      ...

Tiếp theo, bạn sẽ thấy được biểu mẫu ngân sách sẽ trông như thế nào.

Cái nhìn đầu tiên tại các gian hàng

Khi người dùng kích hoạt nút Refresh, một hiển thị giống hoặc gần giống Hình 3 sẽ hiện ra. Mã XForm của nút Refresh chứa một trigger, kích hoạt cơ chế gửi đơn. Cài đặt này, về chức năng giống hệt một thành tố submit thông thường, thể hiện một biểu mẫu có thể có và sử dụng thành tố gửi đơn có hay không có một thành tố submit.

Hình 3. Lập ngân sách sau khi refresh
Lập ngân sách sau khi refresh

Sự khác nhau nhỏ giữa cài đặt submit và trigger nằm ở chỗ Firefox đạt các thành tố submit trên một dòng mới trừ khi được chỉ dẫn, còn đặt các thành tố trigger trên cùng dòng. Ví dụ 7 trình bày mã trigger.

Ví dụ 7. Tạo ra sự gửi đơn mà không cần thành tố submit
   <xforms:trigger ref="instance('acctToolBdgtInst')/loadBudget">
      <xforms:label>Refresh</xforms:label>
      <xforms:action ev:event="DOMActivate">
         <xforms:dispatch name="xforms-submit" target="load_budget"/>
      </xforms:action>
   </xforms:trigger>

Tiếp theo bạn sẽ thấy bạn có thể sử dụng các thành tố action và dispatch trong XForm như thế nào.

Sử dụng action và dispatch trong XForms

Biểu mẫu ngân sách có thể thực thi tốt với một thành tố XForm submit, nhưng sử dụng trigger đem đến một lựa chọn thú vị. Thành tố action trong XForms thực hiện các thành tố chứa bên trong nó liên tiếp. Sử dụng cơ cấu này, một nút bấm duy nhất có thể chi phối nhiều cơ chế gửi đơn hay thực hiện nhiều thao tác. Thành tố dispatch phục vụ như một người điều khiển một sự kiện. Bằng cách sử dụng dispatch, một biểu mẫu XForm có thể thực hiện gần như mọi hành động. Trường hợp này, thể hiện trong Ví dụ 7, đã sử dụng sự kiện xforms-submit, khi được chỉ đến đích load_budget khiến cho phương thức gửi đơn của load_budget thực thi. Thành tố gửi đơn load_budget được thể hiện trong Ví dụ 6.

Tiếp đến, bạn sẽ quan sát một số dữ liệu cá thể XML cho biểu mẫu ngân sách.

Cấu trúc dữ liệu XML của biểu mẫu ngân sách

Tới đây, giới thiệu cấu trúc giữ liệu cho các gian hàng là cần thiết. Ví dụ này sử dụng một cấu trúc dữ liệu phả hệ của các gian hàng trong XML. Ví dụ 8 trình bày một ví dụ ngắn gọn cách thức XML được cấu trúc. Cấu trúc phả hệ này cho phép quá trình tính toán thích hợp với từng gian hàng và gian hàng con của nó. Những cài đặt thay thế đặt tất cả các nút XML cùng một cấp nhưng bổ xung các nút thông tin để tạo khái niệm của phả hệ. Quá trình tính toán trở nên phức tạp tới mức bất khả thi đối với phương pháp đó.

Ví dụ 8. Phả hệ XML department
<?xml version="1.0" encoding="UTF-8"?>
<budgetForm xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events">
  <departments>
    <maxid/>
    <hidden>0</hidden>
    <department id="9">
      <name>United Cabinetry And Millwork</name>
      <department id="3">
        <name>Boxgoods</name>
        <department id="4">
          <name>Blanket Chests</name>

Phương pháp phả hệ có một số sự khó khăn đáng quan tâm. Sự khó khăn chính đến từ điều có thể làm gần như xong. Đoạn mã thể hiện trong Ví dụ 10 tạo ra hiển thị trong Hình 3. Ví dụ 9 trình bày một phiên bản lặp lại đơn giản hơn nhiều, phiên bản mà cuối cùng không làm việc được bởi vì các thành tố lặp lại coi nodelist như một cấu trúc thẳng, song song mà có thể được chọn với một chỉ số nguyên cho các thao tác như là chèn (insert). Mã nguồn Ví dụ 9 sử dụng biểu thức XPath // để tìm kiếm theo phả hệ tất cả các nút dưới các gian hàng tên là department và sử dụng chúng như tập nodeset. Cuối cùng, Ví dụ 10 cung cấp một mô hình làm việc nơi mà mỗi tập nút lặp lại sử dụng tất cả các nút gian hàng tại một cấp duy nhất mà không sử dụng nút nào bên dưới. Phương pháp này cho phép quá trình chèn một nút mới vào cây department.

Ví dụ 9. Hiển thị phả hệ có chiều sâu động department
   <xforms:repeat id="budgetRpt" 
                  nodeset="instance('departmentsInst')/departments//department" 
                  incremental="true">

Các thành tố lặp lại lồng nhau

Ví dụ 10 thể hiện cách thức sử dụng một cáu trúc lặp lại lồng nhau trong XForms như thế nào. Ví dụ này sử dụng một bộ lồng nhau 3 cấp. Mỗi lần lặp có nhiều thành tố XForms khác và chứa một thuộc tính ID. Thuộc tính ID cung cấp truy cập tới chỉ số của cấu trúc lặp cụ thể. Lặp của XForms chạy rất giống các điều khiển biểu mẫu (form) dựa trên tương tác người dùng trực tiếp hơn khác. Một hành vi quan trọng để hiểu về lặp là dòng cuối cùng người dùng nhấn lên được lưu nhớ lại cho lặp đó và có thể lấy được bằng một hàm. Sau đó chỉ số này có thể được sử dụng để trình bày chức năng chèn (insert).

Ví dụ 10. Hiển thị department có chiều sâu mã cố định
   <xforms:repeat id="budgetRpt" 
                  nodeset="instance('departmentsInst')/departments/department" 
                  incremental="true">
     <table width="750px">
       <tr>
         <td width="10%">
           >
         </td>
         <td width="50%">
           <xforms:textarea ref="name"/>
         </td>
         <td width="10%">
           <xforms:textarea ref="planExp"/>
           <xforms:output ref="planExpDisplay"/>
         </td>
         <td width="10%">
           <xforms:textarea ref="planPro"/>
           <xforms:output ref="planProDisplay"/>
         </td>
         <td width="10%">
           <xforms:output ref="curExp"/>
         </td>
         <td width="10%">
           <xforms:output ref="curPro"/>
         </td>
       </tr>
     </table>

     <xforms:repeat id="budgetRpt2" nodeset="department" incremental="true">
       <table width="750px">
         <tr>
           <td width="10%">
             —>
           </td>
           <td width="50%">
             <xforms:textarea ref="name"/>
           </td>
           <td width="10%">
             <xforms:textarea ref="planExp"/>
             <xforms:output   ref="planExpDisplay"/>
           </td>
           <td width="10%">
             <xforms:textarea ref="planPro"/>
             <xforms:output ref="planProDisplay"/>
           </td>
           <td width="10%">
             <xforms:output ref="curExp"/>
           </td>
           <td width="10%">
             <xforms:output ref="curPro"/>
           </td>
         </tr>
       </table>

       <xforms:repeat id="budgetRpt3" nodeset="department" incremental="true">
         <table width="750px">
           <tr>
             <td width="10%">
               ——>
             </td>
             <td width="50%">
               <xforms:textarea ref="name"/>
             </td>
             <td width="10%">
               <xforms:textarea ref="planExp"/>
               <xforms:output ref="planExpDisplay"/>
             </td>
             <td width="10%">
               <xforms:textarea ref="planPro"/>
               <xforms:output ref="planProDisplay"/>
             </td>
             <td width="10%">
               <xforms:output ref="curExp"/>
             </td>
             <td width="10%">
               <xforms:output ref="curPro"/>
             </td>
           </tr>
         </table>

       </xforms:repeat>
     </xforms:repeat>
   </xforms:repeat>

Một tính năng quan trọng nữa của cấu trúc lặp là sự tái định nghĩa của ngữ cảnh hiện tại cho các truy vấn XPath. Cấu trúc lặp kép (two-repeat) thứ 2 sử dụng tập nút department. Mặc dù chúng ám chỉ cùng một văn bản, nhưng mỗi cái chọn một tập các nút khác nhau. Lý do cho điều này là sự tái định nghĩa tăng dần của ngữ cảnh vị trí hiện tại. Bởi vì các biểu thức XPath không chứa lời gọi tới instance() hoặc một tiếp đầu tố / hay một vài thành phần bổ nghĩa khác, chúng được tham chiếu tới ngữ cảnh hiện tại, chính là một trong các nút được chọn bởi biểu thức XPath trong thuộc tính nodeset của thành tố repeat.

Điều khiển biểu mẫu Textarea

Một điều khiển hiển thị biểu mẫu mới của XForms được thể hiện trong Ví dụ 10. Điểu khiển textarea cho phép người sử dụng nhập nhiều dòng dữ liệu. Nhấn Enter trong một điều khiển biểu mẫu textarea nhảy tới dòng văn bản tiếp theo chứ không phải để phát sinh một hành động. Trong trường hợp này, thành tố input có vẻ là giải pháp thích hợp hơn. Tuy nhiên, như Hình 4 trình bày, kết quả của việc thay thế các thành tố textarea với các thành tố input cho một hình ảnh không đẹp lắm. Chú ý rằng sự gióng hàng của tên các gian hàng trở nên xiên lệch và các con số gióng sang trái trong input và gióng phải nếu là các giá trị.

Hình 4. Sử dụng input thay thế cho textarea
Sử dụng input thay thế cho textarea

Cũng phải nhớ rằng textarea được điểu khiển bằng cách sử dụng CSS sau, như đã được thể hiện trong Ví dụ 11.

Ví dụ 11. Điều khiển diện mạo của các hộp nhập dữ liệu textarea
    <style type="text/css">
      textarea { font-family: sans-serif; height: 1.2em; width: 90%; }
    </style>
    <xforms:model id="acctToolBillModel" >
...

Đoạn CSS trên làm cho các hộp textarea gióng thẳng nhau gọn gàng, như bạn đã thấy trong Hình 3.

Tiếp theo, bạn sẽ thấy cách thức để chèn thêm các thành tố department mới.

Chèn thêm các gian hàng mới

Hàm chèn (insert) trên biểu mẫu ngân sách thực thi như được thể thiện trong Hình 5. Thành tố chèn (insert) thực hiện thao tác tạo một nút dữ liệu trong cấu trúc dữ liệu XML. XForms tái xử lý cấu trúc dữ liệu và cập nhật nội dung hiển thị để phản ánh nút dữ liệu mới. Thành tố chèn (insert) đòi hỏi hai nút, một nodeset, và một chỉ số để chèn thành tố mới. Sự cảnh báo của chèn (insert) đến từ thực thế rằng nút mới mà chèn (insert) tạo ra được tạo bởi nhân bản nút cuối cùng trong tập nút. Hai khó khăn tiềm tàng nảy sinh từ cách thức xử lý này. Khó khăn đầu tiên đến từ dữ liệu có thể đã hoặc chưa nằm trong nút được nhân bản. Trong trường hợp ngân sách, một gian hàng cấp thứ hai có thể có hoặc không một gian hàng cấp thứ ba dưới nó.

Hình 5. Chèn một gian hàng
Chèn một gian hàng

Khó khăn thứ hai hiện ra như là một kết quả của vấn đề đầu tiên. Nếu không có nút nào để bắt đầu, không có gì có thể được tạo ra bởi vì không có gì để nhân bản. Những các xử lý này tạo ra một vài thử thách đáng quan tâm trong phát triển ứng dụng XForms với XForms 1.0, nhưng trong trường hợp này nếu người dùng muốn một gian hàng cấp ba, câu trả lời là phải chèn một gian hàng cấp 2 khác, mà sẽ có một gian cấp 3 gắn vào nó miễn phí, đáng khen cho một nút bù nhìn được sử dụng cho mục đích đặc biệt. Mã chèn (insert) cấp thứ ba được trình bày trong Ví dụ 12.

Ví dụ 12. Third-level chèn (insert)
<xforms:trigger ref="instance('acctToolBdgtInst')/newDept3rd">
  <xforms:label>Add New 3rd Level Department</xforms:label>
  <xforms:action ev:event="DOMActivate">
     <xforms:insert nodeset="instance('departmentsInst')/departments/department
          [index('budgetRpt')]/department[index('budgetRpt2')]/department"
          at="index('budgetRpt3')" position="after"/>
     <xforms:setvalue ref="instance('departmentsInst')/departments/department[index
     ('budgetRpt')]
           /department[index('budgetRpt2')]/
           department[index('budgetRpt3')+1]/@id"
           value="instance('departmentsInst')//departments/maxid"/>             
     <xforms:setvalue ref="instance('departmentsInst')/departments/department
     
         [index('budgetRpt')]/department[index('budgetRpt2')]/department[index
         ('budgetRpt3')+1]/name"
                              value="'Enter new department name'"/>
     <xforms:setvalue ref="instance('departmentsInst')/departments/department
      [index('budgetRpt')]/department[index('budgetRpt2')]/department[index
      ('budgetRpt3')+1]/planPro"
                              value="0"/>
     <xforms:setvalue ref="instance('departmentsInst')/departments/department
      [index('budgetRpt')]/department[index('budgetRpt2')]/department[index
      ('budgetRpt3')+1]/planExp"
                              value="0"/>
     <xforms:setvalue ref="instance('departmentsInst')/departments/department
  [index('budgetRpt')]/department[index('budgetRpt2')]/department[index
  ('budgetRpt3')+1]/ordersTotal"
                              value="0"/>
     <xforms:setvalue ref="instance('departmentsInst')/departments/department
  [index('budgetRpt')]/department[index('budgetRpt2')]/department[index
  ('budgetRpt3')+1]/expensesTotal"
                              value="0"/>
  </xforms:action>
</xforms:trigger>

Các bộ lọc XPath, chỉ số lặp, và đặt-giá-trị (setvalue)

Ví dụ 12 chứa rất nhiều mã; dù sao, sự kiểm tra kĩ hơn cho thấy rằng phần lớn mã thực hiện các chứ năng giống hệt nhau, để lại hai điều cần bàn là chèn (insert) và nhiều thuộc tính setvalue. Thành tố chèn (insert) thực hiện như đoạn trước đã đề cập đến; nó sao chép một nút và đặt bản sao mới vào cấu trúc dữ liệu. Chèn (insert) cần một nodeset, và một vị trí hay chỉ số. Biểu thức nodeset, một biểu thức XPath, chiếm hầu hết kí tự trắng trong Ví dụ 12, trông như một con yêu quái. Tuy nhiên, biểu thức chỉ đơn thuần phục vụ để đánh chỉ số xuống tới nút cấp ba sử dụng chỉ số từ các thành tố repeat đã được bàn trước đó. Một nút nên được tạo có toán tử [] trong một biểu thức XPath không thực hiện điều mà một toán tử [] điển hình của hầu hết ngôn ngữ làm. Trong XPath, toán tử [] phục vụ như một bộ lọc. Trong trường hợp này, một số đơn được đặt vào bộ lọc để chọn chỉ số đó; dù sao, rất nhiều thứ có thể được đặt vào bộ lọc.

Các dòng setvalue phục vụ để khởi tạo dữ liệu. Như đã đề cập ở trước, vì dữ liệu là một bản sao, nó có thể có những phần thông tin mà hoặc là không được áp dụng, hay thậm chí có thể gây rắc rối. Ở đây, một trong các khó khăn là duy trì các ID duy nhất của gian hàng. Điều đó được giải quyết bằng cách sử dụng giá trị maxId trong nút cao nhất của các gian hàng. Nó lần lượt được gắn với một hàm XPath để trở thành maxId (cộng thêm một) trong toàn bộ tập nút.

Tiếp đó, bạn sẽ cài đặt mã để bỏ đi các nút cấp 2 đã tồn tại.

Bỏ đi các nút

Ví dụ 13 thể hiện đoạn mã cho các nút bấm bỏ nút đi. Đơn giản hơn rất nhiều so với tính năng chèn (insert), bỏ nút sử dụng chỉ số lặp. Thành tố delete xoá cấu trúc dữ liệu XML được chỉ ra và, do đó, dữ liệu được hiển thị liên kết với thành tố đó sẽ bị xoá bỏ.

Ví dụ 13. Bỏ đi các nút cấp 2
         <xforms:trigger ref="instance
('acctToolBdgtInst')/remove">
           <xforms:label>Remove 2nd Level
Department</xforms:label>
           <xforms:action ev:event="DOMActivate">
             <xforms:delete nodeset="instance
('departmentsInst')/departments/department[index
('budgetRpt')]/department"
                            at="index('budgetRpt2')"/>
           </xforms:action>
         </xforms:trigger>

Ẩn và hiện các nút

Biểu mẫu ngân sách sử dụng thuộc tính thích hợp để ẩn và hiện các gian hàng. Tính năng này hoạt động không được tốt trong trường hợp này bởi vì phần thụt lề thể hiện cấp của gian hàng. Một thành tố setvalue thực hiện chức năng này rất giống hàm khởi tạo trong một thủ tục chèn (insert). Bởi vì thuộc tính thích hợp đã được gắn với tất cả các nút hiển thị, ẩn các nút đi trở thành vấn đề thiết đặt các nút ẩn thành giá trị 1 và ngược lại đối với các nút hiện, như được thể hiện trong Ví dụ 14.

Ví dụ 14. Ẩn và hiện các nút
         <xforms:trigger ref="instance('acctToolBdgtInst')/show2nd">
           <xforms:label>Show 2nd and 3rd Level
Children</xforms:label>
           <xforms:action ev:event="DOMActivate">
             <xforms:setvalue ref="instance
('departmentsInst')//departments/department[index
('budgetRpt')]/hidden" value="0"/>
           </xforms:action>
         </xforms:trigger>
       </td>
       <td>
         <xforms:trigger ref="instance
('acctToolBdgtInst')/hide2nd">
           <xforms:label>Hide 2nd and 3rd Level 
Children</xforms:label>
           <xforms:action ev:event="DOMActivate">
             <xforms:setvalue ref="instance
('departmentsInst')//departments/department[index
('budgetRpt')]/hidden" value="1"/>
           </xforms:action>
         </xforms:trigger>

Tiếp theo, bạn sẽ thấy cách thức để tạo một số kỹ thuật với các nút lá gian hàng như thế nào.

Bằng cách nào các gian hàng với các ngân sách gian hàng con là chỉ chứa thuộc tính đọc (read-only)

Với mục đích lập ngân sách, ngân sách của gian hàng được xác định bởi tổng của các ngân sách của các gian hàng dưới nó. Manh mối trực quan dẫn tới bản chất read-only của gian hàng cha là số liệu không thể thay đổi được. Mỗi một giá trị, lợi nhuận, phí tổn của ngân sách, có hai điều khiển biểu mẫu (form). Bằng cách kích hoạt (enable) điểu khiển biểu mẫu textarea, giá trị trở thành read-write và manh mối trực quan là gian hàng là một nút lá được tạo. Ngược lại, kích hoạt (enable) điểu khiển biểu mẫu đầu ra tạo một hiển thị read-only, chỉ ra rằng gian hàng có các gian hàng con. Quá trình chuyển đổi giữa read-only và read-write và hành vi tính toán số liệu xảy ra như một kết quả của sự gắn kết tới dữ liệu thích hợp và tính toán. Ví dụ 15 thể hiện đoạn mã cho sự gắn kết.

Ví dụ 15. Các phép tính và sự tương thích
<xforms:bind nodeset="instance
('departmentsInst')//department/planPro"
             relevant="../department/@id=false()"/>

<xforms:bind nodeset="instance
('departmentsInst')//department/planExp"
             relevant="../department/@id=false()"/>

<xforms:bind nodeset="instance
('departmentsInst')//department/leafPlanExp"
             relevant="../department/@id=false()"
             calculate="../planExp"/>

<xforms:bind nodeset="instance
('departmentsInst')//department/leafPlanPro"
             relevant="../department/@id=false()"
             calculate="../planPro"/>

<xforms:bind nodeset="instance
('departmentsInst')//department/planProDisplay"
             calculate="sum(..//leafPlanPro)"
             relevant="../department/@id=true()"/>

<xforms:bind nodeset="instance
('departmentsInst')//department/planExpDisplay"
             calculate="sum(..//leafPlanExp)"
             relevant="../department/@id=true()"/>

<xforms:bind nodeset="instance
('departmentsInst')//department/curPro"
             calculate="round(sum(..//ordersTotal))"/>

<xforms:bind nodeset="instance
('departmentsInst')//department/curExp"
             calculate="round(sum(..//expensesTotal))"/>

<xforms:bind nodeset="instance
('departmentsInst')//departments/maxid"
             calculate="max(//department/@id)+1"/>

Biểu thức XPath tất cả đều sử dụng toán tử // để chọn tất cả các nút department. Các nút sau đó tiếp tục được khử tham chiếu cho đến khi tất cả các trường của planPro trong tất cả các nút department đều có kết nối thích hợp đặt cho chúng. Nhắc lại từ bài viết trước là kí tự @ chỉ ra một thuộc tính. ID là một thuộc tính, và do đó, được tham chiếu với kí tự @ trong biểu thức XPath. Sự so sánh là cho true() hay false(). Hai hàm XPath đặc biệt đó định các giá trị logic (Boolean) đúng và sai. Được sử dụng theo cách được trình bày trong Ví dụ 15, sự so sánh trả về true nếu nút được tham chiếu tồn tại. Cũng phải chú ý ứng dụng của các hàm làm tròn (round) và tính tổng (sum) của XPath. Cũng cần xem hàm lấy giá trị lớn nhất (max) trong áp dụng bên trên khi thành tố //departments/maxid được đặt. Thành tố maxid được sử dụng để đặt ID cho các hàng mới được thêm vào, đảm bảo các ID riêng biệt với nhau.

Tiếp theo bạn sẽ tìm hiểu một chút về cách thức để sinh dữ liệu cá thể cho biểu mẫu sử dụng DOM của PHP.

Đề cập ngắn gọn về sinh dữ liệu biểu mẫu ngân sách sử dụng PHP

Vì XForms đòi hỏi toàn bộ cấu trúc cơ bản của dữ liệu XML, cấu trúc dữ liệu phải được tạo từ đầu bởi kịch bản PHP. Kịch bản PHP cố gắng thực hiện với ít hàm nhất có thể; dù sao, dữ liệu này cũng đòi hỏi một cấu trúc phả hệ từ một bảng cơ sở dữ liệu SQL, lại là một cấu trúc thẳng. Kịch bản PHP get_budget thực hiện một chức năng đơn: nó thu thập tất cả các gian hàng từ bảng department trong cơ sở dữ liệu SQL, và xây dựng lại phả hệ dựa trên ID của mỗi nút và ID được lưu trữ của nút cha. Giá trị 0 được cho là nút gốc.

Các truy vấn SQL cho nhiệm vụ này không đáng để quan tâm nhiều. Cơ sở dữ liệu SQL là cần thiết để sử dụng PHP, như đã thể hiện trong Ví dụ 16. Giải thuật được sử dụng là tìm kiếm theo chiều sâu (depth-first traversal), một nút được rút ra khỏi một hàng đợi (queue) và được tham chiếu như một nút cha trong mỗi vòng lặp. ID của nút đó sẽ được truy vấn như một nút cha, trả về tất cả các nút con của gian hàng đó.

Ví dụ 16. Truy vấn SQL cho gian hàng
      $query = sprintf('SELECT * FROM `departments` WHERE Parent=%s 
AND NOT(department_id=%s);',$parentId, $parentId);
      $queryData = mysql_query($query, $sqldb);
      if (!$queryData) die ('Could not execute acct db query: 
<b>'.$query.'</b> because:  ' . mysql_error());
      while ($row = mysql_fetch_assoc($queryData)) {

Tài liệu XML khởi tạo một chuỗi tĩnh (static string), như đã thể hiện trong Ví dụ 17. Sử dụng nó như một cơ sở, các nút mới sẽ được tạo ra và thêm vào lặp lại, tiến dần xuống dưới cây theo phương pháp tìm kiếm sâu. Quá trình thêm các nút mới và sử dụng cấu trúc danh sách liên kết của DomNodes trong PHP được thể hiện trong Ví dụ 17.

Ví dụ 17. Xây dựng cây XML DOMNode và khởi tạo DomDocument
   $doc = new DomDocument('1.0');
   $doc->loadXml('<?xml version="1.0" encoding="UTF-8"?>
<budgetForm xmlns:xforms="http://www.w3.org/2002/xforms" 
xmlns:ev="http://www.w3.org/2001/xml-events">
  <departments>
    <maxid/>
    <hidden>0</hidden>
  </departments>
</budgetForm>');
...
do
{
...

      if($curParent->previousSibling && $curParent-
>previousSibling->nodeName == 'department')
          $curParent = $curParent->previousSibling;
      else
          $curParent = $curParent->childNodes->item($curParent-
>childNodes->length-1);

      $parentId = $curParent->getAttribute('id');

   } while ($parentId);

Chú ý cẩn thận khi xử lý các nút để không thực sự coi danh sách nút là một hàng đợi mà sử dụng hàm remove, bởi vì nút thực sự sẽ mất đi và XML thì không chứa bất cứ nút nào dưới kết quả cuối cùng.

Đó là vỏ bọc cho biểu mẫu ngân sách! Chú ý rằng không phải tất cả mọi thứ trong đó đều được bao quát, nhưng bạn có thể xem mã đầy đủ cho biểu mẫu (form), bao gồm dữ hỗ trợ PHP và SQL khởi tạo cho cơ sở dữ liệu của bạn, trong phần tải về đã được cung cấp.


Biểu mẫu thanh toán (Billing)

Biểu mẫu thanh toán sử dụng nhiều khái niệm tương tự như biểu mẫu ngân sách với một vài bổ xung mới. Biểu mẫu XForms thanh toán giới thiệu thủ tục để mở một cửa sổ mới hiển thị thông tin thích hợp để in một hoá đơn. Phần này cài đặt biểu mẫu này, trạng thái ban đầu được thể hiện trong Hình 6.

Hình 6. Trạng thái ban đầu của biểu mẫu thanh toán
Chèn một gian hàng

Hiển thị bảng

Mặc dù bảng HTML không hỗ trợ XForms và Firefox chưa cài đặt thuộc tính repeat của XForms, vẫn có thể lặp lại toàn bộ cấu trúc bảng. Biểu mẫu XForms thanh toán sử dụng cơ cấu đó để hiển thị dữ liệu thanh toán. Hiển thị có được từ cấu trúc lặp đơn cấp như được thể hiện trong Ví dụ 18. Hình 7 thể hiện biểu mẫu sau khi nhấn nút Refresh/Load Accounts.

Hình 7. Biểu mẫu thanh toán đã được làm mới
Biểu mẫu thanh toán đã được làm mới
Ví dụ 18. Các điều khiển XForms trực quan cho biểu mẫu thanh toán
    <xforms:repeat id="acctRpt" nodeset="instance
('accts')//account">
      <table width="750px">
        <tr>
          <td align="center" width="5%">
            <xforms:output ref="acctId"/>
          </td>
          <td align="center" width="15%">
            <xforms:output ref="creationDate"/>
          </td>
          <td align="center" width="15%">
            <xforms:output ref="totalDue"/>
          </td>
          <td align="center" width="15%">
            <xforms:output ref="accountState"/>
          </td>
          <td align="center" width="15%">
            <xforms:trigger style="display: inline" 
ref="submitCollection">
              <xforms:label>COLLECT!</xforms:label>
              <xforms:action ev:event="DOMActivate">
                <xforms:setvalue ref="@makeDelURI" value="concat('doBillingAction.php?
action=delinquent&acct=',../../acctId,'&amount=',../../payment
Amt,'&bal=',../../totalDue)"/>
                <xforms:load ref="@makeDelURI" show="new"/>
              </xforms:action>
            </xforms:trigger>
            </xforms:trigger>
            <xforms:trigger ref="submitCredit">
              <xforms:label>Issue Credit</xforms:label>
              <xforms:action ev:event="DOMActivate">
                <xforms:setvalue ref="@makeCreURI" 
                                 value="concat('doBillingAction.php?
action=credit&acct=',../../acctId,'&amount=',../../totalDue)"/>
                <xforms:load ref="@makeCreURI" show="new"/>
              </xforms:action>
            </xforms:trigger>
          </td>
          <td align="center" width="10%">
            <xforms:textarea ref="paymentAmt"/>
          </td>
          <td align="center" width="15%">
            <xforms:trigger ref="pay">
              <xforms:label>Record</xforms:label>
              <xforms:action ev:event="DOMActivate">
                <xforms:setvalue ref="@makeRctURI" value="concat
('doBillingAction.php?
action=makepayment&acct=',../../acctId,'&amount=',../../paymen
tAmt,'&bal=', ../../totalDue - ../../paymentAmt)"/>
                <xforms:insert nodeset="../payment" at="count
(../payment)" position="after"/>
                <xforms:setvalue ref="../payment[count
(../payment)]/paymentDate" value="substring(now(),1,10)"/>
                <xforms:setvalue ref="../payment[count
(../payment)]/amount" value="../../paymentAmt"/>
                <xforms:setvalue ref="../payment[count
(../payment)]/acctId" value="../../acctId"/>
                <xforms:load ref="@makeRctURI" show="new"/>
                <xforms:setvalue ref="../paymentAmt" value="0"/>
              </xforms:action>
            </xforms:trigger>          </td>
          <td align="center" width="10%">
            <xforms:trigger ref="bill">
              <xforms:label>Bill</xforms:label>
              <xforms:action ev:event="DOMActivate">
                <xforms:setvalue ref="@makeBilURI" value="concat('doBillingAction.php?
action=createbill&acct=',../../acctId,'&amount=',../../payment
Amt,'&bal=',../../totalDue)"/>
                <xforms:load ref="@makeBilURI" show="new"/>
              </xforms:action>
            </xforms:trigger>
          </td>
        </tr>
      </table>
    </xforms:repeat>
 
      <xforms:bind nodeset="instance('accts')//bill"          
          relevant="../accountState != 'Paid' and 
                    ../accountState != 'Current' and 
                    ../accountState != 'Credit'"/>
      <xforms:bind nodeset="instance('accts')//pay"          
          relevant="../accountState != 'Paid' and ../accountState != 
'Credit'"/>
      <xforms:bind nodeset="instance('accts')//paymentAmt"   
          relevant="../accountState != 'Paid' and ../accountState != 
'Credit'"/>
      <xforms:bind nodeset="instance('accts')//submitCollection"     
          relevant="../accountState = 'Delinquent'"/>
      <xforms:bind nodeset="instance('accts')//submitCredit"                 
         relevant="../accountState = 'Credit'"/>
      <xforms:bind nodeset="instance('accts')//accountState"         
           calculate="
             if(number(../totalDue)>0,
               if(days-from-date(now()) - if(../maxYearPaid='NaN',
                                             days-from-date
(../creationDate),
                                             days-from-date
(../lastPaymentDate)) > 30,
                 if(days-from-date(now()) - if(../maxYearPaid='NaN',
                                               days-from-date
(../creationDate),
                                               days-from-date
(../lastPaymentDate)) > 90,
                   if(days-from-date(now()) - if(../maxYearPaid='NaN',
                                                 days-from-date
(../creationDate),
                                                 days-from-date
(../lastPaymentDate)) > 180,
                        'Delinquent',
                        'Pastdue'),
                     'Due'),
                  'Current'),
                  if(number(../totalDue) = 0,'Paid','Credit'))"/>

Tiếp theo bạn sẽ tìm hiểu về lưu trữ số tiền thanh toán cho các tài khoản.

Lưu trữ số tiền thanh toán

Quá trình lưu trữ đỏi hỏi một giấy biên lai và chỗ chứa dữ liệu. Công cụ thanh toán giải quyết tình huống này bằng cách mở một cửa sổ mới nơi dữ liệu yêu cầu hiển thị trong một định dạng thích hợp cho in ấn và gửi thư. Cũng với sự thuận tiện như vậy, khách hàng có thể nhận được e-mail hoặc bất cứ một trong số các tiến trình thủ tục nào đó. Các nút bấm Record, Bill, và Collections thực thi tương tự bằng cách định dạng bằng tay một hoạt động gửi đơn GET và sử dụng kết quả đó như một đối số cho load với thuộc tính xuất hiện đầu tiên trong loạt bài viết tại thời điểm này. Thuộc tính show=new chỉ cho trình duyệt tạo một cấu trúc thể hiện mới. Hình 8 cho thấy kể quả khi kích hoạt nút bấm COLLECT!.

Hình 8. Hiển thị thu tiền
Hiển thị thu tiền

Hình 9 cho thấy kết quả khi kích hoạt nút bấm Record.

Hình 9. Hiển thị biên lai
Hiển thị biên lai

Kích hoạt nút bấm Bill cho kết quả thể hiện trong Hình 10.

Hình 10. Hiển thị hoá đơn
Hiển thị hoá đơn

Chọn nút bấm Issue Credit cho kết quả thể hiện trong Hình 11.

Hình 11. Hiển thị tín dụng
Hiển thị tín dụng

Tiếp đó, bạn sẽ thấy cách thức để đặt rất nhiều trạng thái khác nhau của một tài khoản dựa trên ngày tháng của lần trả tiền cuối cùng.

Tính toán các trạng thái có thể cho mỗi tài khoản

Ba trạng thái khác nhau xuất hiện trong Hình 7. Mỗi trạng thái tạo ra các điều kiện khác nhau cho hiển thị. Các tài khoản có quyết toán lớn hơn không và chưa trả tiền quá 180 ngày có hiển thị nút bấm COLLECT!, trong khi các tài khoản có quyết toán nhỏ hơn hoặc bằng 0 không có nút bấm nào cả, tuy vậy một quyết toán âm cuối cũng sẽ cần một nút Credit. Ví dụ 19 chứa các khai báo xác đáng và các khai báo tính toán tạo ra hiệu ứng này.

Ví dụ 19. Tính toán trạng thái tài khoản
      <xforms:bind nodeset="instance('accts')//bill"
          relevant="../accountState != 'Paid' and 
                    ../accountState != 'Current' and 
                    ../accountState != 'Credit'"/>
      <xforms:bind nodeset="instance('accts')//pay"          
          relevant="../accountState != 'Paid' and ../accountState != 'Credit'"/>
      <xforms:bind nodeset="instance('accts')//paymentAmt"   
          relevant="../accountState != 'Paid' and ../accountState != 'Credit'"/>
      <xforms:bind nodeset="instance('accts')//submitCollection"     
          relevant="../accountState = 'Delinquent'"/>
      <xforms:bind nodeset="instance('accts')//submitCredit"                 
         relevant="../accountState = 'Credit'"/>
      <xforms:bind nodeset="instance('accts')//accountState"         
           calculate="
             if(number(../totalDue)>0,
               if(days-from-date(now()) - if(../maxYearPaid='NaN',
                                             days-from-date(../creationDate),
                                             days-from-date(../lastPaymentDate)) > 30,
                 if(days-from-date(now()) - if(../maxYearPaid='NaN',
                                               days-from-date(../creationDate),
                                               days-from-date(../lastPaymentDate)) > 90,
                   if(days-from-date(now()) - if(../maxYearPaid='NaN',
                                                 days-from-date(../creationDate),
                                                 days-from-date(../lastPaymentDate))>180,
                        'Delinquent',
                        'Pastdue'),
                     'Due'),
                  'Current'),
                  if(number(../totalDue) = 0,'Paid','Credit'))"/>

Một hàm XPath mới xuất hiện trong đoạn mã này, hàm days-from-date, biến đổi giá trị ngày trở thành một giá trị nguyên tính từ đầu kỷ nguyên. Hàm đó cho phép các tính toán như thế này xảy ra. Như Ví dụ 19 thể hiện, sự gia tăng xuất phát từ một tài khoản Paid tới một tài khoản Delinquent dựa trên một quyết toán dương và vượt qua 30, 90, rồi 180 ngày, và nếu không tồn tại lần nộp tiền nào (nghĩa là không tồn tại ngày nộp tiền nào), thì ngày tạo được sử dụng để so sánh. Một tính năng đáng quan tâm của Ví dụ 19 là toán tử và (and). Nút bấm Bill áp dụng cho các tài khoản đến hoặc quá hạn trả. Bằng các thực hiện hai phép so sánh logic, sự ảnh hưởng đó đã đạt được. Toán tử logic và (and) là nguyên văn từ and, không phải kí hiệu & hay && như rất nhiều lập trình viên đã quen thuộc.

Các con số được xử lý như thế nào

Một cách âm thầm, XForms thực hiện một lượng lớn xử lý số học. Cấu trúc dữ liệu cơ bản bao gồm có một nút tài khoản với một tập các nút có tên order và một tập các nút có tên payment. Bằng cách tính tổng/số lượng đơn đặt hàng trong tập nút, tổng lượng tiền khác hàng đã đồng ý trả cho hãng đã sẵn dùng. Đầu tiên, dù thế nào, cơ cấu XForm cũng phải tính số lượng của mỗi đơn đặt hàng. Tổng của mỗi đơn hàng được tính toán bởi phương trình trong Ví dụ 20.

Ví dụ 20. Số lượng đơn hàng và tổng giá tiền
<xforms:bind nodeset="instance('accts')//order/amount" 
   calculate="../quantity * ../unitPrice * (1-../discount)"/>
<xforms:bind nodeset="instance('accts')//totalDue"     
   calculate="sum(..//order/amount) - sum(..//payment/amount)"/>

Ví dụ 20 giải thích một khả năng nội tại của bộ xử lý XForms đã cung cấp một số sự trợ giúp cho lập trình viên. Trình duyệt xử lý mã XForms giải quyết sự phụ thuộc trong các trật tự thích hợp. Trong trường hợp này, đơn hàng/số lượng các nút cần thiết để được giải quyết trước nút totalDue. Thuộc tính relevant trong Ví dụ 19 giải thích một hành vi tương tự với sự phụ thuộc vào relevant khi tính toán trạng thái các tài khoản.

Phép tính trạng thái tài khoản đòi hỏi ngày tháng. Một vài lựa chọn để cung cấp thông tin tồn tại đó. Một lựa chọn là sử dụng PHP để trích ngày đòi hỏi, và một lựa chọn khác là cho dữ liệu được sắp xếp. XForms 1.0, đáng tiếc, không cung cấp hàm tính giá trị nhỏ nhất min có thể giải quyết ngày tháng chính xác. Đoạn mã trong Ví dụ 21, dù sao, giải thích một cách tiếp cận mạnh hơn để tìm ra ngày nhỏ nhất trong danh sách. Tiến trình bao gồm cả cắt mỗi trường ngày tháng ra thành từng phần cấu tạo cho phép so sánh rồi ráp kết quả lại trở thành một ngày tháng.

Ví dụ 21. Tìm ngày đặt hàng (ngày tạo) nhỏ nhất và ngày trả (ngày trả cuối) lớn nhất
      <xforms:bind nodeset="instance('accts')//order/yearCreate"             
         calculate="substring(../orderDate, 1, 4)"/>
      <xforms:bind nodeset="instance('accts')//minYearCreate"                
         calculate="min(..//yearCreate)"/>
      <xforms:bind nodeset="instance('accts')//order/monthCreate"            
         calculate="if(../yearCreate = ../../minYearCreate,
                     number(substring(../orderDate, 6, 2)),
                     13)"/>
      <xforms:bind nodeset="instance('accts')//minMonthCreate"               
         calculate="min(..//order/monthCreate)"/>
      <xforms:bind nodeset="instance('accts')//order/dayCreate"              
         calculate="if(../monthCreate = ../../minMonthCreate,
                     number(substring(../orderDate, 9, 2)),
                     32)"/>
      <xforms:bind nodeset="instance('accts')//minDayCreate"                 
         calculate="min(..//order/dayCreate)"/>
      <xforms:bind nodeset="instance('accts')//creationDate"                 
         calculate="concat(../minYearCreate,
                           '-',
                           if(../minMonthCreate < 10,
                              concat('0',../minMonthCreate),
                              ../minMonthCreate),
                           '-',
                           if(../minDayCreate < 10,
                              concat('0',../minDayCreate),
                              ../minDayCreate))"/>

      <xforms:bind nodeset="instance('accts')//payment/yearPaid"             
         calculate="substring(../paymentDate, 1, 4)"/>
      <xforms:bind nodeset="instance('accts')//maxYearPaid"                  
         calculate="max(..//yearPaid)"/>
      <xforms:bind nodeset="instance('accts')//payment/monthPaid"            
         calculate="if(../yearPaid = ../../maxYearPaid,
                     number(substring(../paymentDate, 6, 2)),
                     0)"/>
      <xforms:bind nodeset="instance('accts')//maxMonthPaid"                 
         calculate="max(..//payment/monthPaid)"/>
      <xforms:bind nodeset="instance('accts')//payment/dayPaid"              
         calculate="if(../monthPaid = ../../maxMonthPaid,
                     number(substring(../paymentDate, 9, 2)),
                     0)"/>
      <xforms:bind nodeset="instance('accts')//maxDayPaid"                   
         calculate="max(..//payment/dayPaid)"/>
      <xforms:bind nodeset="instance('accts')//lastPaymentDate"              
         calculate="concat(../maxYearPaid,
                           '-',
                           if(../maxMonthPaid < 10,
                              concat('0',../maxMonthPaid),
                              ../maxMonthPaid),
                           '-',
                           if(../maxDayPaid < 10,
                              concat('0',../maxDayPaid),
                              ../maxDayPaid))"/>

Tiếp đó, bạn sẽ thu được một vài quan điểm về cách thức cải thiện thời gian tải XForm của bạn cho một lượng lớn dữ liệu cá thể.

Cập nhập số liệu mà không phải chờ

Một sự cải thiện rõ ràng cho biểu mẫu XForms thanh toán hiện tại là sự bổ sung của giới hạn số lượng các tài khoản được hiển thị một lúc, do đó trình duyệt không phải duy trì cấu trúc dữ liệu của toàn bộ cơ sở dữ liệu. Cho toàn bộ cơ sở dữ liệu trong cấu trúc dữ liệu tập trung vào một việc quan trọng, dẫu vậy. Cần một lượng đáng kể thời gian để truy vấn, xây dựng và tải cấu trúc dữ liệu và quá trình đó rất quý giá đối với sự kiên nhẫn của người dùng. Biểu mẫu này cài đặt một phím tắt để tăng khả năng nhận điều khiển của biểu mẫu (form). Hành động lưu trữ chứa một khối chèn (insert) giống như phần chèn (insert) đã miêu tả trong biểu mẫu ngân sách. Đoạn mã được nói đến xuất hiện trong Ví dụ 18.

Đoạn mã chèn (insert) tạo một nút payment mới và lưu dữ liệu thích hợp vào nút đó. Thay đổi cấu trúc dữ liệu nội tại không thay đổi cơ sở dữ liệu; dù sao, kịch bản PHP sinh ra biên lại có thể dễ dàng truy cập cơ sở dữ liệu đưa ra một đáp trả nhanh hơn rất nhiều so với gửi một kho chứa toàn bộ tập nút của tài khoản, hay thậm chí chỉ một nút đơn lẻ, có thể đòi hỏi tải lại toàn bộ cấu trúc dữ liệu. Do tính toán các phương trình trong Ví dụ 20, quyết toán cho tài khoản đó ngay lập tức phản ánh thông tin mới.

Bạn sắp hoàn thành rồi! Trong phần cuối cùng của đoạn này bạn sẽ học cách PHP trợ giúp biểu mẫu thanh toán

PHP làm được gì cho biểu mẫu XForms thanh toán

PHP phục vụ như một giao diện tới cơ sở dữ liệu SQL trong suốt loạt bài viết này với hấu hết công việc được thực hiện bởi bộ xử lý XForms. Một giới hạn của XForms, dù thế nào, là sự đòi hỏi một nút phải tồn tại. XForms không thể dễ dàng tạo một nút mới và nó đòi hỏi một khuôn mẫu khi một nút mới được tạo. Hầu hết dữ liệu sau XForm thanh toán là dữ liệu đã được tính toán và lưu trữ dữ liệu đã tính toán ngốn không gian đĩa quý giá. PHP tạo một tập nút tài khoản và lồng đơn đặt hàng với các nút payment bên dưới nó, bổ sung thêm các nút vào các nút đơn hàng để tổng hợp và đồng thời cũng vào tài khoản để tính toán. Đoạn mã để thực hiện chức năng này rất giống với đoạn mã đã thể hiện để tính toán các nút ngân sách chỉ khác là cấu trúc dữ liệu kết quả có ít cấp phả hệ hơn.

Tốt lắm! Bạn đã hoàn thành bài viết này. Bây giờ hãy xem lại bài tóm tắt để tìm hiểu thêm về các vấn đề sắp tới, trong Phần 4.


Tóm tắt

Bài viết này giới thiệu một vài khái niệm với XForms. Bài viết bao gồm mở cửa sổ trình duyệt mới và các lặp lồng nhau, đồng thời cũng giải quyết cơ cấu chèn. Biểu mẫu ngân sách giới thiệu quá trình bỏ các nút đi và các đòi hỏi nội tại của cấu trúc dữ liệu để cho phép hiển thị. Các phép tính toán chi tiết, chuỗi phụ thuộc, và nhiều hàm XPath mới xuất hiện trong cả biểu mẫu ngân sách và thanh toán.

Các phần tiếp theo của loạt bài viết này tinh chế cả cài đặt XForms khi cần thiết và các kĩ năng tham gia vào cái đặt XForms. Tiếp theo, Phần 4 cài đặt quản lý tài sản và một vài công cụ hành chính.


Tải về

Mô tảTênKích thước
Sample code for this tutorialaccttool_part3_code.zip100KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

  • Tài liệu XForms Recommendation được duy trì bởi W3C.
  • Tải MozzIE, một điều khiển nguồn mở cho phép bạn kết xuất XForms trong Internet Explorer.

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=407202
ArticleTitle=Sử dụng XForms để tạo công cụ kế toán, Phần 3: Phát triển chức năng quản lý tài sản
publish-date=07102009