Here’s the sarcastic summary with hashtags:
WordPress is not just for blogging. With the right combination of plugins and custom code, you can turn it into a powerful platform for almost any kind of data-driven website, including a searchable directory.
For a recent project, universal-languages.fr, we needed to build a comprehensive directory of language schools, allowing users to search by location and keywords. In this tutorial, I’ll walk you through the exact process of how to build a similar directory from the ground up.
The Tech Stack
We’ll use a combination of powerful and flexible plugins to achieve our goal with minimal hassle:
- Custom Post Type UI (CPT UI): To create a dedicated “Language School” post type, separating our listings from standard blog posts.
- Advanced Custom Fields (ACF): To add custom data fields to our directory items (e.g., address, website, phone number).
- WordPress is not just for blogging. With the right combination of plugins and custom code, you can turn it into a powerful platform for almost any kind of data-driven website, including a searchable directory.
For a recent project, universal-languages.fr, we needed to build a comprehensive directory of language schools, allowing users to search by location and keywords. In this tutorial, I’ll walk you through the exact process of how to build a similar directory from the ground up.
The Tech Stack
We’ll use a combination of powerful and flexible plugins to achieve our goal with minimal hassle.
- Custom Post Type UI (CPT UI): To create a dedicated “Language School” post type, separating our listings from standard blog posts.
- Advanced Custom Fields (ACF): To add custom data fields to our directory items (e.g., address, website, phone number).
- WP Ultimate CSV Importer: To bulk-import data into our directory, saving hours of manual entry.
- Relevanssi: To supercharge the default WordPress search, making it faster and capable of searching through our custom fields.
Step 1: Create the Custom Post Type
First, we need a place to store our directory listings. Installing CPT UI allows us to do this without writing any
register_post_type
code.
- Install and activate the CPT UI plugin.
- Navigate to CPT UI > Add/Edit Post Types.
- Create a new post type. We’ll call it “Language School”. For the slug, our real-world project uses
centre-formation
(French for “training center”).- Fill in the labels as needed (Plural, Singular, etc.).
- Under “Supports”, make sure
Title
,Editor
, andCustom Fields
are checked.- Save the post type.
You now have a new “Language Schools” menu in your WordPress admin!
We also need a way to categorize our schools, for example, by city. For this, we use a custom taxonomy.
- Go to CPT UI > Add/Edit Taxonomies.
- Create a new taxonomy called “City” (
ville
).- Attach it to our “Language School” post type.
- Save the taxonomy.
Step 2: Define Custom Fields with ACF
A directory listing is more than just a title and description. We need fields for structured data. This is where ACF shines.
- Install and activate Advanced Custom Fields.
- Go to Custom Fields > Add New.
- Create a Field Group named “School Details”.
- Set the rule to show this field group if Post Type is equal to Language School.
- Add your fields. For a school directory, you might add:
address
(Text)phone_number
(Text)website_url
(Url)email_address
(Email)Now, when you edit a “Language School” post, you’ll see these fields ready for input.
Step 3: Displaying a Single Directory Item
To display a single listing, WordPress looks for a template file named
single-{post_type_slug}.php
. In our case, this issingle-centre-formation.php
.Create this file in your child theme’s folder and add the following code. It’s a standard WordPress loop, but we use ACF’s
get_field()
function to display our custom data.<?php get_header(); ?> <div id="primary" class="content-area"> <main id="main" class="site-main"> <?php while ( have_posts() ) : the_post(); ?> <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>> <header class="entry-header"> <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?> </header> <div class="entry-content"> <h3>Details</h3> <p><strong>Address:</strong> <?php echo esc_html( get_field('address') ); ?></p> <p><strong>Phone:</strong> <?php echo esc_html( get_field('phone_number') ); ?></p> <p><strong>Website:</strong> <a href="<?php echo esc_url( get_field('website_url') ); ?>" target="_blank">Visit Website</a></p> <hr> <?php the_content(); // This displays the main description from the WordPress editor ?> </div> </article> <?php endwhile; // End of the loop. ?> </main><!-- #main --> </div><!-- #primary --> <?php get_footer(); ?>
Step 4: Building the Search Page and Logic
This is the core of our directory. We’ll create a custom page template for the search form and a shortcode to process the search and display results.
1. The Search Page Template (
search_centres_page.php
)Create a file named
search_centres_page.php
in your child theme with the following content. This template will hold our search form.<?php /* Template Name: School Search Page */ get_header(); ?> <div class="container"> <h1>Find a Language School</h1> <form role="search" method="get" class="search-form" action="<?php echo esc_url( home_url( '/' ) ); ?>"> <input type="hidden" name="post_type" value="centre-formation" /> <label> <span class="screen-reader-text">Search for:</span> <input type="search" class="search-field" placeholder="Search by keyword..." value="<?php echo get_search_query(); ?>" name="s" /> </label> <?php // Dropdown for our "City" taxonomy wp_dropdown_categories( array( 'taxonomy' => 'ville', 'name' => 'ville', 'show_option_all' => 'All Cities', 'value_field' => 'slug', 'hierarchical' => true, 'selected' => get_query_var('ville'), ) ); ?> <input type="submit" class="search-submit" value="Search" /> </form> <div class="search-results"> <?php echo do_shortcode('[centre_search_results]'); ?> </div> </div> <?php get_footer(); ?>
Now, create a new page in WordPress and assign it the “School Search Page” template.
2. The Shortcode for Displaying Results
Add the following code to your child theme’s
functions.php
file. This code creates a shortcode[centre_search_results]
that runs aWP_Query
based on the URL parameters from our form.function centre_search_results_shortcode() { $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1; $search_query = get_search_query(); $city_filter = isset($_GET['ville']) ? sanitize_text_field($_GET['ville']) : ''; $args = array( 'post_type' => 'centre-formation', 'posts_per_page' => 10, 'paged' => $paged, 's' => $search_query, ); // If a city is selected, add a taxonomy query if ( !empty($city_filter) ) { $args['tax_query'] = array( array( 'taxonomy' => 'ville', 'field' => 'slug', 'terms' => $city_filter, ), ); } $query = new WP_Query( $args ); ob_start(); if ( $query->have_posts() ) { echo '<div class="directory-listings">'; while ( $query->have_posts() ) { $query->the_post(); // Simple result display echo '<div class="listing-item">'; echo '<h2><a href="' . get_permalink() . '">' . get_the_title() . '</a></h2>'; echo '<p>' . esc_html(get_field('address')) . '</p>'; echo '</div>'; } echo '</div>'; // Pagination echo paginate_links(array( 'total' => $query->max_num_pages )); } else { echo '<p>No schools found matching your criteria.</p>'; } wp_reset_postdata(); return ob_get_clean(); } add_shortcode( 'centre_search_results', 'centre_search_results_shortcode' );
Why Relevanssi?
By default, WordPress search is limited. By installing and activating Relevanssi, it automatically replaces the standard search. Configure it to index yourcentre-formation
post type and its custom fields. Now, when a user types a keyword, Relevanssi will look inside the address, description, and other ACF fields, providing much more accurate results.
Step 5: Bulk Import Your Data
Manually adding hundreds of listings is not practical. This is where WP Ultimate CSV Importer comes in.
- Prepare a CSV file where the column headers match your ACF field names (e.g.,
address
,phone_number
).- In the WordPress admin, go to the importer plugin’s page.
- Upload your file and map the CSV columns to the corresponding WordPress fields (post title, content, and your custom fields).
- Run the importer.
Conclusion
And there you have it! By combining the power of CPT UI, ACF, and Relevanssi, you can create a robust, searchable directory that is both easy to manage and powerful for the end-user. The final step is to add your own CSS to
style.css
to make it look great. This modular approach is highly flexible and serves as a fantastic foundation for even more complex directory projects.