Saya pikir pilihan terbaik adalah titik akhir. Anda mendapatkan semua data sebagai string sederhana, sehingga Anda dapat memutuskan bagaimana data tersebut akan diuraikan, dan Anda tidak perlu khawatir tentang tabrakan dengan aturan penulisan ulang lainnya.
Satu hal yang saya pelajari tentang titik akhir: menjaga pekerjaan utama seabstrak mungkin, memperbaiki gangguan di WordPress API dengan cara agnostik data.
Saya akan memisahkan logika menjadi tiga bagian: pengontrol memilih model dan tampilan, model untuk menangani titik akhir dan satu atau lebih tampilan untuk mengembalikan beberapa data yang berguna atau pesan kesalahan.
Pengontrol
Mari kita mulai dengan pengontrolnya. Tidak banyak, jadi saya menggunakan fungsi yang sangat sederhana di sini:
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 );
}
Pada dasarnya, ini memuat model T5_CRA_Model
dan menyerahkan beberapa parameter ... dan semua pekerjaan. Pengontrol tidak tahu apa-apa tentang logika bagian dalam model atau tampilan. Itu hanya menempel keduanya. Ini adalah satu-satunya bagian yang tidak dapat Anda gunakan kembali; itu sebabnya saya memisahkannya dari bagian lain.
Sekarang kita membutuhkan setidaknya dua kelas: model yang mendaftarkan API dan tampilan untuk membuat output.
Model
Kelas ini akan:
- daftarkan titik akhir
- menangkap kasus di mana ada titik akhir dipanggil tanpa parameter tambahan
- isi aturan penulisan ulang yang tidak ada karena beberapa bug dalam kode pihak ketiga
- perbaiki kesalahan WordPress dengan halaman depan dan titik akhir statis untuk
EP_ROOT
- parsing URI ke dalam array (ini bisa dipisahkan juga)
- memanggil penangan panggilan balik dengan nilai-nilai itu
Saya harap kode berbicara sendiri. :)
Model tidak tahu apa-apa tentang struktur bagian dalam data atau tentang presentasi. Jadi, Anda dapat menggunakannya untuk mendaftarkan ratusan API tanpa mengubah satu baris.
<?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 );
}
}
Pandangan
Sekarang kita harus melakukan sesuatu dengan data kita. Kami juga dapat menangkap data yang hilang untuk permintaan yang tidak lengkap atau mendelegasikan penanganan ke tampilan lain atau sub-pengendali.
Ini adalah contoh yang sangat sederhana:
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 ) {}
}
Bagian yang penting adalah: pandangan tidak tahu apa-apa tentang titik akhir. Anda dapat menggunakannya untuk menangani permintaan yang sangat berbeda, misalnya permintaan AJAX di wp-admin
. Anda dapat membagi tampilan menjadi pola MVC sendiri atau hanya menggunakan fungsi sederhana.