Skip to content

AJAX

Pollora provides two ways to register WordPress AJAX handlers: the imperative Ajax facade and the declarative #[Ajax] attribute. Both default to logged-in users only (security-by-default).

Use the listen() method to register an AJAX handler inline:

use Pollora\Support\Facades\Ajax;
Ajax::listen('my_action', function () {
wp_send_json_success(['message' => 'It works!']);
});

By default, this registers wp_ajax_my_action only — the handler is restricted to logged-in users. Unauthenticated users cannot reach it.

// Default — logged-in users only (wp_ajax_*)
Ajax::listen('my_action', function () {
wp_send_json_success(['user' => wp_get_current_user()->display_name]);
});
// Explicit — all users, logged-in AND guests (wp_ajax_* + wp_ajax_nopriv_*)
Ajax::listen('public_action', function () {
wp_send_json_success(['message' => 'Hello everyone!']);
})->forAllUsers();
// Guest users only (wp_ajax_nopriv_*)
Ajax::listen('guest_action', function () {
wp_send_json_success(['message' => 'Hello guest!']);
})->forGuestUsers();
Ajax::listen('load_more_posts', [PostController::class, 'loadMore']);

Place the #[Ajax] attribute on a public method to auto-register it as an AJAX handler via the discovery system — no manual registration needed.

use Pollora\Attributes\Ajax;
use Pollora\Ajax\Domain\Model\AjaxAccess;
class NewsletterHandler
{
// Logged-in users only (default)
#[Ajax('subscribe')]
public function subscribe(): void
{
wp_send_json_success(['message' => 'Subscribed!']);
}
// All users (explicit opt-in)
#[Ajax('load_more', access: AjaxAccess::ALL)]
public function loadMore(): void
{
wp_send_json_success([/* ... */]);
}
// Guest users only
#[Ajax('track_visit', access: AjaxAccess::GUEST)]
public function trackVisit(): void
{
wp_send_json_success([/* ... */]);
}
}

The class is automatically discovered and instantiated via the service container, so constructor injection works as expected.

ParameterTypeDefaultDescription
actionstring(required)The WordPress AJAX action name
accessAjaxAccessAjaxAccess::LOGGEDAudience targeting
ValueWordPress HookAudience
AjaxAccess::LOGGEDwp_ajax_{action}Logged-in users only (default)
AjaxAccess::ALLwp_ajax_{action} + wp_ajax_nopriv_{action}Everyone
AjaxAccess::GUESTwp_ajax_nopriv_{action}Guests only

Why secure-by-default? Exposing wp_ajax_nopriv_* allows any unauthenticated visitor to call the endpoint. This should be a conscious decision, not an implicit default. Both the facade and attribute API require explicit opt-in to expose an endpoint publicly.

Send AJAX requests from JavaScript using the ajaxurl global provided by WordPress:

jQuery.post(ajaxurl, {
action: 'my_action',
_wpnonce: myApp.nonce,
data: 'some data'
}, function (response) {
if (response.success) {
console.log(response.data);
}
});

Make sure to localize your script with the nonce:

use Pollora\Support\Facades\Asset;
Asset::add('my-script', 'assets/js/app.js')
->toFrontend()
->localize('myApp', [
'nonce' => wp_create_nonce('wp_rest'),
]);