4

What I am trying to achieve is the following: for every user on our website, I want to do an API request to a service (local REST API interacting with another database) once and then cache the result in the WP_User (sub)class until the user will logout and login again (as this value is used on every page in the application once so otherwise it would have to be retrieved once for every page load, which is very undesirable performance-wise).

The most elegant way in terms of Separation of Concerns I have found up until now, is done by extending (subclassing) the WP_User class as per example featured in the O'Reilly book Building Web Apps with WordPress By Brian Messenlehner & Jason Coleman.

The example code can be seen here: see this file on the author's GitHub.

The problem however is, that we still do not have this Student (extends WP_User, so subclass) available in our code, we still need to instantiate it in the following way to get one Student instance for the current user:

$student = new Student($current_user->ID);

If we do that on a page, the instance will always be created again (hence me referring to the lifecycle in the title) and the call to $student->assignments seems to never be cached inside the WP_User subclass itself after navigating to a new page and/or reloading the page, so for every page load we are hitting the API and database which will probably never perform in our high-traffic production environment.

The $current_user global variable within WordPress itself however (which is an WP_User instance) seems to be created directly after logging in and is then available throughout the whole application as far as my understanding goes. What I really want, is the same availability throughout the application but then for my subclass (Student) instead of the WP_User class, but more importantly, I want to make sure that for every logged in user, the API hit is only done once (just like for $current_user->user_login which is in WP_User for example).

I have also looked into adding user_meta to WP_User, and checked out this question related to it which seemed partially helpful: Does WordPress cache get_user_meta() results?

However, this is retrieved through wp_cache_get which is WordPress Object Cache which clearly states:

  1. Non-persistent cache is available only during the loading of the current page; once the next page loads, it will be blank once again.
  2. The storage size is limited by the total available memory for PHP on the server. Do not store large data sets, or you might end up with an “Out of memory” message.
  3. Using this type of cache makes sense only for operations repeated more than once in the creation of a page.

We are not using the values in the Student subclass more than once in the creation of the page, however, the value is used on every page in the application once so it would have to be retrieved once for every page load.

Am I just thinking in the wrong direction here, or how would this be possible within WordPress? I really need a good long-term solution here which should perform in a high-traffic production environment. Thanks for all input and help in advance!

1

If I did understand well, we need to cache a value retrieved from another REST service, from the login to logout of the user on the wordpress installation, so we will hook into this wp_login to get the value and cache it using Transient API, Options API or a persistent caching plugin.

add_action('wp_login', 'my_get_and_cache_rest_value');
function my_get_and_cache_rest_value ($user_login, $user) {
    // do your rest call
    // cache it using Transient API, Options API or a persistent caching plugin
}

We can then extend the WP_User object and set a our magic calls to get the data we want from the cache.

class MY_User extends WP_User {
    // no constructor so WP_User's constructor is used

    // method to get cached data
    function getMyCachedData() {
        // get data via my cache solution
        if ( ! isset( $this->data->myData ) )
            $this->data->myData = my_get_cached_data( $this->ID );

        return $this->data->myData;
    }

    // magic method to detect $user->my_data
    function __get( $key ) {
        if ( $key == 'my_data' )
        {
            return $this->getMyCachedData();
        }
        else
        {
            // fallback to default WP_User magic method
            return parent::__get( $key );
        }
    }    
}

I hope this would help someone, and cheers to @Daniel.

0

Your question is a bit broad, but the main issue seems to be that for every new student you want to approach an external API, only once.

To do this you must hook into wp_insert_user, which is the core function that registers a new user. By definition this function is only called once for every new user. At the end of this function you see a hook user_register, which is where you can approach the API.

add_action ('user_register','wpse185731_approach_api');
function wpse185731_approach_api ($user_id) {
  $userdata = get_userdata( $user_id );
  if ( some condition based on $userdata) {
    ... approach API with $userdata, then store with:
    add_user_meta( $user_id, $meta_key, $meta_value, $unique );
    }
  }

You would probably want to do this in a plugin, not in your theme.

  • The question states "I want to do an API request to a service (local REST API interacting with another database) once and then cache the result in the WP_User (sub)class until the user will logout and login again" so only populating this value upon user registration is not sufficient. – Don May 25 '18 at 14:45
0

What follows doesn't answer the entire question, but it does address this part:

@Daniel: The $current_user global variable... is then available throughout the whole application as far as my understanding goes. What I really want, is the same availability throughout the application...

I had a similar need, and this is what I came up with.

There is a set_current_user hook (within the wp_set_current_user() function), and because the $current_user global variable is set to an instance of WP_User by the time the action is fired, you can use this hook to "do stuff" to it.

Calls to wp_get_current_user() basically return the value of the global $current_user, but could end up calling wp_set_current_user() under certain conditions, which will cause your custom action to fire.

So, in a custom plugin (I don't think this would work in a theme function.php file), you can define an action:

add_action( 'set_current_user', 'extend_current_user', PHP_INT_MAX );

And then your action can overwrite the global $current_user:

public function extend_current_user()
{
    global $current_user;

    if( $current_user->ID == 0 )
        return;

    $current_user = new Student( $current_user->ID );
}

Your Student class could then implement the Transient API to cache the RESTful data and offer methods or properties that will expose that data. And because the global $current_user will stay as an instance of Student, those methods/properties will always be available from wp_get_current_user() anytime you need them.

Of course, by doing this, a defensive programmer will want to verify that any call to wp_get_current_user() does return an instance of Student before calling any methods on it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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