0

I am developing a plugin to include with a shortcode a form which must include Google map in order to retrieve latitude and longitude by dragging the marker.

EDIT QUESTION WITH COMPLETE CODE

I created the plugin architecture with Boilerplate (http://wppb.me/) and the structure of the core files is

Sighting Form plugin
    |————sighting-form-plugin.php
    |————public
            |————class-sighting-form-public.php
            |----partials
                   |-----sighting-form-public-display.php
    |————includes
            |————class-sighting-form.php
    |————admin
            |————class-sighting-form-admin.php

sighting-form-public.php

//other code to activate/deactivate the plugin  
/**
 * The core plugin class that is used to define internationalization,
 * admin-specific hooks, and public-facing site hooks.
 */
require plugin_dir_path( __FILE__ ) . 'includes/class-sighting-form.php';

/**
 * Begins execution of the plugin.
 *
 * Since everything within the plugin is registered via hooks,
 * then kicking off the plugin from this point in the file does
 * not affect the page life cycle.
 *
 * @since    1.0.0
 */
function run_sighting_form() {

    $plugin = new Sighting_Form();
    $plugin->run();

}
run_sighting_form();

includes/class-sighting-form.php

public function __construct() {
        if ( defined( 'SIGHTING_FORM_VERSION' ) ) {
            $this->version = SIGHTING_FORM_VERSION;
        } else {
            $this->version = '1.0.0';
        }
        $this->plugin_name = 'sighting-form';

        $this->load_dependencies();
        $this->set_locale();
        $this->define_admin_hooks();
        $this->define_public_hooks();

}
private function load_dependencies() {
 require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-sighting-form-loader.php';
 require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-sighting-form-admin.php'
 require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-sighting-form-public.php';

   $this->loader = new Sighting_Form_Loader();

}
//plus other code 

plugins/class-sighting-form-public.php

public function __construct( $plugin_name, $version ) {

        $this->plugin_name = $plugin_name;
        $this->version = $version;
        add_action('wp-footer', [$this, 'init_google_map']);
        add_action( 'rest_api_init',[$this, 'sigh_form_endpoint'] );

}
public function enqueue_scripts() {
    wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/sighting-form-public.js', array( 'jquery' ), $this->version, false );
        $api_key = get_option('sighting_form_settings_google_maps_api_key', '');
        wp_enqueue_script('google-maps',"https://maps.googleapis.com/maps/api/js?key=$api_key&libraries=places", array(), null, true);
}
//Initialize Google Map
    
public function init_google_map() {
        
        $map_zoom = get_option('sighting_form_settings_google_map_zoom', 2);
        $map_center = get_option('sighting_form_settings_google_map_center', '0,0');
        list($center_lat, $center_lng) = explode(',', $map_center);
    
        ?>
        <script>
            // Form Google Maps
            jQuery(document).ready(function($) {
            function initMap() {
    
                var map = new google.maps.Map(document.getElementById('google_map'), {
                    center: {lat: <?php echo $center_lat; ?>, lng: <?php echo $center_lng; ?>},
                    zoom: <?php echo $map_zoom; ?>
                });
    
                var marker = new google.maps.Marker({
                    map: map,
                    position: map.getCenter(),
                    draggable: true
                });
    
                google.maps.event.addListener(marker, 'dragend', function(event) {
                    document.getElementById('latitude').value = event.latLng.lat();
                    document.getElementById('longitude').value = event.latLng.lng();
                });
            }
            // Invoke initMap function when the document is ready
            initMap();
        });
        </script>
<?php
}

public function register_shortcodes() {
        if(!is_admin()){
        add_shortcode( 'sight-form-shortcode', array( $this, 'sighting_form_shortcode_function') );
        //add_shortcode( 'anothershortcode', array( $this, 'another_shortcode_function') );
        }
}
//includes the html/php code of the sighting form shortcode
public function sighting_form_shortcode_function() {
        require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/partials/sighting-form-public-display.php';
}
//create a custom rest-api finction to handle the mail sending of the form
public function sigh_form_endpoint(){
    register_rest_route('v1/sight-form', 'submit', array(
        'methods' => 'POST',
        'callback' => 'handle_sight_form_notice'

    ));
 }
//plus the handle_sight_form_notice function

admin/class-sighting-form-admin.php

//here there is all the settings about Google map that are correctly saved in Mysql wp_options.sql
//$api_key, $map_zoom, $map_center

public/partials/sighting-form-public-display.php

//here I wrote the html code for the form and the $.ajax to send data to the endpoint (it works). 
//the form fields related to the google map are as follow
         <div class="one-half-field">
             <label id = "latitude_label" >Enter value as decimal degrees (es. 45.5647)</label>
             <input type="text" name="latitude" id="latitude" placeholder = 'Latitude' >
         </div>
         <div class="one-half-field">
             <label id = "longitude_label" >Enter value as decimal degrees (es. 20.5647)</label>
             <input type="text" name="longitude" id="longitude" placeholder = 'Longitude'  >
         </div>
         <div class="full-field map-container">
             <p>If you don't know latitude and longitude, please move the marker to identify the approximate sighting position</p>
             <div id="google_map"></div>
         </div> 

Consider that the fields fetched by get_option() are registered via the class-form-admin.php in wp_options

The enqueued script is loaded correctly together with the $api_key, however I cannot see the map in the form, neither I see the function initMap in the footer.

What am I doing wrong?

2
  • 1
    there's a lot of information and context missing from this, e.g. it's not clear where these class functions are, how the class is instantiated and when that happens. It's also unclear if there are any errors in the browser dev console that might explain it. I also can't find a way to look at how WPPB structures its boilerplate to get any of those answers myself, nor do I see any shortcodes in your question, or how a form might be shown ( shortcodes don't have editor UIs ). Can you edit your question to try and answer as many of those questions as possible?
    – Tom J Nowell
    Mar 13 at 17:03
  • @TomJNowell I edited my code to answer your questions. I don't see any error in the inspector, but as I am new in developing a plugin for wordpress I may have missed something Mar 13 at 17:41

1 Answer 1

1

Problem 1

neither I see the function initMap in the footer.

This is because of the action here:

add_action('wp-footer', [$this, 'init_google_map']);

There is no wp-footer hook. You probably intended to use wp_footer: https://developer.wordpress.org/reference/hooks/wp_footer/

Though keep in mind this isn't guaranteed to be in a footer, just at the end of the HTML document before the closing body tag.

That shouldn't matter though because that's not how you're supposed to print inline javascript in WordPress, or expose variables to JS.

Instead:

Problem 2

That's not how shortcodes work! Shortcodes should never output directly:

Note that the function called by the shortcode should never produce an output of any kind. Shortcode functions should return the text that is to be used to replace the shortcode. Producing the output directly will lead to unexpected results. This is similar to the way filter functions should behave, in that they should not produce unexpected side effects from the call since you cannot control when and where they are called from.

https://developer.wordpress.org/reference/functions/add_shortcode/

Here a shortcode is registered:

        if(!is_admin()){
        add_shortcode( 'sight-form-shortcode', array( $this, 'sighting_form_shortcode_function') );
        //add_shortcode( 'anothershortcode', array( $this, 'another_shortcode_function') );
        }

The is_admin check is unnecessary ( and potentially problematic ), so remove that, but the real mistake is this:

//includes the html/php code of the sighting form shortcode
public function sighting_form_shortcode_function() {
        require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/partials/sighting-form-public-display.php';
}

A shortcodes callback returns a string, it's not supposed to output directly! Using echo or printing directly breaks the order of output and can cause syntax errors in REST API endpoints, shortcodes that appear in the wrong place, etc

That code is equivalent to this:

//includes the html/php code of the sighting form shortcode
public function sighting_form_shortcode_function() {
        require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/partials/sighting-form-public-display.php';
        return ""; // this shortcode has no output
}

We can enforce the correct behaviour by type hinting that the function returns a string, and using output buffers, e.g. with this function signature it's impossible to use the callback incorrectly as it would generate a PHP error:

//includes the html/php code of the sighting form shortcode
public function sighting_form_shortcode_function() : string {
4
  • Thanks @TomJNowell. The add_action('wp-footer', [$this, 'init_google_map']); is already in the __construct, at the beginning of the includes/class-sighting-form.php (see my code). The shortcode is working fine as it is. I showed above only the html form fields that are not appearing due to the script not loaded, but there are many other fields in the sighting-form-public-display.php that are visualized correctly with the shortcode. I tested the rest api (it sends the input data via email) and it works fine as well. I am going to try your suggestion wp_add_inline_script Mar 14 at 7:02
  • you've misunderstood wp-footer is not the same as wp_footer the hook uses an underscore, but you used a hyphen/dash. As for the shortcode, I guarantee it is a problem, one that becomes more obvious if you try to load those posts in the REST API, or use nested shortcodes. It outputs how you expected by accident, not because it is done correctly
    – Tom J Nowell
    Mar 14 at 8:57
  • 1
    see developer.wordpress.org/reference/functions/add_shortcode, I've updated the answer with a quote and link to that, shortcodes return their content, they don't echo/output it directly. If you want to use echo etc you need to wrap it in output buffers
    – Tom J Nowell
    Mar 14 at 9:00
  • 1
    Thanks @TomJNowell, your suggestions helped and yes the wp_footer was a typo that I didn't realize!! Now it works Mar 14 at 9:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.