Create a Custom Store Page Template

The default store page shows a map with the store location on it, the address details, and if enabled, the opening hours. You can change this with a few lines of code, or by creating a single-wpsl_stores.php file in your theme folder.

Only adding a field? If you only want to show an extra field below the address, you don’t need to change the layout. See Custom Meta Data for a filter that adds it to the default store page.

Store pages only exist when Store Locator > Settings > Local Pages > Create landing pages? is enabled.

Change the Layout Without a Template File

The easiest way works with every theme, from classic themes like Astra, Kadence, GeneratePress and OceanWP to block themes like Twenty Twenty-Five. The code below turns off the default store page output, and adds the map, the post content, the address and the opening hours in its own order. Your theme keeps its own header, sidebar and styling. Place it in the functions.php inside your active theme folder.

// Turn off the default store page output, the code below adds its own.
add_filter( 'wpsl_skip_cpt_template', '__return_true' );
add_filter( 'the_content', 'custom_store_page_content' );
function custom_store_page_content( $content ) {
// Only change the content of a single store page.
if ( ! is_singular( 'wpsl_stores' ) || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
$output = do_shortcode( '[wpsl_map]' );
$output .= $content;
$output .= do_shortcode( '[wpsl_address directions="true"]' );
$output .= do_shortcode( '[wpsl_hours]' );
return $output;
}

Change the order of the lines to change the layout, or remove a line to leave something out. For example, add $output .= get_the_post_thumbnail( get_the_ID(), 'medium' ); to show the featured image, or use [wpsl_address contact_details="true"] to always show the contact details. The “temporarily closed” or “permanently closed” message from the default store page isn’t included in this layout.

Use a Template File

For full control over the HTML of the store page in a classic theme, create a single-wpsl_stores.php file in your theme folder. Use the single.php of your theme as a starting point, so the page keeps the same structure as your other pages. Themes like Astra and Kadence build their pages with their own hooks, so for those themes the code above is usually the better choice.

Block themes: block themes, like Twenty Twenty-Five, don’t use PHP template files for the header and footer. Instead, go to Appearance > Editor > Templates, add a new template for a single store, and add the map and address with Shortcode blocks: [wpsl_map] and [wpsl_address directions="true"]. Then add add_filter( 'wpsl_skip_cpt_template', '__return_true' ); to your functions.php, so the plugin doesn’t add them a second time.

This example uses the HTML structure that many classic themes share. It first shows the map, followed by the post content and the store address. The opening hours are not included. Change the HTML around it to match the single.php of your theme.

<?php
/**
* Example of a single store template for a classic theme.
*
* Change the HTML to match the single.php of your theme.
*/
get_header(); ?>
<div id="primary" class="content-area">
<main id="main" class="site-main">
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<h1 class="entry-title"><?php single_post_title(); ?></h1>
</header>
<div class="entry-content">
<?php
global $post;
$queried_object = get_queried_object();
// Add the map shortcode
echo do_shortcode( '[wpsl_map]' );
// Add the content
$post = get_post( $queried_object->ID );
setup_postdata( $post );
the_content();
wp_reset_postdata();
// Add the address shortcode
echo do_shortcode( '[wpsl_address]' );
?>
</div>
</article>
</main><!-- #main -->
</div><!-- #primary -->
<?php get_sidebar(); ?>
<?php get_footer(); ?>

The template doesn’t use the WordPress loop, so the plugin doesn’t add its own map and address as well. If you change it to use a loop with the_post(), then also add add_filter( 'wpsl_skip_cpt_template', '__return_true' ); to your functions.php, or the map, address and opening hours show up twice.

Contact details: [wpsl_address] only shows the phone, fax, email and URL when “Below the address on the landing page” is selected for the contact details under Settings > User Experience. To always show them in this template, use [wpsl_address contact_details="true"].

If you do like to show the opening hours, then you can do this by including the code below anywhere between line 23 and 32 in the above template code.

Include Additional Content

The code examples below add more content to the template file from the previous section.

If you have set a featured image, and you’d like to show it on the store pages, then you need to include the get_the_post_thumbnail function in the template code.

The code below shows the featured image in the medium resolution.

Other supported formats are thumbnail, large and full. The image sizes can be configured under Settings > Media.

Category Names

Including the category names in the template is done with the code below.

Place it anywhere between line 23 and 32 in the page template, and the used categories will show up.

$terms = wp_get_post_terms( $queried_object->ID, 'wpsl_store_category' );
if ( $terms && ! is_wp_error( $terms ) ) {
$term_links = [];
foreach ( $terms as $term ) {
$term_links[] = '<a href="' . esc_url( get_term_link( $term->term_id, 'wpsl_store_category' ) ) . '">' . esc_html( $term->name ) . '</a>';
}
echo esc_html__( 'Categories:', 'wp-store-locator' ) . ' ' . implode( ', ', $term_links );
}

The address shortcode can add a directions link itself. It opens Google Maps or OpenStreetMap, depending on the map service you use. Replace [wpsl_address] in the template code with:

// Add the address shortcode, including a
// directions link for the active map service.
echo do_shortcode( '[wpsl_address directions="true"]' );

Custom Meta Data

Fields you create in Store Locator > Settings > Fields Manager are saved with a wpsl_ prefix. A field with the name my_textinput is saved as wpsl_my_textinput. The default fields work the same way: wpsl_phone, wpsl_fax, wpsl_email and wpsl_url.

Without a custom template

To show a field below the address on the default store page, add the code below to the functions.php inside your active theme folder. It adds the field to the output of the [wpsl_address] shortcode, above the directions link. This also works on other pages where you use [wpsl_address id="…"].

add_filter( 'do_shortcode_tag', 'custom_wpsl_address_field', 10, 3 );
function custom_wpsl_address_field( $output, $tag, $attr ) {
if ( $tag !== 'wpsl_address' ) {
return $output;
}
// The store from the id attribute, or the store page that's being viewed.
$store_id = ! empty( $attr['id'] ) ? absint( $attr['id'] ) : get_the_ID();
$my_textinput = get_post_meta( $store_id, 'wpsl_my_textinput', true );
if ( ! $my_textinput ) {
return $output;
}
$field = '<p class="wpsl-my-textinput">' . esc_html( $my_textinput ) . '</p>';
// Add the field above the directions link, or at the end of the address block.
if ( strpos( $output, '<div class="wpsl-location-directions">' ) !== false ) {
return str_replace( '<div class="wpsl-location-directions">', $field . '<div class="wpsl-location-directions">', $output );
}
$position = strrpos( $output, '</div>' );
return ( $position === false ) ? $output . $field : substr_replace( $output, $field, $position, 0 );
}

In a custom template

Grab the value with get_post_meta, and echo it where you want it to show up. Replace wpsl_appointment_url with the name of your own field, including the wpsl_ prefix.

$appointment_url = get_post_meta( $queried_object->ID, 'wpsl_appointment_url', true );
if ( $appointment_url ) {
echo '<p><a href="' . esc_url( $appointment_url ) . '">' . esc_html__( 'Make Appointment', 'wp-store-locator' ) . '</a></p>';
}

The Comments Form

Using comments on store pages requires you to complete the following steps.

  1. Add Comment Support
  2. Bulk Enable Comments
  3. Include the Comments Template

Add Comment Support

The first thing you need to do is to add comment support for the custom post type used by the store locator. You can do this by copying the code below and pasting it in the functions.php inside your active theme folder.

Reload the admin area and open a store location. In the sidebar on the right, a new “Discussion” panel shows up under the Store tab.

If it doesn’t show up, then open the options menu (the three dots in the top right corner), go to Preferences > Panels, and make sure “Discussion” is enabled.

Bulk Enable Comments

Now that the discussion panel shows up, you can enable the comments by ticking the “Allow comments” checkbox for all existing stores.

You could do this manually on every store page, but if you have lots of them, then it’s much easier to run a SQL query that bulk enables all the comments at once.

The code below does exactly that. Place the code in the functions.php inside your active theme folder, reload the page, and remove it again. The SQL query only needs to run once to enable the comments for all the existing store pages.

Include the Comments Template

The last step involves including the code that loads the comments template in the single-wpsl_stores.php.

Copy the code below, and place it wherever you want the comments form to show up in the single-wpsl_stores.php template.

If your theme doesn’t have its own comments.php, copy one from another classic theme, for example /wp-content/themes/twentysixteen/, to your theme folder.

If you choose to include all the used code examples, then the final template code will look like this.

<?php
/**
* Example of a single store template for a classic theme, including all the code examples.
*
* Change the HTML to match the single.php of your theme.
*/
get_header(); ?>
<div id="primary" class="content-area">
<main id="main" class="site-main" role="main">
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<h1 class="entry-title"><?php single_post_title(); ?></h1>
</header>
<div class="entry-content">
<?php
global $post;
$queried_object = get_queried_object();
// Add the map shortcode
echo do_shortcode( '[wpsl_map]' );
// Add the content
$post = get_post( $queried_object->ID );
setup_postdata( $post );
the_content();
wp_reset_postdata();
// Add the address shortcode, including a directions link.
echo do_shortcode( '[wpsl_address directions="true"]' );
// Show the appointment url
$appointment_url = get_post_meta( $queried_object->ID, 'wpsl_appointment_url', true );
if ( $appointment_url ) {
echo '<p><a href="' . esc_url( $appointment_url ) . '">' . esc_html__( 'Make Appointment', 'wp-store-locator' ) . '</a></p>';
}
// Include the comments template
comments_template( 'comments.php' );
?>
</div>
</article>
</main><!-- #main -->
</div><!-- #primary -->
<?php get_sidebar(); ?>
<?php get_footer(); ?>

Shortcodes