Tôi nghĩ rằng lựa chọn tốt nhất là một điểm cuối. Bạn nhận được tất cả dữ liệu dưới dạng một chuỗi đơn giản, do đó bạn có thể quyết định cách phân tích dữ liệu và bạn không phải lo lắng về việc va chạm với các quy tắc viết lại khác.
Một điều tôi học được về các điểm cuối: giữ cho công việc chính càng trừu tượng càng tốt, khắc phục các trục trặc trong API của WordPress theo cách không thể tin được về dữ liệu.
Tôi sẽ tách logic thành ba phần: bộ điều khiển chọn một mô hình và một khung nhìn, một mô hình để xử lý điểm cuối và một hoặc nhiều khung nhìn để trả về một số dữ liệu hữu ích hoặc thông báo lỗi.
Bộ điều khiển
Hãy bắt đầu với bộ điều khiển. Nó không làm được gì nhiều, vì vậy tôi sử dụng một chức năng rất đơn giản ở đây:
add_action( 'plugins_loaded', 't5_cra_init' );
function t5_cra_init()
{
require dirname( __FILE__ ) . '/class.T5_CRA_Model.php';
$options = array (
'callback' => array ( 'T5_CRA_View_Demo', '__construct' ),
'name' => 'api',
'position' => EP_ROOT
);
new T5_CRA_Model( $options );
}
Về cơ bản, nó tải mô hình T5_CRA_Model
và bàn giao một số thông số và tất cả các công việc. Bộ điều khiển không biết gì về logic bên trong của mô hình hoặc khung nhìn. Nó chỉ dính cả hai với nhau. Đây là phần duy nhất bạn không thể sử dụng lại; đó là lý do tại sao tôi giữ nó tách biệt với các phần khác.
Bây giờ chúng ta cần ít nhất hai lớp: mô hình đăng ký API và khung nhìn để tạo đầu ra.
Ngươi mâu
Lớp học này sẽ:
- đăng ký điểm cuối
- bắt các trường hợp có điểm cuối được gọi mà không có bất kỳ tham số bổ sung nào
- điền quy tắc viết lại bị thiếu do một số lỗi trong mã bên thứ ba
- sửa lỗi WordPress với các trang và điểm cuối tĩnh cho
EP_ROOT
- phân tích URI thành một mảng (điều này cũng có thể được tách ra)
- gọi trình xử lý gọi lại với các giá trị đó
Tôi hy vọng mã nói cho chính nó. :)
Mô hình không biết gì về cấu trúc bên trong của dữ liệu hoặc về bản trình bày. Vì vậy, bạn có thể sử dụng nó để đăng ký hàng trăm API mà không cần thay đổi một dòng.
<?php # -*- coding: utf-8 -*-
/**
* Register new REST API as endpoint.
*
* @author toscho http://toscho.de
*
*/
class T5_CRA_Model
{
protected $options;
/**
* Read options and register endpoint actions and filters.
*
* @wp-hook plugins_loaded
* @param array $options
*/
public function __construct( Array $options )
{
$default_options = array (
'callback' => array ( 'T5_CRA_View_Demo', '__construct' ),
'name' => 'api',
'position' => EP_ROOT
);
$this->options = wp_parse_args( $options, $default_options );
add_action( 'init', array ( $this, 'register_api' ), 1000 );
// endpoints work on the front end only
if ( is_admin() )
return;
add_filter( 'request', array ( $this, 'set_query_var' ) );
// Hook in late to allow other plugins to operate earlier.
add_action( 'template_redirect', array ( $this, 'render' ), 100 );
}
/**
* Add endpoint and deal with other code flushing our rules away.
*
* @wp-hook init
* @return void
*/
public function register_api()
{
add_rewrite_endpoint(
$this->options['name'],
$this->options['position']
);
$this->fix_failed_registration(
$this->options['name'],
$this->options['position']
);
}
/**
* Fix rules flushed by other peoples code.
*
* @wp-hook init
* @param string $name
* @param int $position
*/
protected function fix_failed_registration( $name, $position )
{
global $wp_rewrite;
if ( empty ( $wp_rewrite->endpoints ) )
return flush_rewrite_rules( FALSE );
foreach ( $wp_rewrite->endpoints as $endpoint )
if ( $endpoint[0] === $position && $endpoint[1] === $name )
return;
flush_rewrite_rules( FALSE );
}
/**
* Set the endpoint variable to TRUE.
*
* If the endpoint was called without further parameters it does not
* evaluate to TRUE otherwise.
*
* @wp-hook request
* @param array $vars
* @return array
*/
public function set_query_var( Array $vars )
{
if ( ! empty ( $vars[ $this->options['name'] ] ) )
return $vars;
// When a static page was set as front page, the WordPress endpoint API
// does some strange things. Let's fix that.
if ( isset ( $vars[ $this->options['name'] ] )
or ( isset ( $vars['pagename'] ) and $this->options['name'] === $vars['pagename'] )
or ( isset ( $vars['page'] ) and $this->options['name'] === $vars['name'] )
)
{
// In some cases WP misinterprets the request as a page request and
// returns a 404.
$vars['page'] = $vars['pagename'] = $vars['name'] = FALSE;
$vars[ $this->options['name'] ] = TRUE;
}
return $vars;
}
/**
* Prepare API requests and hand them over to the callback.
*
* @wp-hook template_redirect
* @return void
*/
public function render()
{
$api = get_query_var( $this->options['name'] );
$api = trim( $api, '/' );
if ( '' === $api )
return;
$parts = explode( '/', $api );
$type = array_shift( $parts );
$values = $this->get_api_values( join( '/', $parts ) );
$callback = $this->options['callback'];
if ( is_string( $callback ) )
{
call_user_func( $callback, $type, $values );
}
elseif ( is_array( $callback ) )
{
if ( '__construct' === $callback[1] )
new $callback[0]( $type, $values );
elseif ( is_callable( $callback ) )
call_user_func( $callback, $type, $values );
}
else
{
trigger_error(
'Cannot call your callback: ' . var_export( $callback, TRUE ),
E_USER_ERROR
);
}
// Important. WordPress will render the main page if we leave this out.
exit;
}
/**
* Parse request URI into associative array.
*
* @wp-hook template_redirect
* @param string $request
* @return array
*/
protected function get_api_values( $request )
{
$keys = $values = array();
$count = 0;
$request = trim( $request, '/' );
$tok = strtok( $request, '/' );
while ( $tok !== FALSE )
{
0 === $count++ % 2 ? $keys[] = $tok : $values[] = $tok;
$tok = strtok( '/' );
}
// fix odd requests
if ( count( $keys ) !== count( $values ) )
$values[] = '';
return array_combine( $keys, $values );
}
}
Quan điểm
Bây giờ chúng tôi phải làm một cái gì đó với dữ liệu của chúng tôi. Chúng tôi cũng có thể bắt dữ liệu bị thiếu cho các yêu cầu không đầy đủ hoặc ủy quyền xử lý cho các chế độ xem hoặc bộ điều khiển phụ khác.
Đây là một ví dụ rất đơn giản:
class T5_CRA_View_Demo
{
protected $allowed_types = array (
'plain',
'html',
'xml'
);
protected $default_values = array (
'country' => 'Norway',
'date' => 1700,
'max' => 200
);
public function __construct( $type, $data )
{
if ( ! in_array( $type, $this->allowed_types ) )
die( 'Your request is invalid. Please read our fantastic manual.' );
$data = wp_parse_args( $data, $this->default_values );
header( "Content-Type: text/$type;charset=utf-8" );
$method = "render_$type";
$this->$method( $data );
}
protected function render_plain( $data )
{
foreach ( $data as $key => $value )
print "$key: $value\n";
}
protected function render_html( $data ) {}
protected function render_xml( $data ) {}
}
Phần quan trọng là: khung nhìn không biết gì về điểm cuối. Bạn có thể sử dụng nó để xử lý các yêu cầu hoàn toàn khác nhau, ví dụ như các yêu cầu AJAX trong wp-admin
. Bạn có thể chia khung nhìn thành mẫu MVC của riêng nó hoặc chỉ sử dụng một chức năng đơn giản.