Menggunakan rewrite API untuk membuat URL yang tenang

19

Saya mencoba membuat aturan penulisan ulang untuk API yang tenang. Saya hanya ingin melihat apakah ada cara yang lebih baik untuk membuat pekerjaan ini daripada harus menulis setiap kombinasi penulisan ulang yang mungkin.

Oke, jadi saya punya 4 variabel kueri untuk dimasukkan ke dalam URL

  • Indikator
  • Negara
  • Tanggapan
  • Survei

Url dasar akan menjadi www.example.com/some-page/ Urutan keempat variabel akan konsisten tetapi beberapa variabel kueri bersifat opsional.

Jadi saya bisa ...

/indicator/{indicator value}/country/{country value}/response/{response value}/survey/{survey value}/

atau ... (tidak ada / respons /)

/indicator/{indicator value}/country/{country value}/survey/{survey value}/

atau...

/indicator/{indicator value}/country/{country value}/

Apakah ada cara yang lebih baik untuk menyelesaikan ini daripada memfilter rewrite_rules_arraydan menambahkan array aturan penulisan ulang saya yang dibuat secara manual? Apakah add_rewrite_endpoint()rewrite_endpoint atau bermanfaat add_rewrite_tag()bagi saya?

kingkool68
sumber

Jawaban:

18

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_Modeldan 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.

fuxia
sumber
2
Gali itu. Saya suka jenis pola ini.
kingkool68