כיצד ליצור אפליקציית אינטרנט מודרנית באמצעות וורדפרס ולהגיב

שלב את העוצמה של חזית React עם ה- CMS הפופולרי ביותר באינטרנט

רוצה את היתרונות של ספא מודרני של React, אך זקוק למערך אחורי שמרגיש מוכר? במאמר זה נעבור כיצד להגדיר את ה- REST API של וורדפרס, כולל סוגי פוסטים ושדות מותאמים אישית וכיצד להביא נתונים אלה בתוך React.

לאחרונה עבדתי על אפליקציית React עבור לקוח כאשר הם העלו עליי את השאלה הזו: 'האם נוכל להשתמש בה עם WordPress ? '

מאז סוף 2015 התשובה לשאלה זו הייתה חיובית. אך הצעדים הנחוצים ליצירת אתר פועל מנותק עשויים להיראות לא פשוטים, במיוחד לאלה שאינם מכירים את WordPress וגם את React.

במסע שלי ליצור יישום עובד נתקלתי בכמה מכשולים מסובכים, ובמאמר זה אסביר כיצד להימנע מהם. אני אשתף גם כמה טיפים וטריקים שלמדתי בדרך!

תוכן

חלק 1: מידע רקע

  • מהו CMS ללא ראש?
  • מה עלי לדעת לעקוב אחריו?
  • ראשי תיבות מרכזיים
  • היכן אוכל לראות את נתוני JSON של וורדפרס?

חלק 2: וורדפרס

  • הוספת סוג הודעה מותאם אישית
  • שינוי טקסט מציין הכותרת
  • הוספת שדה מותאם אישית לסוג ההודעה המותאמת אישית שלך
  • הפיכת שדות מותאמים אישית לזמינים כ- JSON
  • הגבלת נתוני JSON גלויים

חלק 3: תגובה

  • הבטחות ב- JavaScript
  • שיטת האחזור
  • טיפול בהבטחות

דוגמא לעבודה בתגובה

סיכום

חלק 1: מידע רקע

מהו CMS ללא ראש?

בעבר, שימוש ב- CMS כמו WordPress פירושו שעליך לבנות את ה- frontend שלך באמצעות PHP.

עכשיו, עם CMS ללא ראש, אתה יכול לבנות את הקצה הקדמי שלך עם כל הטכנולוגיות שאתה אוהב; הסיבה לכך היא הפרדה בין חזית לקצה אחורי באמצעות ממשק API. אם אתה רוצה ליצור ספא (יישום של עמוד יחיד) באמצעות React, Angular או Vue, ולשלוט בתוכן באמצעות CMS כמו WordPress, אתה יכול!

מה עלי לדעת לעקוב אחריו?

תוכלו להפיק את המרב ממאמר זה אם יש לכם:

  • קצת ידע על אופן הפעולה של CMS כמו וורדפרס, קצת PHP ורעיון כיצד להקים פרויקט WordPress בסיסי במחשב שלך;
  • הבנה של JavaScript, כולל מאפייני שפה ES6 + ותחביר הכיתה React.

ראשי תיבות מרכזיים

התכנות מלא בז'רגון, אך זה עושה את זה הרבה יותר מהר לדון בכמה מהמושגים במאמר זה. להלן סיכום מהיר של התנאים בהם נשתמש:

  • CMS - מערכת ניהול תוכן. תחשוב וורדפרס, דרופל, ג'ומלה, מגנטו.
  • SPA - יישום של עמוד יחיד. במקום לטעון מחדש כל עמוד בשלמותו, יישום SPA טוען תוכן באופן דינמי. הקוד הבסיסי (HTML, CSS ו- JavaScript) של האתר נטען רק פעם אחת. תחשוב תגיב, Vue, Angular.
  • API - ממשק תכנות יישומים. במילים פשוטות, סדרת הגדרות, ששירות מספק כדי לאפשר לך לקחת ולהשתמש בנתונים שלו. למפות גוגל יש אחד כזה. למדיום יש אחד. ועכשיו, כל אתר וורדפרס מגיע עם ממשק API מובנה.
  • REST - העברת מדינה ייצוגית. סגנון האדריכלות האינטרנט מבוסס סביב שיטות בקשת HTTP: GET, PUT, POSTו DELETE. ה- API המובנה של וורדפרס הוא ממשק API REST או “RESTful”.
  • HTTP - פרוטוקול העברת היפר טקסט. מערך הכללים המשמש להעברת נתונים דרך האינטרנט. זה צוין בתחילת כתובות אתרים כ- httpאו https(הגרסה המאובטחת).
  • JSON - סימון אובייקט JavaScript. למרות שמקורו ב- JavaScript, זהו פורמט שאינו תלוי בשפה לאחסון והעברת נתונים.

במאמר זה אנו משתמשים ב- WordPress כ- CMS שלנו. זה אומר לתכנת את החלק האחורי שלנו ב- PHP ולהשתמש ב- REST API של וורדפרס כדי להעביר נתוני JSON לחזית שלנו.

היכן אוכל לראות את נתוני JSON של וורדפרס?

לפני שמגיעים לדברים הטובים, הערה מהירה לגבי המקום בו תוכלו למצוא את נתוני JSON באתר הוורדפרס שלכם. כיום, בכל אתר וורדפרס יש נתונים של JSON זמינים (אלא אם כן בעל האתר השבית או הגביל את הגישה אליו). אתה מביט ב- JSON הראשי של אתר WordPress על ידי הוספת /wp-jsonשם תחום הבסיס.

כך, למשל, תוכלו להציץ ב- JSON עבור WordPress.org על ידי ביקור בכתובת //wordpress.org/wp-json. לחלופין, אם אתה מנהל אתר וורדפרס באופן מקומי, תוכל לראות את ה- JSON שלו על ידי ביצוע localhost/yoursitename/wp-json.

כדי לגשת לנתונים עבור ההודעות שלך, הקלד localhost/yoursitename/wp-json/wp/v2/posts. לפורמט פוסט מותאם אישית, החלף בפורמט החדש (למשל movies) במקום posts. מה שנראה כעת כמו גוש טקסט בלתי קריא הוא בדיוק מה שיאפשר לנו להשתמש בוורדפרס כ- CMS ללא ראש!

חלק 2: וורדפרס

כדי להגדיר את ה- REST API שלך, רוב מה שתצטרך לעשות יקרה functions.phpבקובץ שלך . אני מניח שאתה יודע כיצד להקים פרויקט וורדפרס ולגשת אליו באמצעות localhost, אך אם תרצה קצת עזרה בנושא זה, אני ממליץ על מאמר זה (זה מה שהשתמשתי בו כדי להתחיל לתכנת עם WordPress).

ברוב הפרויקטים, תרצה להשתמש בסוג פוסט מותאם אישית, אז נתחיל בהגדרה אחת.

הוספת סוג הודעה מותאם אישית

נניח שהאתר שלנו עוסק בסרטים, ואנחנו רוצים סוג פוסט שנקרא 'סרטים'. ראשית, אנו רוצים לוודא שסוג ההודעה 'הסרטים' שלנו נטען בהקדם האפשרי, לכן נצמיד אותו אל initהוו באמצעות add_action:

add_action( 'init', 'movies_post_type' );

אני משתמש movies_post_type(), אבל אתה יכול לקרוא לפונקציה שלך כל מה שתרצה.

לאחר מכן, אנו רוצים לרשום 'סרטים' כסוג פוסט, באמצעות register_post_type()הפונקציה.

The next chunk of code may look overwhelming, but it’s relatively simple: our function takes a lot of in-built arguments to control the functionality of your new post type, and most of them are self-explanatory. We’ll store these arguments in our $args array.

One of our arguments, labels , can take many different arguments of its own, so we split that off into a separate array, $labels , giving us:

Two of the most important arguments are 'supports' and 'taxomonies' , because these control which of the native post fields will be accessible in our new post type.

In the above code, we’ve opted for just three 'supports':

  • 'title'— the title of each post.
  • 'editor'— the primary text editor, which we’ll use for our description.
  • 'thumbnail'— the post’s featured image.

To see the full list of what’s available, click here for supports, and here for taxonomies.

Generate WordPress also has a handy tool to help you code custom post types, which can make the process a lot quicker.

Changing Title Placeholder Text

If the title placeholder text “enter title here” could be a little misleading for your custom post type, you can edit this in a separate function:

Adding a Custom Field to Your Custom Post Type

What if you want a field that doesn’t come pre-defined by WordPress? For example, let’s say we want a special field called “Genre”. In that case, you’ll need to use add_meta_boxes() .

For, we need to attach a new function to WordPress’s add_meta_boxes hook:

add_action( 'add_meta_boxes', 'genre_meta_box' );

Inside our new function, we need to call WordPress’s add_meta_box() function, like so:

function genre_meta_box() { add_meta_box( 'global-notice', __( 'Genre', 'sitepoint' ), 'genre_meta_box_callback', 'movies', 'side', 'low' );}

You can read more about this function’s arguments here. For our purposes, the most critical part is the callback function, which we’ve named genre_meta_box_callback . This defines the actual contents on the meta box. We only need a simple text input, so we can use:

function genre_meta_box_callback() { global $post; $custom = get_post_custom($post->ID); $genre = $custom["genre"][0]; ?>

Finally, our custom field won’t save its value unless we tell it to. For this purpose, we can define a new function save_genre() and attach it to WordPress’s save_post hook:

function save_genre(){ global $post; update_post_meta($post->ID, "printer_category", $_POST["printer_category"]);};
add_action( 'save_post', 'save_genre' );

Together, the code used to create the custom field should look something like this:

Making Custom Fields Available as JSON

Our custom posts are automatically available as JSON. For our “movies” post type, our JSON data can be found at localhost/yoursitename/wp-json/wp/v2/movies .

However our custom fields are not automatically part of this, and so we need to add a function to make sure they are also accessible via the REST API.

First, we’ll need to attach a new function to the rest_api_init hook:

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, ) );};

This function takes an array with get and update callback. For a more straightforward use-case like this, we should only need to specify a 'get_callback' :

function get_genre_meta_field( $object, $field_name, $value ) { return get_post_meta($object['id'])[$field_name][0];};

As a whole, here is the code necessary to register a custom field.

Making Featured Image URLs Available as JSON

Out-of-the-box, WordPress’s REST API doesn’t include URL for your featured images. To make it easier to access this, you can use the following code:

The WordPress filter rest_prepare_posts is dynamic, so we can swap in our custom post type in place of “posts”, such as rest_prepare_movies .

Restricting Visible JSON Data

We almost ready to start pulling in data to our React app, but there’s one more quick optimisation we can make, by limiting the data that is made available.

Some data comes as standard which you may never need in your frontend and — if that’s the case — we can remove it using a filter, like this one. You can find the names of the data types by looking at your /wp-json/wp/v2/movies part of your website.

With that done, once you’ve added a few movies using the WordPress backend, and we have everything we need to start bringing the data into React!

Part 3: React

Original text


To fetch external data in JavaScript, you need to use promises. This will likely have implications for the way you want to structure your React components, and in my case (converting an existing React project), I had to re-write a fair amount of code.

Promises in JavaScript

Promises in JavaScript are used to handle asynchronous actions — things that happen outside the usual step-by-step or “synchronous” order of execution (after hoisting).

The good news is that asynchronous JavaScript is a lot easier than it used to be. Before ES6, we were dependent on callback functions. If multiple callbacks were necessary (and they often were), nesting would lead to code that was very difficult to read, scale and debug — a phenomenon sometimes known as callback hell, or the pyramid of doom!

Promises were introduced in ES6 (or ES2015) to solve that problem, and ES8 (or ES2018) saw the introduction of async ... await , two keywords which further simplify asynchronous functionality. But for our purposes, the most critical promise-based method is fetch() .

The Fetch Method

This method has been available since Chrome 40, and it is an easier-to-use alternative to XMLHttpRequest() .

fetch() returns a promise and so it is “then-able”, meaning that you can use the then() method to process the outcome.

You can add fetch to a method inside your React class component, like so:

fetchPostData() { fetch(`//localhost/yoursitename/wp-json/wp/v2/movies?per_page=100`) .then(response => response.json()) .then(myJSON => { // Logic goes here});}

In the code above, two things are important:

  • First, we are calling a URL with the filter ?per_page=100 appended onto the end. By default, WordPress only shows 10 items per page, and I often find myself wanting to increase that limit.
  • Second, before processing our data, we are using the .json() method. This method is used primarily in relation to fetch(), and it returns the data as a promise and parses the body text as JSON.

In most cases, we’ll want to run this function as soon as our React component has mounted, and we can specify this using the componentDidMount() method:

componentDidMount() { this.fetchPostData();}

Handling Promises

Once you have returned a promise, you have to be careful about handling it in the correct context.

When I first tried to use promises, I spent a while trying to pass that data to variables outside of the scope of the promise. Here are a few rules of thumb:

  • In React, the best way to use promises is via the state. You can use this.setState() to pass promise data into your component’s state.
  • It is best to process, sort and re-arrange your data within a series of then() methods following the initial fetch() . Once any processing is complete, it is best practice to add the data to state within your final then() method.
  • If you want to call any additional functions to process your promise (including within render()) it’s good practice to prevent the function from running until the promise has resolved.
  • So, for example, if you’re passing your promise to this.state.data , you can include a conditional within the body of any functions that depend on it, like below. This can prevent annoying unwanted behaviour!
myPromiseMethod() { if (this.state.data) { // process promise here } else { // what to do before the fetch is successful }}

A Working Example in React

Let’s say we want to pull in the name, description, featured_image and genre of the custom WordPress post type we defined in part 1.

In the following example, we’ll fetch those four elements for each movie and render them.

As so often with React tutorials, the following block of code may look intimidating, but I hope it will seem much simpler when we break it down.

constructor(props)

In this method, we call super(props), define our initial state (an empty data object) and bind three new methods:

  • fetchPostData()
  • renderMovies()
  • populatePageAfterFetch()

componentDidMount()

We want to fetch our data as soon as the component has mounted, so we’ll call fetchPostData() in here.

fetchPostData()

We fetch the JSON from our URL, passing .json() in the first .then() method.

In the second .then() method, we extract the four values we want for every movie entry we’ve fetched and then add them to our newState object.

We then use this.setState(newState) to add this information to this.state.data .

renderMovies()

The conditional if (this.state.data) means that the function will only run once data has been fetched.

In here, we take an array of all our fetched movies from this.state.data and pass it to the function populatePageAfterFetch() .

populatePageAfterFetch()

In this function, we prepare the data for each movie to be rendered. This should look straightforward to anyone who’s used JSX, with one potential stumbling block.

The value of movie.description is not plain text, but HTML markup. To display this, we can use dangerouslySetInnerHTML={{__html: movie.description}} .

Note: The reason this is potentially “dangerous” is that, if your data were hijacked to contain malicious XSS scripts, these would be parsed too. As we’re using our own server/CMS in this article, we shouldn’t need to worry. But if you do want to sanitise your HTML, take a look at DOMPurify.

render()

Finally, we control where our rendered data will appear by calling the renderMovies() method within our chosen iv> tags. We’ve now successfully fetched data from our WordPress site and displayed it!

Conclusion

Overall, I hope this article makes the process of connecting a React front-end to a WordPress back-end as painless as possible.

Like so much in programming, what can look intimidating to begin with quickly becomes second nature with practice!

I’d be very interested to hear about your own experiences using WordPress as a headless CMS, and I’m happy to answer any questions in the comments.