In the last blog, I created Custom Post Types (CPT) and went through how to introduce them to our WordPress websites. Now, let’s put them into action.
Go to your new Custom Post Type that you just created. (Find it in the admin area; the left-hand navigation and add a new post to it). For my own example, I’m using the link “Post Type Item (CPTUI)” which is the CPT I created using the recommended plugins.
Dashboard ----> POST TYPE e.g "Post Type Item (CPTUI)" ----> Add a New Post ---->Now type in a title for your CPT Post. (This will be just like when you create a new Post or Page) using the Title input box.
"CPTUI (Item 1)"After that, you can go down the page to your field group container and the field(s). This should now be attached to the edit posts page. In my example, it is titled “Custom Fields (CPTUI)” which is the title that I entered when I created it in the field groups page and is now represented in code as written down below.
<?php
acf_add_local_field_group( array (
'key' => 'group_6661cac3ec83b',
'title' => 'Custom Fields (CPTUI)',
'fields' => array(Field Group Title: Custom Fields (CPTUI)In that field group, I already set up a single custom field, which is a text input box. Every custom field you make will have a field label, an ID and a value.
<?php the_field( "field_name" ) ?>The field label is what is used to select advanced custom fields in code. And then actions can be performed on them. For example, we use the field ID to display their values with a method like the_field().
field_one
"field 1 - item 1"When you’re ready, click “Publish” to create your first new post.
"post published"Now let’s see what happens when you try to view this post. After publishing your post, you can click the “View Post” link in a small tooltip.
e.g https://wordpress.jonniegrieve.co.uk/new_post_type/cptui-item-1/
Already you should see that things don’t look quite right. That will no doubt manifest differently in your own theme. But for me, I can already see one WordPress warning message on my screen.
Associated Files:
Warning: foreach() argument must be of type array|object, null given in C:\xampp\htdocs\wordpress\subdomain_2024\wp-content\themes\subdomain_2024\single.php on line 47There’s no custom field for “associated files” with this CPT so when I created my first post, it brought up that warning message.
But the point is this; WordPress doesn’t yet have a way to know whether it is supposed to show you a Single Post page from the WordPress Core, or one of your CPTs. So it is falling back to the single.php template.
The WordPress Template hierarchy makes it possible for more specific templates to be served i.e. a Single Post template specific to your CPT.
Look again at the URL. You may have noticed the part of the URL that says “new_post_type“. That is the Post Type “slug” identifier. http://wordpress.jonniegrieve.co.uk/new_post_type/cptui-item-1/
By adding a new file to our theme that contains this CPT slug, we have given WordPress the template file it is looking for; further down the hierarchy than single.php.
single-new_post_type.phpIt’s a single post template that is unique to our CPT.
The code below is a snippet from the single post-type template that I used. It will retrieve the value of the custom field with the id “field_one” field in this instance of the CPT and display it. It assumes one existing post in the “CPT UI” Post Type.
<div>
<strong>field_name: </strong>
<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : the_post(); ?>
<?php // the_content(); ?>
<?php field_name: the_field( "field_one" ); ?>
<?php endwhile; else : ?>
<!-- The loop ends at this point -->
<?php endif; ?>
</div>Second example
Here’s another example. Remember that in the last blog, I created a second CPT example which used only code. The CPT slug identifier for this was – “portfolio_item“. We can create new posts for the Post Type in largely the same way.
Add a new post titled “Portfolio CPT Item “.
Dashboard ----> Portfolio CPT ----> Add a New PostAdd the appropriate template file to your theme and verify it is working using the URL WordPress gives you.
single-portfolio_item.phphttp://wordpress.jonniegrieve.co.uk/portfolio_item/portfolio-cpt-item-1/
The content retrieved is unique to the CPT because WordPress sees the template file and recognises the post type slug in the URL. But there are no custom fields in this CPT. This means you can get the content you need from this CPT by using the regular the_content() template tag.
<div>
<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : the_post(); ?>
<?php the_content(); ?>
<?php endwhile; else : ?>
<!-- The loop ends at this point -->
<?php endif; ?>
</div>Incidentally, there’s one more point to address. There may be occasions where we lose the Custom Fields box in WordPress Post editors after we’ve activated and used the Advanced Custom Fields Plugins. To get this back and ensure it’s always visible, just add the following filter in your functions.php file.
add_filter('acf/settings/remove_wp_meta_box', '__return_false');WP Queries
The WP_Query class is a powerful tool in WordPress. WP_Query is a class used in WordPress theme development that accepts a variety of parameters to request and fetch posts around those parameters. This provides a way to customise how your CPT looks and to perform queries on them.
Go into one of your template files for Custom Post Types. Find a place in your code, somewhere towards the top of the template file and define a new instance of WP_Query. Pass it an $args variable.
/* single-new_post_type.php */
<?php
$loop = new WP_Query( $args ); The $args variable is there as a container to store a list of settings and parameters for your query. This is done using an array.
$args = array()
<?php
$loop = new WP_Query( $args ); Here’s an example of the parameters you might use. An exhaustive list is available here. Link
$args = array(
'post_type' => 'new_post_type',
'post_status' => 'publish',
...
);The post type parameter is a string of your post type slug .e.g. new_post_type. Check that these strings match or your query will not work.
Accessing the properties and the methods of the WP Query class with the -> operator.
In this case:
$custom_query->have_posts(): Calls thehave_posts()method of theWP_Queryobject$custom_query.$custom_query->the_post(): Calls thethe_post()method of theWP_Queryobject$custom_query.
These methods are part of the WP_Query class, which is used to handle custom queries in WordPress. Here’s the full example.
$args = array(
'post_type' => 'new_post_type',
'post_status' => 'publish'
);
$custom_query = new WP_Query( $args ); ?>
<div>
<strong>field_name: </strong>
<?php while ( $custom_query->have_posts() ) : $custom_query->the_post(); ?>
<?php // the_content(); ?>
<?php field_name: the_field( "field_one" ); ?>
<?php endwhile; ?>
</div>
CPT’s work best when you can create multiple posts of the same type and retrieve the content appropriate to that particular post – like WordPress Core Posts and Pages. You’ll also want to be able to go back and forth between posts.
Let’s see what effect this has on custom fields.
field_name: field 1 - item 3 field 1 - item 2 field 1 - item 1Because we’ve added 3 posts, we’re getting all 3 values from the field_one” field on the same line, which is not what we want to happen.
We can fix this by using
<?php while ( have_posts() ) : the_post(); ?>not
<?php while ( $custom_query->have_posts() ) : $custom_query->the_post(); ?>This basically means we’re going back to using the main query loop to display the value of the custom field.
So what’s happening? WordPress knows it’s having to work with a Custom field but it is either retrieving fields using the custom query loop or the main query loop, which work in different ways. The main query loop processes the posts retrieved by the main WordPress query, which is determined by the URL and template being used.
So what purpose does $wp_query serve?
Put simply, using $wp_query offers you more control over how posts from custom post types are queried and displayed, which is essential for advanced WordPress development involving custom fields and custom post types.
You might need to use the $wp_query class to retrieve and display specific content based on various criteria. I have used it before to create “pockets” or smaller areas of unique content rather than using the main content editor. In our case… we have simply one field per post… 3 that we’ve published where we don’t need to call the loop on the custom query. That’s because WordPress already knows we’re working with the Post Type.
If you want to display unique values from custom fields, use the main query loop. Otherwise, use WP Query.
But now we’re going to add 2 more posts.
You should add the wp_reset_postdata() tag: This Resets the global $post object to ensure subsequent queries or loops work correctly. We use wp_reset_postdata() after the loop to restore the global $post variable to the main query’s post.
Pagination in CPT
Finally, we should add some basic pagination methods so we can move swiftly between the different Posts. To make it easier to style these links I added a container class to the template.
<div class="pagination_section">
</div>Add the pagination methods in this section. These basic methods will provide Next and Previous posts to the page whenever they are available.
<div class="pagination_section">
<?php echo previous_post_link( '%link', ' « Previous Post; ' ); ?>
<?php echo next_post_link( '%link', 'Next Post » ' ); ?>
</div>Conclusion
Whether you need to use the custom query loop or the main query loop depends on what you need your website to do. But generally, if you want to retrieve and display unique values from custom fields across multiple posts from a custom post type, you should use the main query loop.
<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : the_post(); ?>
. . .The main query loop is used to display the primary content of your page or post. It’s the default loop that WordPress runs to determine which posts to show on a page based on the URL.
Otherwise, we can retrieve multiple custom fields from a single post-type instance using WP_Query so we can run all the queries we need to use the WP Query class.
$args = array(
'post_type' => 'new_post_type',
'post_status' => 'publish',
'posts_per_page' => -1, // Ensure you get all posts
);
$custom_query = new WP_Query( $args ); ?>
. . .
<?php if ($custom_query->have_posts()) : ?>
<?php while ( $custom_query->have_posts() ) : $custom_query->the_post(); ?>Links
https://coloradodigital.com/blog/how-use-wpquery-display-custom-post-type
https://developer.wordpress.org/reference/classes/wp_query
https://developer.wordpress.org/reference/classes/wp_query/#parameters