Cara Membuat Aplikasi Web Modern Menggunakan WordPress Dan React
Belakangan ini, saya sedang kerjakan program React untuk konsumen saat mereka ajukan pertanyaan ini ke saya: 'Bisakah kita memakainya dengan WordPress?'
Semenjak akhir 2015, jawaban atas pertanyaan ini adalah ya. Tapi beberapa langkah yang dibutuhkan untuk membikin situs terpisah yang berperan kemungkinan tidak gampang, khususnya untuk mereka yang tidak terlatih dengan WordPress dan React.
Diperjalanan saya untuk membikin program yang berperan, saya menjumpai beberapa masalah yang sulit, dan dalam artikel ini, saya akan menerangkan langkah menghindariinya. Saya akan membagi beberapa panduan dan trick yang saya dalami sejauh ini!
Bagian 1: Informasi Latar Belakang
Apakah itu CMS Tanpa Kepala?
Dahulu, memakai CMS seperti WordPress memiliki arti Anda harus membuat frontend memakai PHP.
Saat ini, dengan CMS tanpa kepala, Anda bisa membuat ujung depan dengan tehnologi apa saja yang Anda suka; ini karena pembelahan front-end dan back-end lewat API. Bila Anda ingin membuat SPA (program satu halaman) memakai React, Angular atau Vue, dan mengatur content memakai CMS seperti WordPress, Anda dapat!
Apa yang Harus Saya Tahu untuk Diikuti?
Anda akan memperoleh hasil maksimal dari artikel ini bila Anda mempunyai:
- beberapa pengetahuan mengenai langkah kerja CMS seperti WordPress, sedikit PHP, dan ide mengenai langkah mempersiapkan project WordPress dasar di computer Anda;
- pengetahuan mengenai JavaScript, terhitung feature bahasa ES6+ dan sintaks kelas React.
Akronim Kunci
Pemrograman sarat dengan slogan, tapi membuat bisa lebih cepat untuk mengulas beberapa ide dalam artikel ini. Berikut rekap cepat istilah yang hendak kami pakai:
- CMS — mekanisme management content. Pikir WordPress, Drupal, Joomla, Magneto.
- SPA — program satu halaman. Dibanding berisi ulangi tiap halaman keseluruhannya, program SPA berisi konten secara aktif. Kode dasar (HTML, CSS, dan JavaScript) website termuat cuma sekali. Pikir React, Vue, Angular.
- API — antar-muka pemrograman program. Dalam istilah simpel, rangkaian pengertian, yang disiapkan service untuk memungkinkannya Anda ambil dan memakai datanya. Google Maps punyai satu. Sedang punyai satu. Dan saat ini, tiap situs WordPress diperlengkapi dengan API bawaan.
- REST - transfer status representasional. Style arsitektur situs berdasar sistem keinginan HTTP: GET , PUT , POST dan DELETE. API bawaan WordPress adalah REST atau "RESTful" API.
- HTTP — prosedur transfer hypertext. Kelompok ketentuan yang dipakai untuk mentransfer data lewat situs. Itu ditetapkan pada awal URL sebagai http atau https (versus aman).
- JSON — notasi objek JavaScript. Walau datang dari JavaScript, ini ialah pola bebas bahasa untuk penyimpanan dan data transfer.
Pada artikel ini, kami memakai WordPress sebagai CMS kami. Itu memiliki arti memprogram back-end kami dalam PHP dan memakai REST API WordPress untuk mengirim data JSON ke frontend kami.
Di Mana Saya Dapat Melihat Data JSON WordPress?
Sebelum membahas hal-hal bagus, catatan singkat tentang di mana Anda dapat menemukan data JSON di situs WordPress Anda. Saat ini, setiap situs web WordPress memiliki data JSON yang tersedia (kecuali jika pemilik situs telah menonaktifkan atau membatasi aksesnya). Anda melihat JSON utama dari situs WordPress dengan menambahkan/wp-json
ke nama domain root.
Maka misalkan, Anda bisa menyaksikan JSON untuk WordPress.org dengan mengunjungi https://wordpress.org/wp-json. Atau, bila Anda jalankan situs WordPress secara lokal, Anda bisa melihat JSON-nya dengan ikuti localhost/yoursitename/wp-json
Untuk mengakses data postingan Anda, ketik localhost/yoursitename/wp-json/wp/v2/posts
. Untuk format kiriman khusus, tukar dalam format baru (mis. Film
), bukan kiriman
. Apa yang sekarang tampak seperti blok teks yang tidak dapat dibaca adalah apa yang memungkinkan kita menggunakan WordPress sebagai CMS tanpa kepala!
Bagian 2: WordPress
Untuk mempersiapkan REST API Anda, sebagian besar yang perlu Anda lakukan akan terjadi di file functions
.php Anda. Saya akan memandang Anda ketahui langkah mempersiapkan project WordPress dan mengaksesnya memakai localhost
, tapi bila Anda memerlukan dana untuk itu, saya mereferensikan artikel ini (itu yang saya pakai untuk mengawali pemrograman dengan WordPress).
Untuk mayoritas project, Anda ingin memakai tipe posting khusus, jadi silahkan kita awali dengan mempersiapkannya.
Menambahkan Jenis Posting Kustom
Katakanlah situs kita tentang film, dan kita menginginkan jenis postingan yang disebut 'film'. Pertama, kami ingin memastikan jenis posting 'film' kami dimuat sesegera mungkin, jadi kami akan melampirkannya ke pengait init
, menggunakan add_action
:
add_action( 'init', 'movies_post_type' );
Saya menggunakan movies_post_type()
, tetapi Anda dapat memanggil fungsi apa pun yang Anda inginkan.
Selanjutnya, kami ingin mendaftarkan 'film' sebagai jenis posting, menggunakan fungsi register_post_type()
.
Potongan kode berikutnya mungkin terlihat luar biasa, tetapi ini relatif sederhana: fungsi kami memerlukan banyak argumen bawaan untuk mengontrol fungsionalitas jenis kiriman baru Anda, dan sebagian besar sudah cukup jelas. Kami akan menyimpan argumen ini di array $args
kami.
Salah satu argumen kami, labels
, dapat mengambil banyak argumen berbeda, jadi kami membaginya menjadi array terpisah, $labels
, memberi kami:
<?php // Register Custom Post Type: Movies
function movies_post_type() {
$labels = array(
'name' => _x( 'Movies', 'Post Type General Name', 'text_domain' ),
'singular_name' => _x( 'Movie', 'Post Type Singular Name', 'text_domain' ),
'menu_name' => __( 'Movies', 'text_domain' ),
'name_admin_bar' => __( 'Movie', 'text_domain' ),
'archives' => __( 'Item Archives', 'text_domain' ),
'attributes' => __( 'Item Attributes', 'text_domain' ),
'parent_item_colon' => __( 'Parent Item:', 'text_domain' ),
'all_items' => __( 'All Items', 'text_domain' ),
'add_new_item' => __( 'Add New 3D Printer', 'text_domain' ),
'add_new' => __( 'Add New', 'text_domain' ),
'new_item' => __( 'New Movie', 'text_domain' ),
'edit_item' => __( 'Edit Movie', 'text_domain' ),
'update_item' => __( 'Update Movie', 'text_domain' ),
'view_item' => __( 'View Item', 'text_domain' ),
'view_items' => __( 'View Items', 'text_domain' ),
'search_items' => __( 'Search Item', 'text_domain' ),
'not_found' => __( 'Not found', 'text_domain' ),
'not_found_in_trash' => __( 'Not found in Trash', 'text_domain' ),
'featured_image' => __( 'Featured Image', 'text_domain' ),
'set_featured_image' => __( 'Set featured image', 'text_domain' ),
'remove_featured_image' => __( 'Remove featured image', 'text_domain' ),
'use_featured_image' => __( 'Use as featured image', 'text_domain' ),
'insert_into_item' => __( 'Insert into item', 'text_domain' ),
'uploaded_to_this_item' => __( 'Uploaded to this item', 'text_domain' ),
'items_list' => __( 'Items list', 'text_domain' ),
'items_list_navigation' => __( 'Items list navigation', 'text_domain' ),
'filter_items_list' => __( 'Filter items list', 'text_domain' ),
);
$args = array(
'label' => __( 'Movie', 'text_domain' ),
'description' => __( 'Our featured films.', 'text_domain' ),
'labels' => $labels,
'supports' => array( 'title', 'editor', 'thumbnail'),
'taxonomies' => array( '' ),
'hierarchical' => false,
'public' => true,
'show_in_rest' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 5,
'show_in_admin_bar' => true,
'show_in_nav_menus' => true,
'can_export' => true,
'has_archive' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'capability_type' => 'page',
);
register_post_type( 'movies', $args );
}
add_action( 'init', 'movies_post_type', 0 );
Two of the most important arguments are 'supports'
and 'taxomonies'
, because these kontrol which of the native post fields will be accessible in our new post tipe.
In the above kode, we've opted for just three 'supports'
:
- 'title'— the judul of each post.
- 'editor'— the primary teks editor, which we'll use for our description.
- 'thumbnail'— the post's fiturd gambar.
To see the full daftar of what's available, klik here for supports, and here for taxonomies.
Generate WordPress also has a handy tool to help you kode kustom post tipes, which can make the process a lot quicker.
Mengubah Teks Placeholder Judul
Jika teks placeholder judul "masukkan judul di sini" bisa sedikit menyesatkan untuk jenis kiriman khusus Anda, Anda dapat mengeditnya dalam fungsi terpisah:
<?php // Change title placeholder text for a custom post type
function change_title_text( $title ){
$screen = get_current_screen();
if ( 'movies' == $screen->post_type ) {
$title = 'Enter movie name here';
}
return $title;
}
add_filter( 'enter_title_here', 'change_title_text' );
Menambahkan Bidang Kustom ke Jenis Posting Kustom Anda
Bagaimana jika Anda menginginkan bidang yang tidak ditentukan sebelumnya oleh WordPress? Misalnya, katakanlah kita menginginkan bidang khusus yang disebut "Genre". Dalam hal ini, Anda harus menggunakan add_meta_boxes()
.
Karena, kita perlu melampirkan fungsi baru ke pengait add_meta_boxes
WordPress:
add_action( 'add_meta_boxes', 'genre_meta_box' );
Di dalam fungsi baru kita, kita perlu memanggil fungsi add_meta_box()
WordPress, seperti:
function genre_meta_box() {
add_meta_box(
'global-notice',
__( 'Genre', 'sitepoint' ),
'genre_meta_box_callback',
'movies',
'side',
'low'
);
}
Anda dapat membaca lebih lanjut tentang argumen fungsi ini di sini. Untuk tujuan kita, bagian terpenting adalah fungsi callback, yang kita beri nama genre_meta_box_callback
. Ini mendefinisikan konten aktual pada kotak meta. Kami hanya membutuhkan input teks sederhana, sehingga kami dapat menggunakan:
function genre_meta_box_callback() {
global $post;
$custom = get_post_custom($post->ID);
$genre = $custom["genre"][0];
?>
<input style="width:100%" name="genre" value="<?php
echo $genre; ?>" />
<?php
};
Terakhir, bidang khusus kami tidak akan menyimpan nilainya kecuali kami memintanya. Untuk tujuan ini, kita dapat mendefinisikan fungsi baru save_genre()
dan melampirkannya ke hook save_post
WordPress:
function save_genre(){
global $post;
update_post_meta($post->ID, "printer_category",
$_POST["printer_category"]);
};add_action( 'save_post', 'save_genre' );
Bersama-sama, kode yang digunakan untuk membuat bidang khusus akan terlihat seperti ini:
<?php // Add Custom Field Type: Genre
function genre_meta_box() {
add_meta_box(
'global-notice',
__( 'Genre', 'sitepoint' ),
'genre_meta_box_callback',
'movies',
'side',
'low'
);
}
function genre_meta_box_callback() {
global $post;
$custom = get_post_custom($post->ID);
$genre = $custom["genre"][0];
?>
<input style="width:100%" name="genre" value="<?php
echo $genre; ?>" />
<?php
}
function save_genre(){
global $post;
update_post_meta($post->ID, "genre",
$_POST["genre"]);
}
add_action( 'add_meta_boxes', 'genre_meta_box' );
add_action( 'save_post', 'save_genre' );
Membuat Bidang Khusus Tersedia sebagai JSON
Pos khusus kami tersedia secara otomatis sebagai JSON. Untuk jenis posting "film" kami, data JSON kami dapat ditemukan di localhost/yoursitename/wp-json/wp/v2/movies
.
Namun bidang khusus kami tidak secara otomatis menjadi bagian dari ini, jadi kami perlu menambahkan fungsi untuk memastikannya juga dapat diakses melalui REST API.
Kemudian, kita dapat menggunakan fungsi register_rest_field()
bawaan, seperti:
add_action( 'rest_api_init', 'register_genre_as_rest_field' );
Then, we can use the in-built register_rest_field()
function, like so:
function register_genre_as_rest_field() {
register_rest_field(
'movies',
'genre',
array(
'get_callback' => 'get_genre_meta_field',
'update_callback' => null,
'schema' => null,
)
);
};
Fungsi ini mengambil array dengan callback get dan update. Untuk kasus penggunaan yang lebih mudah seperti ini, kita hanya perlu menentukan 'get_callback'
:
function get_genre_meta_field( $object, $field_name, $value ) {
return get_post_meta($object['id'])[$field_name][0];
};
Secara keseluruhan, berikut adalah kode yang diperlukan untuk mendaftarkan bidang khusus.
<?php // Register a Custom Field Type as a REST field
function register_genre_as_rest_field() {
register_rest_field(
'movies',
'genre',
array(
'get_callback' => 'get_genre_meta_field',
'update_callback' => null,
'schema' => null,
)
);
};
function get_genre_meta_field( $object, $field_name, $value ) {
return get_post_meta($object['id'])[$field_name][0];
};
add_action( 'rest_api_init', 'register_genre_as_rest_field' );
Membuat URL Gambar Unggulan Tersedia sebagai JSON
Out-of-the-box, REST API WordPress tidak menyertakan URL untuk gambar unggulan Anda. Untuk memudahkan mengaksesnya, Anda dapat menggunakan kode berikut:
Filter WordPress rest_prepare_posts
bersifat dinamis, sehingga kita dapat menukar jenis kiriman khusus kita sebagai pengganti “postingan”, seperti rest_prepare_movies
.
<?php // Add 'featured_image_url' to REST data
function post_featured_image_json( $data, $post, $context ) {
$featured_image_id = $data->data['featured_media'];
$featured_image_url = wp_get_attachment_image_src( $featured_image_id, 'original' );
if( $featured_image_url ) {
$data->data['featured_image_url'] = $featured_image_url[0];
}
return $data;
}
add_filter( 'rest_prepare_movies', 'post_featured_image_json', 10, 3 );
<?php // Add 'featured_image_url' to REST data
function post_featured_image_json( $data, $post, $context ) {
$featured_image_id = $data->data['featured_media'];
$featured_image_url = wp_get_attachment_image_src( $featured_image_id, 'original' );
if( $featured_image_url ) {
$data->data['featured_image_url'] = $featured_image_url[0];
}
return $data;
}
add_filter( 'rest_prepare_movies', 'post_featured_image_json', 10, 3 );
Membatasi Data JSON yang Terlihat
Kami hampir siap untuk mulai menarik data ke aplikasi React kami, tetapi ada satu lagi pengoptimalan cepat yang dapat kami lakukan, dengan membatasi data yang tersedia.
Beberapa data hadir sebagai standar yang mungkin tidak pernah Anda perlukan di frontend Anda dan — jika demikian — kami dapat menghapusnya menggunakan filter, seperti ini. Anda dapat menemukan nama tipe data dengan melihat bagian /wp-json/wp/v2/movies
di situs web Anda.
Setelah itu selesai, setelah Anda menambahkan beberapa film menggunakan backend WordPress, dan kami memiliki semua yang kami butuhkan untuk mulai memasukkan data ke dalam React!
<?php // Filter REST data
function filter_rest_data( $data, $post, $request ) {
$_data = $data->data;
$params = $request->get_params();
if ( ! isset( $params['id'] ) ) {
unset( $_data['date'] );
unset( $_data['slug'] );
unset( $_data['date_gmt'] );
unset( $_data['modified'] );
unset( $_data['modified_gmt'] );
unset( $_data['guid'] );
unset( $_data['type'] );
};
$data->data = $_data;
return $data;
};
add_filter( 'rest_prepare_movies', 'filter_rest_data', 10, 3 );
Bagian 3: React
Untuk ambil data eksternal dalam JavaScript, Anda perlu memakai janji. Ini peluang akan berimplikasi pada langkah Anda ingin membuat elemen React Anda, dan dalam kasus saya (mengonversi project React yang ada), saya harus menulis kembali code yang lumayan banyak.
Promise dalam JavaScript
Promise dalam JavaScript dipakai untuk tangani perlakuan asinkron — beberapa hal yang terjadi di luar posisi eksekusi langkah setiap langkah atau "sesuai" yang umum (sesudah pengangkatan).
Berita baiknya adalah JavaScript asinkron lebih gampang dibanding awalnya. Saat sebelum ES6, kami tergantung pada peranan callback. Bila beberapa callback dibutuhkan (dan memang kerap demikian), nesting akan hasilkan kode yang paling susah untuk dibaca, diskalakan, dan di-debug — sebuah peristiwa yang kadang dikenali sebagai callback hell, atau piramida keruntuhan!
Janji diperkenalkan di ES6 (atau ES2015) untuk mengatasi masalah itu, dan ES8 (atau ES2018) melihat pengenalan async ... await
, dua kata kunci yang semakin menyederhanakan fungsionalitas asinkron. Namun untuk tujuan kita, metode berbasis janji yang paling penting adalah fetch()
.
Fetch Method
Metode ini telah tersedia sejak Chrome 40, dan merupakan alternatif yang lebih mudah digunakan untuk XMLHttpRequest()
.
fetch()
mengembalikan sebuah promise sehingga "then-able", artinya Anda dapat menggunakan metode then()
untuk memproses hasilnya.
Anda bisa menambahkan metode pengambilan di dalam komponen kelas React Anda, seperti:
import React, { Component } from 'react';
export default class Movies extends Component {
constructor(props) {
super(props);
this.state = { data: {} };
this.fetchPostData = this.fetchPostData.bind(this);
this.renderMovies = this.renderMovies.bind(this);
this.populatePageAfterFetch = this.populatePageAfterFetch.bind(this);
}
componentDidMount() {
this.fetchPostData();
}
fetchPostData() {
fetch(`http://localhost/yoursitename/wp-json/wp/v2/movies?per_page=100`)
.then(response => response.json())
.then(myJSON => {
let objLength = Object.keys(myJSON).length;
let newState = this.state;
for (let i = 0; i < objLength; i++) {
let objKey = Object.values(myJSON)[i].title.rendered;
let currentMovie = newState.data[objKey];
currentMovie = {};
currentMovie.name = Object.values(myJSON)[i].title.rendered;
currentMovie.description = Object.values(myJSON)[i].content.rendered;
currentMovie.featured_image = Object.values(myJSON)[i]['featured_image_url'];
currentMovie.genre = Object.values(myJSON)[i]['genre'];
}
this.setState(newState);
});
}
renderMovies() {
if (this.state.data) {
const moviesArray = Object.values(this.state.data)
return Object.values(moviesArray).map((movie, index) => this.populatePageAfterFetch(movie, index));
}
}
populatePageAfterFetch(movie, index) {
if (this.state.data) {
return (
<div key={index} index={index}>
<h2 className="name">{movie.name}</h2>
<h3 className="genre">{movie.genre}</h3>
<div className="description" dangerouslySetInnerHTML={{__html: movie.description}} />
<img className="featured_image" src={movie.featured_image} alt={movie.name} />
</div>
)
}
}
render() {
return (
<>
<h1>Movies</h1>
<div>{this.renderMovies()}</div>
</>
)
}
}
constructor (props)
Dalam metode ini, kami memanggil super(props)
, menentukan status awal kami (objek data
kosong) dan mengikat tiga metode baru:
-
ambilPostData()
-
renderMovies()
-
populatePageAfterFetch()
constructor(props) {
super(props);
this.state = { data: {} };
this.fetchPostData = this.fetchPostData.bind(this);
this.renderMovies = this.renderMovies.bind(this);
this.populatePageAfterFetch = this.populatePageAfterFetch.bind(this);
}
komponen DidMount()
Kami ingin mengambil data kami segera setelah komponen terpasang, jadi kami akan memanggil fetchPostData()
di sini.
componentDidMount() {
this.fetchPostData();
}
view raw
Fetch Post Data()
Kami mengambil JSON dari URL kami, meneruskan .json()
dalam metode .then()
pertama.
Dalam metode .then()
kedua, kami mengekstrak empat nilai yang kami inginkan untuk setiap entri film yang kami ambil dan kemudian menambahkannya ke objek newState
kami.
Kami kemudian menggunakan this.setState(newState)
untuk menambahkan informasi ini ke this.state.data
.
fetchPostData() {
fetch(`http://localhost/yoursitename/wp-json/wp/v2/movies?per_page=100`)
.then(response => response.json())
.then(myJSON => {
let objLength = Object.keys(myJSON).length;
let newState = this.state;
for (let i = 0; i < objLength; i++) {
let objKey = Object.values(myJSON)[i].title.rendered;
let currentMovie = newState.data[objKey];
currentMovie = {};
currentMovie.name = Object.values(myJSON)[i].title.rendered;
currentMovie.description = Object.values(myJSON)[i].content.rendered;
currentMovie.featured_image = Object.values(myJSON)[i]['featured_image_url'];
currentMovie.genre = Object.values(myJSON)[i]['genre'];
}
this.setState(newState);
});
}
renderMovies()
Kondisional if (this.state.data)
berarti bahwa fungsi hanya akan berjalan setelah data diambil.
Di sini, kami mengambil larik dari semua film yang diambil dari this.state.data
dan meneruskannya ke fungsi populatePageAfterFetch()
.
renderMovies() {
if (this.state.data) {
const moviesArray = Object.values(this.state.data)
return Object.values(moviesArray).map((movie, index) => this.populatePageAfterFetch(movie, index));
}
}
Populate Page After Fetch()
Dalam fungsi ini, kami menyiapkan data untuk setiap film yang akan dirender. Ini seharusnya terlihat mudah bagi siapa saja yang menggunakan JSX, dengan satu potensi batu sandungan.
Nilai movie.description
bukanlah teks biasa, melainkan markup HTML. Untuk menampilkan ini, kita dapat menggunakan dangerlySetInnerHTML={{__html: movie.description}}
.
populatePageAfterFetch(movie, index) {
if (this.state.data) {
return (
<div key={index} index={index}>
<h2 className="name">{movie.name}</h2>
<h3 className="genre">{movie.genre}</h3>
<div className="description" dangerouslySetInnerHTML={{__html: movie.description}} />
<img className="featured_image" src={movie.featured_image} alt={movie.name} />
</div>
)
}
}
Catatan: Alasan mengapa ini berpotensi "berbahaya" adalah, jika data Anda dibajak untuk memuat skrip XSS berbahaya, ini juga akan diuraikan. Karena kami menggunakan server/CMS kami sendiri dalam artikel ini, kami tidak perlu khawatir. Tetapi jika Anda ingin membersihkan HTML Anda, lihatlah DOMPurify.
Render()
Terakhir, kita mengontrol di mana data yang dirender akan muncul dengan memanggil metode renderMovies()
dalam tag <div>
pilihan kita. Kami sekarang telah berhasil mengambil data dari situs WordPress kami dan menampilkannya!
render() {
return (
<>
<h1>Movies</h1>
<div>{this.renderMovies()}</div>
</>
)
}
Kesimpulan
Keseluruhannya, saya berharap artikel ini membuat proses menghubungkan front-end React ke back-end WordPress segampang mungkin.
Seperti beberapa hal dalam pemrograman, apa yang kelihatan mengerikan untuk memulai dengan cepat jadi kebiasaan dengan latihan!
Saya akan sangat tertarik untuk dengar mengenai pengalaman Anda sendiri memakai WordPress sebagai CMS tanpa kepala, dan dengan suka hati saya jawab pertanyaan apa saja di komentar.