Khanh Hoang - Kenn
Kenn is a user experience designer and front end developer who enjoys creating beautiful and usable web and mobile experiences.
Trong các ứng dụng mã nguồn mở, người dung có thể tùy biến chương trình bằng cách thay đổi hoặc thêm các đoạn mã vào chương trình. Điều này có thể dẫn đến tình trạng mỗi khi có bản cập nhật, người dung phải kiểm tra xem sự tùy chỉnh đó có còn tường thích hay không và có hoạt động đúng như mong muốn hay không. Tuy nhiên DP hoạt động theo một cách hoàn toàn khác. Mọi thay đổi và sự mở rộng được thực hiện thong qua các module. Trong chương 2 này mình sẽ tập trung vào phần quan trọng nhất giúp bạn có thể phát triển web dựa trên drupal. Cụ thể là việc xây dựng một module cho DP.
Cấu trúc của một module có thể gồm nhiều tập tin khác nhau nhưng bắt buộc phải có ít nhất một tập tin <module name>.info và một tập tin <module name>.module trong đó <module name> là tên của thư mục chưa module được tạo, tên này không được trùng với tên của các module đã tồn tại trong hệ thống.
File chứa các thong tin về module đó. Nó được viết theo kiểu file PHP INI – Một định dạng file phục vụ cho việc xác định thiết lập. Nội dung một file .info có dạng như sau:
name = “Google Maps” description = “Display a map using google Maps API” core = 6.x php = 5.1
Module hiển thị trong phần quản trị:
Name và Description là 2 trường bắt buộc của 1 module DP.
Sau khi đã hoàn thành được file .info chúng ta tiếp tục viết code cho file .module.
Đây là file quan trọng nhất trong một module. Nó chứa các đoạn mã PHP thực hiện các chức năng của module đó. File này thong thường sẽ mở rộng một số hàm hook mà DP gọi tại các thời điểm nhất định của một yêu cầu.
Nội dung bắt đầu bằng thẻ <?php, sau đó là mã nguồn. Theo quy ước của DP, file này sẽ không chứa dấu đóng. Việc này giúp tránh in các những ký tự trống trong đầu ra của đoạn script trong một số trường trường hợp. Ví dụ, nếu các ký tự trống xuất hiện ra trước phần thong tin header của HTML gửi đi, thì ở trình duyệt máy khách sẽ thong báo lỗi ở đầu trang web.
Mã nguồn cụ thể sẽ được trình bày trong phần sau.
DP tuân theo các tiêu chuẩn viết mã và tài liệu một cách rất chặt chẽ. Chi tiết có thể xem tại: http://drupal.org/coding-standards . Phần này tương đối đơn giản nên mình sẽ nói sơ quan để mọi người tự tìm hiểu.
Phần đầu tiên của module các DEV phải chú thích rõ rang và đầy đủ chức năng của khối mã tiếp theo đó. Ví dụ:
/** * @file * Module for displaying a map from Google Maps. * This module provides block content retrieved from * Google Maps data. * @see http://map.google.com */
Hook Block: Hook này được sử dụng để tạo một block hiển thị thông tin. Khi Hook_block được gọi, DP sẽ truyền 3 tham số vào hàm: $op, $delta và $edit. Hook Help: Cung cấp các thông tin cần thiết cho người sử dụng. Nhận 2 tham số $path (chứa URI của trang được gọi) và tham số $arg chứa các thông tin thêm. Hook User: Mỗi người dùng đã đăng ký thì sẽ có một trang account riêng để xem thông tin của người dùng. Tuy nhiên Hook user còn làm được nhiều việc hơn như vậy. Nó còn có thực hiện các chức năng thêm sửa xóa dữ liệu nữa.
Hook Mail: Phần lớn các yêu cầu về việc gửi mail trong drupal đều được hỗ trợ bởi các hàm có trong mail API. Tuy nhiên tùy theo yêu cầu người phát triển có thể tùy ý chỉnh định dạng của thông điệp. việc này được thực hiện qua Hookmail(). HookView: Trước khi một node được hiển thị tới người dùng, thông tin của nó có thể được cập nhật bằng cách sử dụng hook view. Hook này để nạp vào một node nhưng node đó chưa được hiển thị. Hook Menu: Cho phép người phát triển tạo đăng ký một URI nhất định và ánh xạ với một hàm sử lý. Hook này thường được dùng để tạo menu cho trang web.
Trong phần này chúng ta sẽ tập trung xem xét kỹ về cơ chế hoạt động của các hook bằng cách tạo một hook riêng và triển khai vào các module. Mục đích của việc này là giúp người phát triển module có thể chỉ định được nội dung sẽ được đưa vào bản tin và cách hiển thị của chúng.
Việc định nghĩa một hook rất đơn giản. Thậm chí chúng ta chỉ tổn vài dòng mã. Tuy nhiên để tiện cho việc phát triển chúng ta nên định nghĩa hook một cách chi tiết.// Một số lưu ý để tối ưu và dễ dàng phát triển hook:
Ví dụ: để quyết định xem module nào sẽ chịu trách nhiệm đáp ứng request, Drupal sẽ yêu cầu các module cung cấp đường dẫn mà nó giải quyết. Drupal làm điều này bằng cách lên danh sách các module và gọi hàm hook menu có trong mỗi module.
Cài đặt cho annotate_menu():
/** * Implementation of hook_menu(). */ function annotate_menu($may_cache) { $items = array(); if ($may_cache) { $items[] = array( 'path' => 'admin/settings/annotate', 'title' => t('Annotation settings'), 'description' => t('Change how annotations behave.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('annotate_admin_settings'), 'access' => user_access('administer site configuration') ); } return $items; }
Khi Drupal hoàn tất việc yêu cầu các module tạo menu của chúng, nó sẽ lựa chọn hàm thích hợp để gọi cho request.
Các hook tự định nghĩa cũng được sử dụng giống các hook cơ bản trong drupal. Tuy nhiên để sử dụng được Hook trong DP ta cần có cài đặt chuẩn cho Hook. <Modulename>_<hookname>() và có các tham số trùng với khai báo của hook.
Hàm chuyển đổi ngôn ngữ t(): Hàm được dùng để hỗ trợ chế độ đa ngôn ngữ trong DP (mặc định là anh-mỹ). Tuy nhiên để thực hiện được ta phải bật module Content translation Hàm watchdog(): Nằm trong nhân DP là hàm biên bản cho DP: Thông báo trạng thái, trợ giúp & thông báo người dùng, thông báo tình trạng nguy hiểm, …
Jquery là một trong thư viện Javascript mạnh mẽ nhất hiện nay. Sử dụng một mô hình thiết kế hướng đối tượng là Fluent Interface. Jquery cho phép liên kết một chuỗi các lời gọi hàm để tạo nên một truy vấn phức tạp. Chi tiết xem tại: http://jquery.com
Thông thường JQuery sẽ truy vấn tới cây DOM. Một cây DOM là một dạng cấu trúc dữ liệu định nghĩa nên cấu trúc văn bản như XML, HTML.
Jquery được sử dụng trong rất nhiều trường hợp, không chỉ là truy vấn. Nó còn có bộ công cụ Ajax, công cụ xử lý sự kiện. hiệu ứng và một số tiện ích khác.
Hoạt động của JQuery: Phần này mình sẽ tập trung trình bày tới các bạn cách JQuery tìm kiếm một văn bản có cấu trúc bằng cách sử dụng CSS và XPath. CSS được sử dụng cho văn bản HTML, XHTML. XPath được sử dụng trong văn bản XML. Ta không cần thiết phải xây dựng các cũ pháp để tìm kiếm trong Javascript, JQuery sẽ triển khai các cũ pháp truy vấn có sẵn của CSS.
Giả sử đoạn mã HTML cần thao tác là : <p id="#intro">Welcome to the World of Widgets</p> Nếu muốn đặt màu sắc cho nền của đoạn trong thẻ <p></p>, chúng ta sẽ sử dụng CSS để xác định đoạn văn bản thông qua ID #intro. ID này không được trùng với các ID khác trong cùng một trang.
#intro { background-color: blue; }
Việc này có thể được thử hiện bằng cách sử dụng jQuery như sau:
// Định nghĩa biến jQuery như một hàm. var jQuery = function(a,c) {...} // Ánh xạ không gian tên jQuery đến ký hiệu “$” var $ = jQuery; $("#intro").css("background-color", "blue"); Trong trường hợp sử dụng lớp CSS, đoạn mã jQuery sẽ là: $(".intro").css("background-color", "blue").fadeIn("slow");
jQuery cũng có thể thực hiện một số hiệu ứng lên trang web, ví dụ: $("#intro").css("background-color", "blue").fadeIn("slow"); hoặc: $("p.intro").css("background-color", "blue").fadeIn("slow"); Nếu một thành phần của trang HTML không được đánh dấu bằng ID hoặc bộ lọc lớp, cách truy vấn trên sẽ không thực hiện được. Lúc đó, XPath là một giải pháp tốt để thay thế. XPath có phần linh hoạt hơn trong việc tạo các quy tắc để xác định thành phần trong HTML. Ngoài ra, XPath còn hỗ trợ sử dụng biểu thức chính quy.
Ví dụ:
$(“a[@target=_blank]”)
$(“input[@type=checkbox]”)
Chi tiết về các biểu thức sử dụng trong XPath có thể xem thêm tại địa chỉ http://docs.jquery.com/DOM/Traversing/Selectors #JQuery trong Drupal
Mặc định, jQuery sẽ được cài đặt cùng với Drupal, vì thế việc sử dụng là khá dễ dàng. Mã nguồn của jQuery được chứa trong file jquery.js nằm trong thư mục misc. File này sẽ được nạp khi có lời gọi hàm drupal_add_js(). Hàm này có nhiệm vụ nhận mã JavaScript và thêm vào mã nguồn trang web. Ví dụ:
<?php drupal_add_js( '$(document).ready(function(){ $("p").fadeIn("slow"); });', 'inline' ); ?>
Tham số đầu tiên của hàm drupal_add_js() là đoạn mã JavaScript sẽ được thực thi. Tham số thứ hai (inline) cho biết Drupal sẽ ghi đoạn mã vào giữa hai thẻ <script></script> ở trong thẻ <head> của trang HTML.
Tuy nhiên, để đảm bảo rằng module hoạt động đúng như mong đợi, chúng ta cần phải đảm bảo các truy vấn jQuery chỉ chạy khi trình duyệt hỗ trợ JavaScript và AJAX. Nếu trình duyệt không hỗ trợ, các thay đổi đối với HTML phía máy khách sẽ không được thực hiện. Thư viện JavaScript của Drupal có hàm Drupal.jsEnabled() sẽ kiểm tra điều kiện này.
if(Drupal.jsEnabled) { // code goes here }
Giá trị trả về của hàm này là TRUE hoặc FALSE tương ứng với kết quả kiểm tra.
Từ phiên bản 4.7, Drupal cung cấp một API hỗ trợ việc create, validate và process form trong HTML. API này biểu diễn các form bằng một mảng chứa các thuộc tính và giá trị. Một số ưu điểm của bộ máy này:
Trong phần này, chúng ta sẽ tìm hiểu về Form API và ứng dụng vào một số ví dụ cụ thể.
Quá trình xử lý form:
Các bước trong quá trình khởi tạo: Các bước trong quá trình xử lý:
Các thành phần của một form được khai báo trong một mảng có cấu trúc phân cấp và có thể ghép lồng nhau. Mỗi thành phần gồm có các cặp thuộc tính/giá trị. Ví dụ sau định nghĩa một thành phần textfield:
<?php $form['foo'] = array( '#type' => 'textfield', '#title' => t('bar'), '#default_value' => $object['foo'], '#size' => 60, '#maxlength' => 64, '#description' => t('baz'), ); ?>
Tạo một nút bấm gửi dữ liệu:
<?php $form['submit'] = array( '#type' => 'submit', '#value' => t('Save'), ); ?>
Một vài điểm lưu ý:
**Thuộc tính name của thành phần được khai báo trong mảng $form và nằm ở cuối mảng. Ví dụ:
Form API cho phép tuỳ chỉnh giao diện đối với từng form, thậm chí đối với riêng từng thành phần trong một form. Việc tuỳ chỉnh được thực hiện bằng cách nạp chồng các thành phần giao diện tại thời điểm tạo form: •*Thêm thuộc tính #theme vào form chính hoặc các thành phần, cho phép chỉ định hàm giao diện nào sẽ được áp dụng. •*Chèn thẻ đánh dấu trực tiếp vào form như một thành phần của form đó. Các thẻ đánh dấu có thể là #prefix, #suffix hoặc #markup. Thẻ này dùng để chỉ định vị trí các đoạn mã thiết lập giao diện trong form. Ví dụ:
$form['access'] = array( '#type' => 'fieldset', '#title' => t('Access log settings'), '#prefix' => '<div class="foo">', '#suffix' => '</div>', );
bước này, hàm drupal_render() sẽ được gọi.
Form API có bước kiểm tra dữ liệu vào, thực hiện trên tất cả các form được gửi lên. Ngoài các hàm có sẵn, người phát triển cũng có thể tự tạo các hàm kiểm tra theo ý muốn. Tên hàm phải được đặt theo quy tắc <formID>_validate. Hàm này nhận hai tham số: $form và $form_state. $form là mảng chứa khai báo của form.
$form_state[‘values’] chứa giá trị cần kiểm tra. Ví dụ:
function test_form_validate($form, &$form_state) { if ($form_state['values']['name'] == '') { form_set_error('', t('You must select a name.')); } }
Cách thức thông thường được sử dụng để gửi form lên là thông qua hàm. Một form được gửi lên bằng nút bấm có thuộc tính type => 'submit' sẽ được truyền tới hàm tương ứng. Ví dụ:
function test_form_submit($form, &$form_state) { db_query("INSERT INTO {table} (name, log, hidden) VALUES ('%s', %d, '%s')", $form_state['values']['name'], $form_state['values']['access']['log'], $form_state['values']['hidden']); drupal_set_message(t('Your form has been saved.')); }
Hàm gửi dữ liệu chỉ được gọi khi nhận lệnh từ nút bấm submit có phương thức POST và dữ liệu được kiểm tra hợp lệ.
hook_form_alter là một công cụ cực kỳ mạnh mà Drupal cung cấp. Chúng ta có thể sử dụng nó để thực hiện được nhiều việc, tưởng chừng như không thể: Thay đổi cấu trúc một form mà không cần sửa mã nguồn, định dạng form, thêm kịch bản xử lý cá nhân vào form hệ thống, ...
Tạo cấu trúc tập tin cho module có tên 'demo'
sites/all/modules/demo sites/all/modules/demo/demo.info sites/all/modules/demo/demo.module
Mô tả module 'demo' -- định nghĩa tập tin demo.info
name = demo version = 7.x-dev package = @drupal.org description = Demo module, created by chientq
Khai báo kịch bản của module 'demo' -- định nghĩa tập tin demo.module
<?php /<strong> * Implemetation of hook_menu */ function demo_menu ($may_cache) { if ($may_cache) { return array ( array ( 'path' => 'demo', 'access' => true, 'title' => 'QuangChien\'s testing form', 'callback' => 'drupal_get_form', 'callback arguments' => array ('demo_demo') ) ); } } /</strong> * Define array structure for our testing form */ function demo_demo () { return array ( 'mytextfield' => array ( '#type' => 'textfield', '#title' => 'My textfield', '#default_value' => 'http://drupal.org/', '#description' => 'This is my text field!' ) ); } /** * Handler for demo_demo form */ function demo_demo_submit ($form_id, $form_values) { drupal_set_message ('Called demo_demo_submit ()'); } ?>
Phần này sẽ giới thiệu sơ lược về cách kết nối và truy vấn CSDL trong Drupal.
Drupal xác định được CSDL, tài khoản và mật khẩu của người dùng khi thiết lập kết nối bằng cách phân tích file settings.php. nằm trong thư mục sites/default. Dòng thông tin định nghĩa kết nối tới CSDL có dạng:
$db_url = 'mysql://username:password@localhost/databasename';
Phương thức định nghĩa kết nối là mysql hoặc pgsql phụ thuộc vào CSDL được sử dụng là MySQL hay PostgreSQL.
Dựa vào đó, Drupal xác định file thiết lập CSDL sẽ tham chiếu tới và khởi động CSDL tương ứng. Quá trình này được minh hoạ bởi hình dưới đây:
Quá trình thao tác với CSDL Một đoạn mã bên ngoài muốn truy cập vào CSDL của Drupal sẽ phải gọi include_once(‘includes/bootstrap.inc’) sau đó gọi: drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE) để tạo kết nối, sau đó thực hiện truy vấn bằng hàm db_query().
Drupal cung cấp hàm db_query() để thực thi một truy vấn tới kết nối đang hoạt động. Những truy vấn được hỗ trợ bao gồm SELECT, INSERT, UPDATE và DELETE. Dưới đây là một số ví dụ.
Lấy tất cả các hàng của một trường từ bảng joke có vid là số nguyên, có giá trị bằng $node->vid:
db_query('SELECT * FROM {joke} WHERE vid = %d', $node->vid);
Chèn một hàng mới vào bảng joke. Hàng mới chứa 2 giá trị nguyên và một xâu ký tự:
db_query("INSERT INTO {joke} (nid, vid, punchline) VALUES (%d, %d, '%s')",$node->nid, $node->vid, $node->punchline);
Cập nhật tất cả các hàng trong bảng joke có trường vid là số nguyên có giá trị bằng $node->vid. Các hàng thoả mãn điều kiện này sẽ thay đổi bằng cách đặt trường punchline bằng giá trị của xâu chứa trong $node->punchline db_query('DELETE FROM {joke} WHERE nid = %d', $node->nid); // Drupal đặt ra một số quy tắc về cú pháp trong phát biểu SQL. Đầu tiên, tên bảng phải được đặt trong dấu {}. Quy ước này cho phép những người dùng bị giới hạn số lượng CSDL tạo ra có thể cài đặt Drupal trong một CSDL có sẵn mà không bị xung đột.// Tiếp theo là cờ giữ chỗ trong truy vấn. Ở ví dụ trên, %d sẽ tự động được thay thế bằng giá trị $node->nid tương ứng. Dưới đây là bảng ký hiệu giữ chỗ và kiểu dữ liệu thay thế:
Bảng ký hiệu giữ chỗ trong truy vấn CSDL
Bảng ký hiệu giữ chỗ trong truy vấn CSDL.
Có nhiều cách khác nhau để lấy kết quả truy vấn.
Lấy một giá trị: Nếu muốn kết quả trả về chỉ một giá trị thì có thể sử dụng hàm db_result().
$sql = "SELECT COUNT(*) FROM {node} WHERE type = 'blog' AND status = 1"; $total = db_result(db_query($sql));
Trả về nhiều hàng: Với kết quả là một tập gồm nhiều hàm, hàm
db_fetch_object() giúp chọn ra từng hàng để xử lý.
$sql = "SELECT * FROM {node} WHERE type = 'blog' AND status = 1"; $result = db_query(db_rewrite_sql($sql)); while ($data = db_fetch_object($result)) { //code goes here }
Để nhận được kết quả là một mảng, chúng ta có thể sử dụng hàm db_fetch_array() thay cho db_fetch_object().
Giới hạn phạm vi kết quả: Với những truy vấn cho ra rất nhiều hàng, có thể giới hạn phạm số lượng như sau:
$sql = "SELECT * FROM {node} n WHERE type = 'blog' AND status = 1 ORDER BY n.created DESC"; $result = db_query_range(db_rewrite_sql($sql), 0, 10);
Trong trường hợp này, hàm db_query_range() được sử dụng thay cho điều kiện LIMIT vì không phải tất cả CSDL đều hỗ trợ cú pháp LIMIT.