This is my very first question here. So please don’t be too angry, if I miss something out or make mistakes while using the plattform. I was, of course, trying to find an answer on my problem first. Didnt work out too good 🙂
My Problem: I’m trying to implement more dynamic content on a page, so I’m working around with ajax-function-calls in a shortcode.
I managed to follow a lot of tutorials and successfully created a single shortcode with a post-type-parameter, which outputs f.e. the last 5 posts of a type and a pagination. I created the needed ajax function call in js and everything. Its working and so far I thought, I understand the logic behind it.
But when I’m using the shortcode, with another post-type-parameter, twice or more on the same page, only the content of the last shortcode is loaded.
A single shortcode with a given post type is always loading the content correctly.
So now I’m stucked and I think, maybe I didnt get the idea behind ajax-calls and localization for js in wordpress, yet.
If you add the script via action, like you should, its loaded only once, of course. How does localization work in general, when an “ajax.js” is called more than once on a page?
Do I maybe have to create ajax_actions dynamically for every shortcode in a case like that?
I will post my code, if you wish, but maybe the general answer already helpes me to understand the idea behind it.
greetings to everybody
EDIT:
Okay, here is the code:
functions.php
require_once KNE_THEME_DIR . '/includes/helpers/view-classes/class-kne-theme-data-slider.php';
$slider = new KNE_DATA_SLIDER();
add_shortcode('kne_data_slider', array($slider, 'shortcode'));
// creating Ajax call for WordPress
add_action('wp_ajax_nopriv_kne_ajax_posts', array($slider, 'get_data_callback'));
add_action('wp_ajax_kne_ajax_posts', array($slider, 'get_data_callback'));
the dataslider-class
<?php
defined('ABSPATH') or die('No script kiddies please!');
class KNE_DATA_SLIDER
{
/** Data Slider Konstruktor */
public function __construct()
{
}
/** Shortcode */
public function shortcode($atts)
{
extract(shortcode_atts(array(), $atts));
$posts_total = (isset($atts['posts_total']) ? $atts['posts_total'] : -1);
$posts_per_page = (isset($atts['posts_per_page']) ?$atts['posts_per_page'] : -1);
$post_type = (isset($atts['post_type']) ? $atts['post_type'] : 'post');
$kne_unique = rand(1, 9999);
$nonce = wp_create_nonce('kne_ajax_nonce');
wp_register_script('kne_ajax_js', KNE_THEME_URI . '/assets/js/ajax-implementation.js', array('jquery'),'', false);
wp_enqueue_script('kne_ajax_js');
$data_string = [
'ajaxurl' => admin_url('admin-ajax.php'),
'security' => $nonce,
'page' => $page = (isset($page) ? $page : 1),
'post_type' => $post_type,
'posts_total' => $posts_total,
'per_page' => $posts_per_page,
'kne_unique_id' => $kne_unique
];
wp_add_inline_script('kne_ajax_js', 'var kne_ajax_object = ' . json_encode($data_string), 'after');
// wp_localize_script('kne_ajax_js', 'kne_ajax_object', $data_string);
KNE_THEME_ENGINE::add_bootstrap();
ob_start(); ?>
<div class="kne-posts-container-<?php echo $kne_unique ?>">
<span class="kne-loader">Loading...</span>
<div class="kne-post-content">
</div>
<div class="kne-post-content_name">
</div>
</div>
<?php return ob_get_clean();
}
/** Get Data Callback */
public function get_data_callback($atts)
{
// check the nonce - still too simple
check_ajax_referer('kne_ajax_nonce', 'security');
//get the data from ajax() call
if (isset($_POST['page'])) {
$post_type = $_POST['post-type'];
$page = $_POST['page'];
$page -= 1;
$per_page = (isset($_POST['per_page']) ? $_POST['per_page'] : -1); //set the per page limit
$start = $page * $per_page;
$all_the_posts = new WP_Query(array(
'post_type' => $post_type,
'post_status ' => 'publish',
'orderby' => 'post_date',
'order' => 'DESC',
'posts_per_page' => $per_page,
'offset' => $start
));
if ($all_the_posts->have_posts()) {
while ($all_the_posts->have_posts()) {
$all_the_posts->the_post();
echo the_title('<h3>', '</h3>', true);
}
}
die();
}
} // end of data-callback-function
} // end of class
the js
jQuery(document).ready(function ($) {
console.log('Ajax Implementation loading');
var post_data = {
'action' : 'kne_ajax_posts',
'security' : kne_ajax_object.security,
'page' : kne_ajax_object.page,
'post-type': kne_ajax_object.post_type,
'kne_unique_id' : kne_ajax_object.kne_unique_id,
'per_page' : kne_ajax_object.per_page,
}
$.post({
url: kne_ajax_object.ajaxurl,
data: post_data,
type: 'post',
success:
(response) => {
console.log(response);
var container = $('.kne-posts-container-' + kne_ajax_object.kne_unique_id);
$(container).append(response);
// var name_active_link = $(container + ' .kne-post-content' + '_name' + ' .kne_posts_pagination li.active');
// $(name_var).html(response);
// $(container + ' .kne-loader').css('display', 'none');
// $(name_active_link).on('click',function(){
// console.log('clicked');
// var page = $().attr('p');
// kne_posts_make_ajax_call(kne_ajax_object, page);
// });
},
error:
(response) => {
console.warn(response);
The problem is that your JS needs to know the post type for each shortcode, but rather than looking at the shortcodes output, it looks at the main global
kne_ajax_object
, and since there is only onekne_ajax_object
, there can only be one post type.Additionally, your javascript code only runs one time, and only makes one AJAX call, which means there can only be one post type and one shortcode.
Instead, store the AJAX URL/security stuff in
kne_ajax_object
but put the rest of the information on the HTML the shortcode generates.E.g. instead of this:
Why not this?
Then do the equivalent of this in JS:
Or even better, use the REST APIs that WordPress already provides. WP already has AJAX friendly data only URLs that return all this information as JSON, with pretty URLs like
example.com/wp-json/wp/v2/posts
.Additional Notes
the_title
already echo’s so this is the same asecho echo get_title(
, you don’t need to useecho
on most functions that begin withthe_
Using
-1
forposts_per_page
is bad, I know you want to list all posts, but can your server load an infinite number of posts? If it can handle 500 or less posts, why not set it to 500? Why chance that your server will fallover trying to send thousands of posts? Setposts_per_page
to a high number you never expect to reach, but you know your server can actually handle.There’s no point using
offset
, this isn’t going to apply some old MySQL pagination speed trick. Just used the standard parameter'paged' => $page
, it might even be faster.Finally, escaping. This is bad code:
Are you sure
$url
is a URL? Do you trust that? What if your site was hacked and it actually contains"><script>nasty stuff goes here</script>
?Instead escape:
Now it is always a URL, it is guaranteed, we no longer need to trust
$url
. If it contains something nasty, it’ll be neutered, cut down to a URL shaped size, and the worst case scenario is now a broken link.Finally, this:
This is not object oriented programming, it’s just functions being put inside a class. If you removed the class and turned them into pure functions it would work exactly the same. It’s just additional boilerplate.
If you’re going to create classes and objects, do it because you need to. If you don’t plan to create more than one of an object, and your class has no internal state, it’s usually a sign you don’t need a class/objects for the task. Namespaces will serve the same purpose just as well. A lot of developers think that as they get more experienced everything becomes objects and OOP, and use classes for everything regardless of what it is or wether it benefits tem, but that’s not true. OOP is a tool that can be very useful, but it’s not a universal hammer that does everything, use it when it’s needed but for most things in WordPress like simple shortcodes or action hooks it’s overkill.