Trong thời gian qua có nhiều bạn thắc mắc thêm chức năng tự đếm view bài viết, kiểu như xem một lần thì lượt xem sẽ tăng lên một giá trị, và kết hợp với cái chức năng đếm lượt xem đó để làm tính năng widget hiển thị bài viết xem nhiều. Cái vấn đề mà mình thường thấy ở đây là có nhiều bạn tìm được plugin nhưng lại không có đủ chức năng cần, mà plugin có đủ tính năng thì lại hoạt động không tốt, không như ý muốn.
Vậy thì ở bài này, mình sẽ hướng dẫn bạn tự viết một plugin có chức năng đếm lượt xem cho post hoặc bất cứ cái gì, đồng thời hướng dẫn luôn cách viết code tạo widget hiển thị post nhiều trong tuần, tháng hoặc bao nhiêu ngày tùy ý bạn.
Tổng quan kỹ thuật
Ở bài hướng dẫn này, chúng ta sẽ làm việc với hai tính năng khá quan trọng và cũng rất thú vị trong WordPress đó là Custom Post Field để tạo khóa (meta key) chứa dữ liệu lượt xem (meta data) cho mỗi bài và tính năng tạo widget để làm cái widget hiển thị danh sách các bài theo thứ tự từ được xem nhiều đến thấp, kèm theo thumbnail và số lượt view nếu thích.
Sở dĩ ở đây chúng ta nên sử dụng Custom Post Field cho chức năng đếm lượt xem là vì con số lượt xem chúng ta có thể hiểu rằng nó là một dữ liệu thêm vào của mỗi bài viết, mà trong WordPress thì đã có Custom Post Field có nhiệm vụ cho phép chúng ta tạo ra những cái khóa (key) tùy ý và mỗi khóa đều sẽ có giá trị riêng của nó. Vậy thì việc sử dụng Custom Post Field để tạo ra một khóa riêng để xác định và sử dụng giá trị của nó như là con số lượt xem là điều hợp lý nhất và rút ngắn thời gian triển khai vì chỉ cần vài dòng code là được.
Còn đối với tính năng tạo widget hiển thị danh sách các bài xem nhiều thì chắc chắn sẽ sử dụng Query & Loop để liệt kê các bài viết xem nhiều ra rồi. Nhưng làm sao để lấy được các bài được xem nhiều, và làm sao lấy dữ liệu đó trong khoảng thời gian nhất định (7 ngày chẳng hạn)? May mắn thay, WordPress cho phép chúng ta tạo query và lấy dữ liệu dựa theo meta key (khóa của Custom Post Field) và chúng ta có thể sắp xếp lại kiểu dữ liệu này theo hướng giảm dần trong giá trị, thành ra chúng ta sẽ có danh sách các bài viết xem nhiều nhất.
Hướng dẫn chi tiết
1. Thiết lập plugin
Bước này quá dễ dàng rồi, chúng ta sẽ tạo một thư mục tên là topview trong thư mục /wp-content/plugins để chứa plugin, và trong thư mục này sẽ có 2 file tên là plugins.php và styles.css như hình dưới.
Và trong file plugins.php ta có vài đoạn khai báo thông tin cho plugin như sau:
<?php
/*
Plugin Name: PostView
Plugin Author: ThachPham
Description: Plugin đếm lượt xem bài viết và hiển thị top bài xem nhiều
Version: 1.0
Author URI: https://thachpham.com
*/
Tí nữa chúng ta sẽ viết code vào phía bên dưới. Bây giờ hãy tạm thời vào Plugins -> Installed Plugins và kích hoạt nó lên đi.
2. Viết code đếm lượt view
Code đếm lượt view ở đây khá ngắn gọn vì nó là tính năng đơn giản mà, chỉ có các dòng như sau:
function postview_set($postID) {
$count_key = ‘postview_number’;
$count = get_post_meta($postID, $count_key, true);
if($count==”){
$count = 0;
delete_post_meta($postID, $count_key);
add_post_meta($postID, $count_key, ‘0’);
}else{
$count++;
update_post_meta($postID, $count_key, $count);
}
}
Trong code này, chúng ta sẽ tạo ra một hàm tên là postview_set()
với ý nghĩa cụ thể là:
- Dòng số 02 – 03: Thiết lập biến riêng để kiểm tra dữ liệu trong meta key, cái
$count_key
nghĩa là thiết lập tên metakey được lưu trong table wp_postmeta. Lưu ý là sau này nếu bạn đổi cái tên key này, tất cả giá trị sẽ được reset lại. - Dòng số 04 – 08: Kiểm tra xem cái meta key
postview_number
có dữ liệu chưa, nếu dữ liệu của nó là rỗng thì sẽ tiến hành tạo mới cho nó một meta key với tênpostview_number
ở trên. Đồng thời đưa biến$count
về giá trị là0
. - Dòng số 08 – 11: Nếu meta key
postview_number
đã có dữ liệu (phủ định của việc không có dữ liệu thì nghĩa là đã có dữ liệu chứ còn gì) thì biến$count
sẽ được tăng thêm một giá trị.
Vậy thì hàm này sẽ thực thi thế nào? Để hàm này chạy được, bạn phải đặt code sau vào file single.php trong theme đang sử dụng, và phải chèn nằm trong loop (trong cặp while):
<?php postview_set(get_the_ID()); ?>
Nghĩa là mỗi khi khách truy cập vào website thì hàm này sẽ được lặp lại một lần nữa và cập nhật vào database, cứ như thế, như thế sau mỗi lần khách truy cập, đó là lý do tại sao nếu website bạn đông lượt truy cập thì có thể tính năng này sẽ chiếm khá nhiều tài nguyên vì sửa dữ liệu liên tục.
3. Viết code hiển thị lượt xem
Ở code trên chúng ta chỉ tiến hành viết code cho việc cập nhật lượt xem thôi chứ chưa hiển thị ra, nếu bạn có nhu cầu hiển thị lượt xem ra trang nội dung bài viết thì chèn thêm đoạn sau vào file plugins.php
function postview_get($postID){
$count_key = ‘postview_number’;
$count = get_post_meta($postID, $count_key, true);
if($count==”){
delete_post_meta($postID, $count_key);
add_post_meta($postID, $count_key, ‘0’);
return "0 lượt xem";
}
return $count.’ lượt xem’;
}
Đồng thời chèn thêm đoạn này vào dưới nó để tránh tình trạng một truy cập mà cộng tới 2 lượt xem:
remove_action( ‘wp_head’, ‘adjacent_posts_rel_link_wp_head’, 10, 0);
Đoạn code này cũng tương tự như code đếm lượt xem thôi, chỉ khác một cái là nó return thêm biến $count
ra ngoài để hiển thị giá trị của meta key postview_number
, tức là lượt xem. Sau đó bạn chèn đoạn sau vào file single.php ở vị trí cần hiển thị lượt xem, dĩ nhiên là phải đặt trong cặp while:
<?php echo postview_get(get_the_ID()); ?>
Tất nhiên bạn có thể chèn ở vị trí nào nếu thích trong cặp while vì mỗi theme có cấu trúc khác nhau và ý mỗi người mỗi khác nên mình không thể chỉ ra vị trí cụ thể để chèn được. Nhưng đẹp nhất là chèn kế bên các đoạn code hiển thị lượt bình luận, category, tên tác giả. Nếu bạn thấy file single.php của bạn có đoạn get_template_part('content',....)
thì cứ tìm file content.php và content-xxx.php trong theme rồi chèn code vào tất cả file đó cũng được.
Kết quả sau khi chèn:
Lưu ý khi dùng các plugin tạo cache cho website như WP Super Cache, W3 Total Cache thì nó sẽ hiển thị lượt xem không chính xác và hàm này cũng vô tác dụng. Lý do là cache sẽ được lưu dưới dạng file tĩnh .html nên giá trị sẽ không thay đổi khi họ truy cập cho đến khi cache được xóa. Nhưng database cache và object cache thì vẫn làm việc, mình test thì thấy như vậy còn đối với bạn thế nào thì không chắc chắn lắm.
4. Viết widget hiển thị bài xem nhiều
Trước hết, để hiểu hết toàn bộ code nếu bạn chưa biết thì nên đọc qua hai bài này:
Và code tạo widget hiển thị bài được xem nhiều ta sẽ có như sau, chèn vào file plugins.php luôn nhé:
/* Tạo widget hiển thị bài xem nhiều
* @tham khảo tại http://bit.ly/1tY8TFn
*/function create_topview_widget() {
register_widget( ‘TopView_Widget’ );
}
add_action( ‘widgets_init’, ‘create_topview_widget’ );class TopView_Widget extends WP_Widget {
/*
* Thiết lập tên widget và description của nó (Appearance -> Widgets)
*/
function TopView_Widget() {
$options = array(
‘classname’ => ‘topview’,
‘description’ => ‘Xem bài viết xem nhiều nhất’
);
$this->WP_Widget(‘topview’, ‘Top View’, $options);
}/*
* Tạo form điền tham số cho widget
* ở đây ta có 3 form là title, postnum (số lượng bài) và postdate (tuổi của bài
*/
function form($instance) {
$default = array(
‘title’ => ‘Bài xem nhiều nhất’,
‘postnum’ => 5,
‘postdate’ => 30
);
$instance = wp_parse_args( (array) $instance, $default );
$title = esc_attr( $instance );
$postnum = esc_attr( $instance );
$postdate = esc_attr( $instance );echo "<label>Tiêu đề:</label> <input class=’widefat’ type=’text’ name=’".$this->get_field_name(‘title’)."’ value=’".$title."’ />";
echo "<label>Số lượng bài viết:</label> <input class=’widefat’ type=’number’ name=’".$this->get_field_name(‘postnum’)."’ value=’".$postnum."’ />";
echo "<label>Độ tuổi của bài viết (ngày)</label> <input class=’widefat’ type=’number’ name=’".$this->get_field_name(‘postdate’)."’ value=’".$postdate."’ />";
}/*
* Cập nhật dữ liệu nhập vào form tùy chọn trong database
*/
function update($new_instance, $old_instance) {
$instance = $old_instance;
$instance = strip_tags($new_instance);
$instance = strip_tags($new_instance);
$instance = strip_tags($new_instance);
return $instance;
}function widget($args, $instance) {
global $postdate; // Thiết lập biến $postdate là biến toàn cục để dùng ở hàm filter_where
extract( $args );
$title = apply_filters( ‘widget_title’, $instance );
$postnum = $instance;
$postdate = $instance;echo $before_widget;
echo $before_title.$title.$after_title;$query_args = array(
‘posts_per_page’ => $postnum,
‘meta_key’ => ‘postview_number’,
‘orderby’ => ‘meta_value_num’,
‘order’ => ‘DESC’,
‘ignore_sticky_posts’ => -1
);/*
* Cách lấy bài viết theo độ tuổi (-30 days = lấy bài được 30 ngày tuổi)
* @tham khảo tại http://bit.ly/1y7WXFp
*/
function filter_where( $where = ” ) {
global $postdate;
$where .= " AND post_date > ‘" . date(‘Y-m-d’, strtotime(‘-‘.$postdate.’ days’)) . "’";
return $where;
}
add_filter( ‘posts_where’, ‘filter_where’ );$postview_query = new WP_Query( $query_args );
remove_filter( ‘posts_where’, ‘filter_where’ ); // Xóa filter để tránh ảnh hưởng đến query khác
if ($postview_query->have_posts() ) :
echo "<ul>";
while ( $postview_query->have_posts() ) :
$postview_query->the_post(); ?><li>
<?php /* Bỏ comment nếu muốn hiện thumbnail
if ( has_post_thumbnail() )
the_post_thumbnail( ‘thumbnail’ );
else
echo "</br><img src=’http://dummyimage.com/50/000/fff&text=thach’>";
*/
?>
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</li><?php endwhile;
echo "</ul>";
endif;
echo $after_widget;
}
}
Và bây giờ bạn vào Appearance -> Widget và kéo widget tên Top View vào sidebar và đặt tùy chọn cho nó là xong.
Nếu bạn đang sử dụng plugin WP-PostViews và muốn sử dụng widget này cho plugin đó thì chỉ việc đổi 'meta_key' => 'postview_number'
thành 'meta_key' => 'views'
.
5. Thêm CSS cho widget
Để widget mặc định thì nó chuối quá, ở đây mình có 1 vài đoạn CSS ngắn để bạn làm cái widget top bài xem nhiều nó có cái gì đó đặc biệt hơn một xíu. Tuy nhiên do chỉ là demo nên mình chẳng có làm gì nhiều ngoài việc đánh số thứ tự ho danh sách bài cả, do đó hãy tự viết thêm CSS nếu cần.
Chèn đoạn sau vào file styles.css
.topview ul {
counter-reset: my-badass-counter;
}
.topview li:before {
content: counter(my-badass-counter);
counter-increment: my-badass-counter;
padding: 5px 15px 8px 0;
line-height: 1em;
color: #7f7f7f;
font-weight: bold;
font-family: Arial!important;
color: #A6A6A6;
float: left;
}
.topview li {
clear: both;
margin: 10px 0;
overflow: hidden;
}
.topview img {
float: left;
margin-right: 5px;
width: 50px;
height: 50px;
}
Đồng thời chèn đoạn sau vào file plugins.php để nó tự chèn file styles.css của plugin vào theme.
/*
* Chèn CSS của plugin vào theme
*/
function custom_styles() {wp_register_style( ‘topview-css’, plugins_url( ‘styles.css’, __FILE__ ) , false, false, ‘all’ );
wp_enqueue_style( ‘topview-css’ );}
add_action( ‘wp_enqueue_scripts’, ‘custom_styles’ );
Bây giờ ta tạm có kết quả:
Vậy là xong bài rồi đấy. :D
6. Toàn bộ code trong bài
File plugins.php
<?php
/*
Plugin Name: PostView
Plugin Author: ThachPham
Description: Plugin đếm lượt xem bài viết và hiển thị top bài xem nhiều
Version: 1.0
Author URI: https://thachpham.com
*/// Set post view
function postview_set($postID) {
$count_key = ‘postview_number’;
$count = get_post_meta($postID, $count_key, true);
if($count==”){
$count = 0;
delete_post_meta($postID, $count_key);
add_post_meta($postID, $count_key, ‘0’);
}else{
$count++;
update_post_meta($postID, $count_key, $count);
}
}
// Get post view
function postview_get($postID){
$count_key = ‘postview_number’;
$count = get_post_meta($postID, $count_key, true);
if($count==”){
delete_post_meta($postID, $count_key);
add_post_meta($postID, $count_key, ‘0’);
return "0 lượt xem";
}
return $count.’ lượt xem’;
}remove_action( ‘wp_head’, ‘adjacent_posts_rel_link_wp_head’, 10, 0);
/* Tạo widget hiển thị bài xem nhiều
* @tham khảo tại http://bit.ly/1tY8TFn
*/function create_topview_widget() {
register_widget( ‘TopView_Widget’ );
}
add_action( ‘widgets_init’, ‘create_topview_widget’ );class TopView_Widget extends WP_Widget {
/*
* Thiết lập tên widget và description của nó (Appearance -> Widgets)
*/
function TopView_Widget() {
$options = array(
‘classname’ => ‘topview’,
‘description’ => ‘Xem bài viết xem nhiều nhất’
);
$this->WP_Widget(‘topview’, ‘Top View’, $options);
}/*
* Tạo form điền tham số cho widget
* ở đây ta có 3 form là title, postnum (số lượng bài) và postdate (tuổi của bài
*/
function form($instance) {
$default = array(
‘title’ => ‘Bài xem nhiều nhất’,
‘postnum’ => 5,
‘postdate’ => 30
);
$instance = wp_parse_args( (array) $instance, $default );
$title = esc_attr( $instance );
$postnum = esc_attr( $instance );
$postdate = esc_attr( $instance );echo "<label>Tiêu đề:</label> <input class=’widefat’ type=’text’ name=’".$this->get_field_name(‘title’)."’ value=’".$title."’ />";
echo "<label>Số lượng bài viết:</label> <input class=’widefat’ type=’number’ name=’".$this->get_field_name(‘postnum’)."’ value=’".$postnum."’ />";
echo "<label>Độ tuổi của bài viết (ngày)</label> <input class=’widefat’ type=’number’ name=’".$this->get_field_name(‘postdate’)."’ value=’".$postdate."’ />";
}/*
* Cập nhật dữ liệu nhập vào form tùy chọn trong database
*/
function update($new_instance, $old_instance) {
$instance = $old_instance;
$instance = strip_tags($new_instance);
$instance = strip_tags($new_instance);
$instance = strip_tags($new_instance);
return $instance;
}function widget($args, $instance) {
global $postdate; // Thiết lập biến $postdate là biến toàn cục để dùng ở hàm filter_where
extract( $args );
$title = apply_filters( ‘widget_title’, $instance );
$postnum = $instance;
$postdate = $instance;echo $before_widget;
echo $before_title.$title.$after_title;$query_args = array(
‘posts_per_page’ => $postnum,
‘meta_key’ => ‘postview_number’,
‘orderby’ => ‘meta_value_num’,
‘order’ => ‘DESC’,
‘ignore_sticky_posts’ => -1
);/*
* Cách lấy bài viết theo độ tuổi (-30 days = lấy bài được 30 ngày tuổi)
* @tham khảo tại http://bit.ly/1y7WXFp
*/
function filter_where( $where = ” ) {
global $postdate;
$where .= " AND post_date > ‘" . date(‘Y-m-d’, strtotime(‘-‘.$postdate.’ days’)) . "’";
return $where;
}
add_filter( ‘posts_where’, ‘filter_where’ );$postview_query = new WP_Query( $query_args );
remove_filter( ‘posts_where’, ‘filter_where’ ); // Xóa filter để tránh ảnh hưởng đến query khác
if ($postview_query->have_posts() ) :
echo "<ul>";
while ( $postview_query->have_posts() ) :
$postview_query->the_post(); ?><li>
<?php /* Bỏ comment nếu muốn hiện thumbnail
if ( has_post_thumbnail() )
the_post_thumbnail( ‘thumbnail’ );
else
echo "</br><img src=’http://dummyimage.com/50/000/fff&text=thach’>";
*/
?>
<a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
</li><?php endwhile;
echo "</ul>";
endif;
echo $after_widget;
}
}/*
* Chèn CSS của plugin vào theme
*/
function custom_styles() {wp_register_style( ‘topview-css’, plugins_url( ‘styles.css’, __FILE__ ) , false, false, ‘all’ );
wp_enqueue_style( ‘topview-css’ );}
add_action( ‘wp_enqueue_scripts’, ‘custom_styles’ );
File style.css
.topview ul {
counter-reset: my-badass-counter;
}
.topview li:before {
content: counter(my-badass-counter);
counter-increment: my-badass-counter;
padding: 5px 15px 8px 0;
line-height: 1em;
color: #7f7f7f;
font-weight: bold;
font-family: Arial!important;
color: #A6A6A6;
float: left;
}
.topview li {
clear: both;
margin: 10px 0;
overflow: hidden;
}
.topview img {
float: left;
margin-right: 5px;
width: 50px;
height: 50px;
}
Giải pháp đếm view và tương thích với cache
Trong bài này mình chỉ hướng dẫn bạn làm tính năng đếm view đơn giản nên nó sẽ không thể tương thích với cache vì lý do mình đã giải thích trong bài rồi. Nhưng mình nghĩ có một giải pháp khác có thể làm cái đếm view này rất tốt và tương thích với cache đó là sử dụng AJAX để sửa đổi giá trị meta key và sử dụng AJAX để lấy dữ liệu trong database để hiển thị ra ngoài.
Lý do sử dụng AJAX có thể tương thích với cache đó là dù trang bạn có cache hay không có cache thì các file Javascript vẫn xử lý bình thường, do vậy việc sử dụng AJAX bạn có thể hiểu là nó sẽ chỉ lấy dữ liệu ra sau khi trang được tải xong nên việc có cache hay không đều không quan trọng. Nhưng còn cụ thể thế nào thì mình sẽ test nhiều hơn rồi viết hướng dẫn sau.
Có thể bạn thích: Plugin đếm lượt view bằng AJAX làm việc với cache.
Lời kết
Bài tới đây có vẻ cũng dài rồi, và hy vọng với cách giải thích của mình bạn có thể hiểu được tính năng này làm việc như thế nào và quan trọng nhất là cách xây dựng bộ tính năng này. Bài vẫn còn cơ bản nên sẽ còn nghèo ý tưởng, nếu bạn có thêm ý tưởng nào để phát triển code tối ưu hơn thì hãy chia sẻ thêm cho mình ở phần bình luận nhé.