In this tutorial, we will import a list of movies – including images and custom fields – inside a custom post type by fetching a public API.
Table of Contents
- Introduction
- Get access to the API
- Get the response from API
- Create the custom posts
- Set the featured image
- Populate the ACF fields
- Enable AJAX
- Full code
- Run the AJAX call
Introduction
First of all, this tutorial is a follow-up to the great video shared in the group recently by Cédric Bontems where he explained in detail how to fetch data from an API to recreate the Netflix homepage. In this tutorial, we’ll use a different approach to reach the same result by importing the data to WordPress instead of calling the API on each page load.
Why does it matter? Well, there are several benefits to doing so:
- With this method, you just need to fetch the API once. In fact, you could completely remove the code once the data has been imported since all the content will be accessible from your WordPress data tables.
- Once the data is saved in WordPress, you can manipulate it without using custom PHP code. That means you can create query loops inside the Bricks Builder, create nested sliders, filters, single pages, category pages, etc…
- Using data from your WordPress tables will be much faster than calling the API every time. It allows you to cache both the content and the WP queries.
- It doesn’t rely on an external source to show your content. if the API is down for some reason, you are not in trouble!
This tutorial is also massively inspired by the WPCasts video. Make sure to visit his channel to learn advanced WordPress tips and tricks.
Get access to the API
In this tutorial, we’ll use the same API shared by Cédric: https://www.themoviedb.org/settings/api. He explained how to create an account and get access to it so I won’t be too long here. Make sure to define your API key inside your functions.php file:
define ('TMDB_API_KEY', '<YOUR_API_KEY>' );
Get the response from API
We’ll follow the exact same logic described in my Introduction to Fetching API in Bricks, so make sure to read it if you haven’t yet. Let’s create our main function:
function bl_get_movies_from_api(){
$moviedb_api = 'https://api.themoviedb.org/3/movie/top_rated?api_key=' . TMDB_API_KEY . '&language=en-US';
$response = wp_safe_remote_get($moviedb_api);
// Stop the function if the response returns an error
if ( is_wp_error( $response ) ) {
return false;
}
// Get the body part
$response_body = wp_remote_retrieve_body( $response );
// Decode the JSON object into a PHP array
$response_body_decoded = json_decode( $response_body, true );
return $response_body_decoded['results'];
}
At this point, we are returning a PHP array with the data of each movie. The output looks like this:
array(20) {
[0]=>
array(14) {
["adult"]=>
bool(false)
["backdrop_path"]=>
string(32) "/rl7Jw8PjhSIjArOlDNv0JQPL1ZV.jpg"
["genre_ids"]=>
array(2) {
[0]=>
int(10749)
[1]=>
int(18)
}
["id"]=>
int(851644)
["original_language"]=>
string(2) "ko"
["original_title"]=>
string(15) "20 Century Girl"
["overview"]=>
string(377) "Yeon-du asks her best friend Bora to collect all the information she can about Baek Hyun-jin while she is away in the U.S. for heart surgery. Bora decides to get close to Baek's best friend, Pung Woon-ho first. However, Bora's clumsy plan unfolds in an unexpected direction. In 1999, a year before the new century, Bora, who turns seventeen, falls into the fever of first love."
["popularity"]=>
float(314.92899999999997)
["poster_path"]=>
string(32) "/od22ftNnyag0TTxcnJhlsu3aLoU.jpg"
["release_date"]=>
string(10) "2022-10-06"
["title"]=>
string(17) "20th Century Girl"
["video"]=>
bool(false)
["vote_average"]=>
float(8.8000000000000007)
["vote_count"]=>
int(248)
}
[1]=> etc....
In this tutorial, we’ll use:
['backdrop_path']
to get the image link and use it as the featured image of our post['title']
will be our post title['id']
will be used inside the post slug to make sure the post is unique['overview']
for the post excerpt['release_date']
,['vote_average']
, and['vote_count']
to fill our ACF custom fields
Create the custom posts
At this point, we have a big array of all the movie information. Now we want to loop inside the array and create a custom post for each movie, but before let’s create a custom function to handle our post slug:
function bl_slugify($text){
// remove unwanted characters
$text = preg_replace('~[^-\w]+~', '', $text);
// trim
$text = trim($text, '-');
// remove duplicate -
$text = preg_replace('~-+~', '-', $text);
// lowercase
$text = strtolower($text);
if (empty($text)) {
return 'n-a';
}
return $text;
}
This function will make sure to replace any special character, separate each word with “-“, and transform the text to lowercase. Nothing fancy here.
It’s time to loop inside the array and create the custom posts to our CPT movie:
// Loop in each movie
foreach($response_body_decoded['results'] as $movie){
// Slug
$movie_slug = bl_slugify( $movie['title'] . '-' . $movie['id'] );
// Existing movie inside the CPT
$existing_movie = get_page_by_path( $movie_slug, 'OBJECT', 'movie' );
// Check if the movie post has already been created
if( $existing_movie === null ){
// Create a new custom post
$inserted_movie = wp_insert_post( [
'post_name' => $movie_slug,
'post_title' => $movie['title'],
'post_excerpt' => $movie['overview'],
'post_type' => 'movie',
'post_status' => 'publish'
] );
// Continue to the next loop iteration if the post was not correctly created
if( is_wp_error( $inserted_movie ) || $inserted_movie === 0 ) {
continue;
}
}
}
Few considerations:
- to create our slug, we concatenate the movie title and the ID, so we’re sure the slug will be unique.
- before creating the post, we run the get_page_by_path() function that will check if the slug already exists inside our custom post type movie. if it doesn’t (which is what we want), it returns
null
and will create a new post – making sure we don’t duplicate an existing post if we run the function multiple times. - We use the core WordPress function wp_insert_post() to create the custom post. This function allows passing an array as an argument to set the post name (the slug), the post title, etc…
- after the post has been created, we check if everything was correctly processed. If the function detects an error, or if the custom post returns no post_id, then the loop iteration will stop and process the next movie.
Set the featured image
Let’s download the image from the API, place it inside our media library and set it as the featured image of the post:
// Set Featured Image
$url = 'https://image.tmdb.org/t/p/original' . $movie['backdrop_path'];
$img = media_sideload_image( $url, $inserted_movie);//download image to wpsite from url
$img = explode("'",$img)[1];// extract http.... from <img src'http...'>
$attId = attachment_url_to_postid($img);//get id of downloaded image
set_post_thumbnail( $inserted_movie, $attId );//set the given image as featured image for the post
- First of all, we compose the image URL using the [‘backdrop_path’] value from the API.
- Then we use the media_sideload_image() function to download the image to our server
- We extract the URL from the img tag and attach it to our post using the attachment_url_to_postid() function. At this point, the image is inside our media library.
- The last step is to set the image as the featured image of the post using the set_post_thumbnail() function.
Populate the ACF fields
First of all, lets create our fields in ACF:
Make sure to check the Field Keys checkbox from the Screen Options.
Now, let’s map our custom fields with the API values. On the left of the arrow, the ACF fields key. On the right, the array value from the API:
// Map ACF keys with the API values
$acf_fields = array(
'field_6375ea77d3491' => 'release_date',
'field_6375ea8fd3492' => 'vote_average',
'field_6375ea9fd3493' => 'vote_count',
);
Finally, let’s update each field with the API value:
// Update each ACF value
foreach( $acf_fields as $key => $name ) {
update_field( $key, $movie[$name], $inserted_movie );
}
Bear with me, we are pretty close to the end.
Enable AJAX
So the idea is to run our function once through an AJAX call and populate all our custom posts. In order to enable the AJAX call and use our function as a callback we need to use the wp_ajax hook:
add_action( 'wp_ajax_nopriv_get_movies_from_api', 'get_movies_from_api' );
add_action( 'wp_ajax_get_movies_from_api', 'get_movies_from_api' );
Full code
OK. All our code is ready:
// Movies API
add_action( 'wp_ajax_nopriv_get_movies_from_api', 'get_movies_from_api' );
add_action( 'wp_ajax_get_movies_from_api', 'get_movies_from_api' );
function bl_get_movies_from_api(){
define ('TMDB_API_KEY', '<YOUR_API_KEY>' );
$moviedb_api = 'https://api.themoviedb.org/3/movie/top_rated?api_key=' . TMDB_API_KEY . '&language=en-US';
$response = wp_safe_remote_get($moviedb_api);
// Stop the function if the response returns an error
if ( is_wp_error( $response ) ) {
return false;
}
// Get the body part
$response_body = wp_remote_retrieve_body( $response );
// Decode the JSON object into a PHP array
$response_body_decoded = json_decode( $response_body, true );
// Loop in each movie
foreach($response_body_decoded['results'] as $movie){
// Slug
$movie_slug = bl_slugify( $movie['title'] . '-' . $movie['id'] );
// Existing movie inside the CPT
$existing_movie = get_page_by_path( $movie_slug, 'OBJECT', 'movies' );
// Check if the movie post has already been created
if( $existing_movie === null ){
// Create a new custom post
$inserted_movie = wp_insert_post( [
'post_name' => $movie_slug,
'post_title' => $movie['title'],
'post_excerpt' => $movie['overview'],
'post_type' => 'movie',
'post_status' => 'publish'
] );
// Continue to the next loop iteration if the post was not correctly created
if( is_wp_error( $inserted_movie ) || $inserted_movie === 0 ) {
continue;
}
// Set Featured Image
$url = 'https://image.tmdb.org/t/p/original' . $movie['backdrop_path'];
$img = media_sideload_image( $url, $inserted_movie);//download image to wpsite from url
$img = explode("'",$img)[1];// extract http.... from <img src'http...'>
$attId = attachment_url_to_postid($img);//get id of downloaded image
set_post_thumbnail( $inserted_movie, $attId );//set the given image as featured image for the post
// Map ACF keys with the API values
$acf_fields = array(
'field_6375ea77d3491' => 'release_date',
'field_6375ea8fd3492' => 'vote_average',
'field_6375ea9fd3493' => 'vote_count',
);
// Update each ACF value
foreach( $acf_fields as $key => $name ) {
update_field( $key, $movie[$name], $inserted_movie );
}
}
}
}
function bl_slugify($text){
// remove unwanted characters
$text = preg_replace('~[^-\w]+~', '', $text);
// trim
$text = trim($text, '-');
// remove duplicate -
$text = preg_replace('~-+~', '-', $text);
// lowercase
$text = strtolower($text);
if (empty($text)) {
return 'n-a';
}
return $text;
}
Run the AJAX call
The easy part! Go into /wp-admin/ and paste the following URL: your-domain.com/wp-admin/admin-ajax.php?action=bl_get_movies_from_api (change your-domain to your actual domain). This will run our AJAX call through the admin-ajax.php method.
Now just wait. The function is running in the background and importing all the data from the API.
When the function has finished running, it will return 0. That’s totally normal.
Let’s check our custom post type movie!
All the movies have been imported! Now let’s check the post data:
Everything is correctly filled and saved.
Congrats, you just imported a bunch of movies inside a custom post type using a public API!
8 comments
Saleh Alnaggar
I can't thank you enough for this amazing tutorial, it helped me a lot, I wish you all the best.
Aa
Same comment as Mike ????????????????
Maxime Beguin
More love to you ❤️
Mike
This has genuinely changed the way I'll work from now on.
For a long time, I've thought "I'll learn something like this one day", and the day has never come, until now.
I probably wouldn't have given it a second look had it not been from BricksLabs, but you guys keep doing cool stuff 🙂
I'm one big step closer to signing up with you guys because of this!
Cheers.
Maxime Beguin
Wow thanks Mike, that made my day!
Brendan Frye
Would this be possible to run on a custom taxonomy instead of a custom post type?
yosnap
This is one of the best tutorials. I've been waiting a lot for this.
Thank you very much Maxime
Maxime Beguin
Thanks yosnap, I love these feedback ❤️