Custom Queries

Table of Contents

Developers are able to access MegaCalendar data for use in custom-built theme templates.

If you are a theme developer looking to use MegaCalendar Event data to build custom templates this is a good place to start. For most use cases these functions should suffice, if you need more in-depth functionality you can look into building your own custom API requests via the included API wrapper.

Commonly Used Functions

Template Functions

These functions are available directly from your theme or plugin. Click the function name for usage/implementation details.

megacal_events – Programmatically add MegaCalendar to a page

megacal_is_event_detail – Determine if the current page is the Event Detail page or not

See additional template functions here.

Instance Methods

These functions must be accessed through the plugin instance – See get_instance below. Click the function name for usage/implementation details.

MegabaseCalendar::get_instance – Get the Plugin instance in order to access the Instance methods

MegabaseCalendar::megacal_get_settings – Get the Plugin settings as an associative array

megacal_get_events – Query Events from the MegaCalendar API

megacal_get_public_events – Query published Events from the MegaCalendar API

get_event_detail_url – Get the url to the Event Detail page

See additional instance methods here.

The Basics

In general, if you want to query Events from MegaCalendar you’ll be using megacal_get_public_events or megacal_get_events.

Each of these functions accepts an array of arguments to help you filter the results to get exactly what you need. The list of acceptable arguments can be found here.

Our plugin converts the API responses into objects to make accessing the properties easier. The most common objects you’ll be dealing with are:

  • Event – The object representing an Event
  • Venue – The object representing a Venue
  • Category – The object representing an Event’s Category

Examples

This example shows how to query your next 4 upcoming Events, and display them as a list in a custom template

<?php
// Get the Plugin Instance
$megacalendar = MegabaseCalendar::get_instance();

// Query the next 4 events
$events = $megacalendar->megacal_get_public_events( array(
    'upcoming' => true,
    'max_result' => 4,
));
?>

<?php foreach( $events as $event ): ?>
    <?php $event_date = $event->get_event_date(); ?>

    <a class="eventBox boxWrap" href="<?php echo site_url( $megacalendar->get_event_detail_url( $event->get_id() ) ); ?>">
        <!-- If a Poster or Event Thumbnail Exists -->
        <?php if( !empty( $event->get_image_url_square() ) ): ?>
            <?php $thumbnail_src = $event->get_image_url_square(); ?>
            <img src="<?php echo esc_url( $thumbnail_src ); ?>" class="eventImg imgShadow alignleft" alt="<?php esc_attr_e( $event->get_title() ); ?>" />
        <?php else: ?>
            <div>
                <img src="<?php echo esc_url( megacal_get_default_event_image_path(), array( 'http', 'https' ) ); ?>" class="eventDefault artistReplace alignleft defaultPic" />
            </div>
        <?php endif; ?>
        
        <!-- Display the Event Date & Title -->
        <div class="text">
            <p class="date" title="<?php echo date('D M d, Y', strtotime( $event_date )); ?>"><?php echo date('l n/j/y', strtotime( $event_date )); ?></p>
            <p class="name"><?php esc_html_e( $event->get_title() ); ?></p>
        </div>
    </a>
<?php endforeach;  ?>

Complex Queries

The arguments passed to megacal_get_events allow you to filter the results in a multitude of ways. The best way to showcase how this works is by example. Below you will see several example queries showing how to filter your Event queries for different use cases.

See all of the acceptable arguments here.

Examples

This example shows how to query published Events by a set of known Category ids

<?php
// Get the Plugin Instance
$megacalendar = MegabaseCalendar::get_instance();

// Query public Events by Category
$events = $megacalendar->megacal_get_events( array(
    'published' => true,
    'category' => array( 1, 2, 3 ),
));
?>

This example shows how to query all unpublished Events

<?php
// Get the Plugin Instance
$megacalendar = MegabaseCalendar::get_instance();

// Query unpublished Events 
$events = $megacalendar->megacal_get_events( array(
    'published' => false,
));
?>

This example shows how to use max_result and start_row to query different pages of results for public events

<?php
// Get the Plugin Instance
$megacalendar = MegabaseCalendar::get_instance();

// Query the first page of events
$events_page_1 = $megacalendar->megacal_get_public_events( array(
    'max_result' => 10,
    'start_row' => 0,
));

// Query the second page of events
$events_page_2 = $megacalendar->megacal_get_public_events( array( 
    'max_result' => 10, 
    'start_row' => 10,
));
?>

This example shows how to query a list of published AND unpublished Events in a certain date range

<?php
// Get the Plugin Instance
$megacalendar = MegabaseCalendar::get_instance();

// Query ALL events in January 2023
$events = $megacalendar->megacal_get_events( array(
    'start_date' => '2023-01-01',
    'end_date' => '2023-01-31',
));
?>

This example shows how to query a list of published Events at a specific venue, in a certain date range

<?php
// Get the Plugin Instance
$megacalendar = MegabaseCalendar::get_instance();

// Query Events at Venue id 25 from January 2023
$events = $megacalendar->megacal_get_public_events( array(
    'venue' => 25,
    'start_date' => '2023-01-01',
    'end_date' => '2023-01-31',
));
?>

There are significant combinations of filters that you could use when building your own complex queries, but we hope these examples provide enough context to get you started.

Advanced


Caching

The MegaCalendar Plugin has a robust caching system in place in order to speed up loading times, reduce API requests, reduce database calls, and reduce the overall cost of operation. This cache system also extends to our megacal_get_events convenience functions.

That said, we wanted to provide some detail about how our cache system is built for curious developers, those who choose to bypass our cache system and build their own, or those who are building a more complex implementation that requires direct API interaction.

In this section, we’ll provide some insight into how we solved our caching needs in the internals of the Plugin, and we’ll make some suggestions as to different ways that you can implement your own cache on top of MegaCalendar.  We recommend reading through the cache system outline as a primer to implementing your own cache, so that you have a base-level understanding of the internal cache system before getting into development.

There are two layers of caching generally used in WordPress: transients and the wp_cache functions. The primary difference between these on a standard WordPress installation is that transients are database-level cache and the wp_cache functions are in-memory cache (meaning they will only persist until the next page load). We use both types, where applicable, and will outline how you can do the same.

Let’s start with a basic example:

$events = $megacalendar->megacal_get_event_request( array( 
    'upcoming' => true,
));

This call on its own will hit the MegaCalendar API every time this page is loaded. That means time wasted sending the request, waiting for a response, and converting the response into Event objects before your template code even begins to run.

We can improve this using transients, to store the result in the database:

$events = get_transient( 'my_custom_event_list' );

if( $events === false ) {
    $events = $megacalendar->megacal_get_event_request( array( 
        'upcoming' => true,
    ));
    
    set_transient( 'my_custom_event_list', $events );
}

Now we are first checking for a transient in the database called my_custom_event_list, and storing the result in $events. Only if it doesn’t exist do we actually make the call to megacal_get_public_events, after which we call set_transient so that the next time this code runs that data will be stored in the database. There are still some ways we can improve this code.

  • First: We don’t specify a transient expiration time which gives us less control and knowledge about how it will behave.
  • Second: Our cache isn’t flushed with the rest of the Event cache.
  • Third: We call the database every time get_transient runs. In this case, that’s probably okay but if our code was in a loop, for example, we are doing unnecessary work.

Let’s solve the expiration time first. It’s important to note that transient expiration time isn’t guaranteed, your transients might be cleared sooner than their expiration time, but they should never be available after their expiration time. This gives us the ability to set some expectations for at least how often we want to enforce a fresh API hit. We also need to decide what that expire time should be, it can be any length of time represented in seconds, in this case we’re going to use the MegaCalendar Event Cache expire time constant. This is 1 hour from the time that the transient is created, and will be available in your theme as long as the Plugin is active.

$events = get_transient( 'my_custom_event_list' );

if( $events === false ) {
    $events = $megacalendar->megacal_get_event_request( array( 
        'upcoming' => true,
    ));

    set_transient( 'my_custom_event_list', $events, MEGACAL_EVENT_CACHE_EXPIRE_TIME );
}

As you can see, not much has changed, but we specified the expiration time in the set_transient call.

Next, let’s make our cache flush when a site user clicks the “Flush Event Cache” button, adds/deletes Events, or changes Event details. We need to add our transient name to the Event Cache keys by adding a filter into functions.php.

// .. functions.php ..
function my_custom_event_cache_keys( $cache_keys ) {
    // Include my_custom_event_list cache key when event cache is flushed
    $cache_keys[] = 'my_custom_event_list';
    return $cache_keys;
}
add_filter( 'megacal_flush_event_cache_keys', 'my_custom_event_cache_keys' );

$cache_keys is the array of all cache keys that get flushed when a user interacts with Events or explicitly flushes the cache. We add our custom transient key to the array, then return the whole array. You can add as many custom keys as you need this way, and our existing system will handle the user interaction so you don’t have to think about it.

Finally, we can utilize the wp_cache functions if we want to further cache the results in memory. This is not strictly necessary and is best used when running the same query multiple times on a single page or when using a persistent memory cache such as Memcache or Redis.

$events = wp_cache_get( 'my_custom_event_list_cache', '', false, $found );

if( $found === false ) {
    $events = get_transient( 'my_custom_event_list' );

    if( $events === false ) {
        $events = $megacalendar->megacal_get_event_request( array( 
            'upcoming' => true,
        ));

        set_transient( 'my_custom_event_list', $events, MEGACAL_EVENT_CACHE_EXPIRE_TIME );
    }

    wp_cache_set( 'my_custom_event_list_cache', $events, '' );
}

Now we are first checking wp in-memory cache for the key ‘my_custom_event_list_cache’. Only when it doesn’t exist, we make the call to get_transient, and then only when that also doesn’t exist do we continue to make the call to the API. If ‘my_custom_event_list_cache’ wasn’t found at the start, we set it using wp_cache_set after either getting the result from the transient or from the API.

Caching Advanced Queries

In the previous section, we had a relatively simple query that could easily be cached by storing the query result in a transient, but what if you have a dynamic query? Some examples might include, but are not limited to:

  • A filter allowing end-users to filter Events by a specific Venue or specific Categories
  • A filter allowing end-users to specify start and end date
  • A filter allowing admins to filter by published or unpublished

You can probably see how the ambiguity of the queries would complicate the simple caching system that was outlined above.

We took a couple of approaches to this issue in the views inside of the plugin, and you can utilize a similar approach where it makes sense in your own templates. The underlying concept is as follows:

  • Create a transient key to store your query results
  • Your transient will hold an array of results identified by unique sub-keys related to the specifics of the query
  • Update the cache every time a variant of your query is used

The caveat to this approach is that a cold cache hit for any sub-key will result in a reset of the transient’s expiration time. For us in the context of the Plugin, that trade-off was acceptable.

Your sub-keys can be any uniquely identifying key. In the Plugin, for example, we use Event ID to store Event Details results in a single transient, and we use {start_date}_{end_date} to cache months on the Calendar. However, the simplest approach that will work in every case is to create an md5 string from the arguments passed to your query.

The md5 checksum will always be the same for the same set of arguments, no matter what those arguments are. Below is a brief example showing the concept in practice

// Get event category from query string param
$cat = intval( $_GET['event_category'] );

$args = array(
    'upcoming' => true,
    'category' => array( $cat ),
);

// Create the sub-key
$cache_sub_key = md5( serialize( $args ) );
$cache = get_transient( 'my_custom_event_cat_queries' ); 
if( false === $cache || !array_key_exists( $cache_sub_key, $cache ) ) {

    $events = $this->megacal_get_event_request( $args );

    $cache = empty( $cache ) ? array() : $cache;
    $cache[$cache_sub_key] = $events;

    set_transient( 
        'my_custom_event_cat_queries', 
        $cache, 
        MEGACAL_EVENT_CACHE_EXPIRE_TIME
    );

} else {
    $events = $cache[$cache_sub_key];
}


Custom API Requests

If you need functionality beyond simply querying Events, you will need to implement your own API requests. When building custom API requests, you can programmatically create or delete events, change Venue or Category details, change User details, interact with Event approvals, and more.

Building your own API requests is an extremely in-depth topic and is covered in more detail on this page. If you have specific implementation questions, please reach out and we will do our best to assist you.