Kellen Mace, Author at WebDevStudios https://webdevstudios.com/author/kellen/ WordPress Design and Development Agency Mon, 15 Apr 2024 16:02:10 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.2 https://webdevstudios.com/wp-content/uploads/2022/07/cropped-wds-icon.white-on-dark-60x60.png Kellen Mace, Author at WebDevStudios https://webdevstudios.com/author/kellen/ 32 32 58379230 The Journey to 1.0 — WDS Blocks and WordPress Gutenberg https://webdevstudios.com/2018/05/22/wds-blocks-wordpress-gutenberg/ https://webdevstudios.com/2018/05/22/wds-blocks-wordpress-gutenberg/#comments Tue, 22 May 2018 16:00:02 +0000 https://webdevstudios.com/?p=18432 WordPress made a big leap into the world of modern web development with its announcement of Gutenberg last year—reimagining what WordPress is and can be by providing a new interface for creating content built partially on the React.js platform. Since it will most likely be rolled into Core sometime mid to late 2018, WebDevStudios (WDS) Read More The Journey to 1.0 — WDS Blocks and WordPress Gutenberg

The post The Journey to 1.0 — WDS Blocks and WordPress Gutenberg appeared first on WebDevStudios.

]]>
WordPress made a big leap into the world of modern web development with its announcement of Gutenberg last year—reimagining what WordPress is and can be by providing a new interface for creating content built partially on the React.js platform. Since it will most likely be rolled into Core sometime mid to late 2018, WebDevStudios (WDS) is looking toward the future. Enter WDS Blocks.

As of May 2018, WordPress runs ~30.7% of the internet. For better or worse, it’s going to be hard to ignore the impact Gutenberg will have on the web when it officially launches. The new feature is being teased to the public now, and though it’s nowhere near a stable final release, its adoption rate is growing exponentially and WDS needed a game plan to ensure we’re not behind the curve.

Pre-Planning

In August 2017, during one of our weekly leadership calls, the topic of Gutenberg was brought up. Everyone thought the project was a bit premature to start building on top of. We discussed the possible future implications but weren’t ready to give the idea of building our own custom Gutenberg blocks any serious attention. Gutenberg had just barely been announced, and like many suggested features, its future was uncertain. So it was tabled for several months.

Four months later, WDS named Greg Rickaby to the position of Director of Engineering, and by this time, Gutenberg had become the biggest topic in the WordPress space—there was no ignoring it.

Greg spoke with Cristina Holt, our Director of Project Management, and the initial thoughts were to approach Gutenberg just like a client project and pitch it to our CEO, Brad Williams, and put the full weight of our talented developers behind the project.

“First we needed to train our engineers and project managers,” explains Greg. “Second, we had to come up with a development plan and schedule, then finally put together a project team. It took a few conversations with leadership, but Brad and Lisa eventually agreed to let us move forward with the ‘WDS Gutenberg Project.’”

Once the project was approved, Cristina and Greg assembled a team which would be led by Lead Frontend Engineer, Corey Collins, and Lead Backend Engineer, Kellen Mace. The engineering team was rounded out by Jo Murgel, Eric Fuller, and Will Schmierer.

Our first release was to replicate or replace components such as Heroes, Cards, and Calls-To-Action. These are components that are commonly used by our clients on typical website projects and are already included in our starter theme, wd_s. We call these “Global Content Blocks” and they’re powered by Advanced Custom Fields’ “Flexible Content” feature.

The Training Plan

Zac Gordon has been leading the charge on educating WordPress developers since Matt Mullenweg so famously said, “Learn JavaScript, deeply.” We purchased Zac’s Gutenberg course (now called WordPress Block Editor) over at https://javascriptforwp.com/. After all, the best way to learn anything is to immerse yourself.

Each of our engineers was scheduled some time to take the course. Once they completed their training, they could get spun up on WDS Blocks.

The Development Plan

While training, Kellen Mace had read about Ahmad Awais’s Create Guten Block on Github, which bills itself as a zero-configuration toolkit for spinning up Gutenberg blocks. He proposed using it to provide the foundation for our WDS Blocks project. Finding this toolkit was honestly a relief, as the thought of scaffolding the project and configuring Webpack from scratch wasn’t something any of us really wanted to do.

We also needed to provide greater advanced settings for each block including the ability to change the background decoration (image, color, video), add some visual flair (animate.css classNames), and a few other options to provide greater control to users beyond what is inherently available from WordPress out of the box.

Our blocks needed to provide a simple and clean backend experience with a fairly generic theme-friendly frontend render. For release 1.0 we ended up working toward a hero, call to action area, recent posts grid, manual-select related posts grid, a dual-column layout, and a gist embed. Since then we’d added several updates to these blocks and a users grid block with room to grow going forward.

The Result

Like most stories, the journey is far more interesting than the end result. We learned a lot about what Gutenberg can and can’t do, why they chose React.js, and why they added an abstraction layer.

Utilizing Gutenberg’s Inspector Control tools, we were able to create a set of Background, Text, and Other Options that we could easily replicate across all of our blocks. We even wrote about it so we could share what we learned and how we were able to keep our code DRY in order for our options to be as easy as possible to implement in new blocks.

Despite the insistence from WordPress themselves that aMultiSelect component wasn’t necessary, Issue #1044, we found the use case for such a component important. So we built one as a part of our recent posts and users grid blocks.

During the rest of our development, we found ourselves needing to reuse bits and pieces throughout our blocks. In addition to creating seven new blocks (plus a default block to copy from), we also created 10 new reusable components! These cover Options noted above, post search, and MultiSelect amongst others. Being able to create standalone components, and control their output at the component level, helps us keep a clean and consistent codebase across all of our blocks.

Based on the way Gutenberg is built and how it renders markup to post_content there was a problem with creating dynamic or non-static blocks that needed to be worked out. More on that at the WDS Blog: WordPress Gutenberg — Arrays, Attributes, and the Fundamental Flaw – WebDevStudios.

Initially, our plan was to render all of our blocks via PHP because we ran into issues when updating the markup for blocks. For instance, when rendering in JSX, if you were to add a className to your block container, you would receive a popup in the post editor informing you that your block was no longer valid, and the new className would not be present on the front-end. After some digging, we were able to realize that Gutenberg’s deprecated function was exactly what we needed. This allows for you to make changes to your block, to have those updates visible on both the backend and frontend of your site and to avoid the “invalid markup” popup after changing your block’s markup. You can read more about Deprecated Blocks in the Gutenberg Handbook.

Finally, WordPress provides a feature for styling both the front and backend render. Before Gutenberg existed, you’d have a simple text-based WYSIWYG editor with no real indication of how the content might look on the front end. Appending theme styles to a WordPress Gutenberg Block has never been easier, with its shared styles allowing for a user experience which more closely resembles that of the frontend.

The Future

This is just the beginning. Every release of Gutenberg unveils changes in functionality, bug fixes and deprecated features forcing us to keep up with testing and look for additional functionality that can improve the user experience.

We plan to use WDS Blocks as a starting point for new website projects, providing many of the common blocks that our clients will likely need, then add to those any project-specific blocks.

We’ve opened the repo to the public and encourage everyone to get involved with the project. We welcome your help as we work to build out a library of powerful and useful blocks that take full advantage of WordPress’ new editing experience.

The post The Journey to 1.0 — WDS Blocks and WordPress Gutenberg appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2018/05/22/wds-blocks-wordpress-gutenberg/feed/ 1 18432
Working with Transients like a Boss https://webdevstudios.com/2016/07/19/working-transients-like-boss/ https://webdevstudios.com/2016/07/19/working-transients-like-boss/#comments Tue, 19 Jul 2016 13:00:24 +0000 https://webdevstudios.com/?p=13438 We’ve covered the reasons why using transients (and caching in general) can greatly enhance the performance of WordPress sites. I’m offering up what I find to be two compelling solutions for pain points that are often encountered when working with transients–how to create dynamic keys and delete transients in bulk. Creating Dynamic Keys Let’s say I have Read More Working with Transients like a Boss

The post Working with Transients like a Boss appeared first on WebDevStudios.

]]>
We’ve covered the reasons why using transients (and caching in general) can greatly enhance the performance of WordPress sites. I’m offering up what I find to be two compelling solutions for pain points that are often encountered when working with transients–how to create dynamic keys and delete transients in bulk.

Creating Dynamic Keys

Let’s say I have a function that accepts an array of three arguments and tries to get a transient that is specific to those parameters. If found, the transient’s data will be used. If not, the data will be regenerated. One common way to do this is as follows (see line 24 in particular):

/**
 * Get the number of posts.
 *
 * @param array $args {
 *     @type string $post_type   The post type.
 *     @type string $after_date  The earliest date to get posts for.
 *     @type string $before_date The latest date to get posts for.
 * }
 * @return int|bool              The number of posts.
 */
function wds_get_post_count( $args ) {

    // The default arguments.
    $defaults = array(
        'post_type'  => 'post',
        'after_date' => '2016-1-1',
        'before_date' => '',
     );

    // Merge arguments passed in with the defaults.
    $args = wp_parse_args( $args, $defaults );

    // Try to get data from transient.
    $post_count = get_transient( 'wds_post_count_' . $args['post_type'] . '_' . $args['after_date'] . '_' . $args['before_date']  );

    // If the transient was not found, regenerate the data.
    if ( ! $post_count ) {
        $post_count = wds_regenerate_post_count( $args );
    }

    return $post_count;
}

This results in dynamically created transient keys such as wds_post_count_post_2016-1-1_2016-7-1.

This technique is potentially problematic if you have values with spaces in them, arrays, or values such as true, false, or null. Extra checks would have to be in place to account for those possibilities and convert those values to something that can be used in the transient key every time we get, set, or delete it. Doing so can become messy and unwieldy–and the transient keys become longer and longer the more arguments you add to them. We can forego those unnecessary complications by replacing the code on line 24 with the following, instead:

$post_count = get_transient( 'wds_post_count_' . md5( serialize( $args ) )  );

serialize() will create a storable representation of a value, then passing that to md5() will create a unique hash of the data that’s always exactly 32 characters. Best of all, we can pass our entire $args array to those functions without the need to break out, validate, and add each of its values to the transient key individually. This results in keys such as:

wds_post_count_B3A076241698F988761FA618286DF384

Technically speaking, you could get rid of the wds_post_count_ prefix, but I recommend leaving it in so that part of the key is still human-readable and provides an indicator of what data it stores. Another bonus: If you’re using a GUI tool to view the database, you can sort the wp_options table by the option_name column, and all options with the name _transient_wds_post_count_* will be grouped together and easy to locate.

A complete example showing how to use serialize() and md5() when getting and setting transients is below.

<?php

/**
 * Get the number of posts.
 *
 * @param array $args {
 *     @type string $post_type   The post type.
 *     @type string $after_date  The earliest date to get posts for.
 *     @type string $before_date The latest date to get posts for.
 * }
 * @return int|bool              The number of posts.
 */
function wds_get_post_count( $args ) {

    // The default arguments.
    $defaults = array(
        'post_type'  => 'post',
        'after_date' => '2016-1-1',
        'before_date' => '',
     );

    // Merge arguments passed in with the defaults.
    $args = wp_parse_args( $args, $defaults );

    // Try to get data from transient.
    $post_count = get_transient( 'wds_post_count_' . md5( serialize( $args ) )  );

    // If the transient was not found, regenerate the data.
    if ( ! $post_count ) {
        $post_count = wds_regenerate_post_count( $args );
    }

    return $post_count;
}

/**
 * Regenerate the number of posts.
 *
 * @param array $args {
 *     @type string $post_type   The post type.
 *     @type string $after_date  The earliest date to get posts for.
 *     @type string $before_date The latest date to get posts for.
 * }
 * @return int|bool              The number of posts.
 */
function wds_regenerate_post_count( $args ) {

    // Run a query to get the data.
    $posts = new WP_Query( array(
        'post_type'  => $args['post_type'],
        'date_query' => array(
                'after'  => $args['after_date'],
                'before' => $args['before_date'],
            ),
    ) );

    // If posts were found,
    if ( $posts->have_posts() ) {

        // Save the data to a transient.
        set_transient( 'wds_post_count_' . md5( serialize( $args ) ), $posts->post_count, MONTH_IN_SECONDS );

        // Return the data.
        return $posts->post_count;
    }

    return false;
}

Deleting Transients in Bulk

If you’ve ever used WordPress’ wp_cache_* functions to set and get data using the object cache, you know that the $group parameter can be used to assign cached data to a group, then wp_cache_delete() can be used to easily delete the cached data within that group. Handy! When working with transients, we have no such luxury. Because of this, it’s common practice to loop through all possible key permutations, calling delete_transient() each time, like this:

// Get all post types.
$post_types = get_post_types();

// Loop through each post type.
foreach ( $post_types as $key => $post_type ) {

    // Get all possible values for after & before dates.
    $after_dates  = wds_get_post_count_after_dates();
    $before_dates = wds_get_post_count_before_dates();

    // Loop through all after dates.
    foreach ( $after_dates as $after_date ) {

        // Loop through all before dates.
        foreach ( $before_dates as $before_date ) {

            // Delete the transient.
            delete_transient( 'wds_post_count_' . $post_type . '_' . $after_date . '_' . $before_date );
        }
    }
}

As you can see, having several nested loops in place just to make sure all transient values are deleted is cumbersome, and can be prone to error in the event that even one permutation of the transient key isn’t accounted for. Instead of all of that, wouldn’t it be nice to just be able to delete all transients whose keys begin with wds_post_count? One of our resident WDS code wizards, Parbs, devised the set of functions below that can be used for just such a purpose.

/**
 * Delete all transients with a key prefix.
 *
 * @param string $prefix The key prefix.
 */
function wds_delete_transients( $prefix ) {
    wds_delete_transients_from_keys( wds_search_database_for_transients_by_prefix( $prefix ) );
}

/**
 * Searches the database for transients stored there that match a specific prefix.
 *
 * @param  string $prefix Prefix to search for.
 * @return array|bool     Nested array response for wpdb->get_results or false on failure.
 */
function wds_search_database_for_transients_by_prefix( $prefix ) {

    global $wpdb;

    // Add our prefix after concating our prefix with the _transient prefix
    $prefix = $wpdb->esc_like( '_transient_' . $prefix . '_' );

    // Build up our SQL query
    $sql = "SELECT `option_name` FROM $wpdb->options WHERE `option_name` LIKE '%s'";

    // Execute our query
    $transients = $wpdb->get_results( $wpdb->prepare( $sql, $prefix . '%' ), ARRAY_A );

    // If if looks good, pass it back
    if ( $transients && ! is_wp_error( $transients ) ) {
        return $transients;
    }

    // Otherise return false
    return false;
}

/**
 * Expects a passed in multidimensional array of transient keys.
 *
 * array(
 *     array( 'option_name' => '_transient_blah_blah' ),
 *     array( 'option_name' => 'transient_another_one' ),
 * )
 *
 * Can also pass in an array of transient names.
 *
 * @param  array|string $transients  Nested array of transients, keyed by option_name,
 *                                   or array of names of transients.
 * @return array|bool                Count of total vs deleted or false on failure.
 */
function wds_delete_transients_from_keys( $transients ) {

    if ( ! isset( $transients ) ) {
        return false;
    }

    // If we get a string key passed in, might as well use it correctly
    if ( is_string( $transients ) ) {
        $transients = array( array( 'option_name' => $transients ) );
    }

    // If its not an array, we can't do anything
    if ( ! is_array( $transients ) ) {
        return false;
    }

    $results = array();

    // Loop through our transients
    foreach ( $transients as $transient ) {

        if ( is_array( $transient ) ) {

            // If we have an array, grab the first element
            $transient = current( $transient );
        }

        // Remove that sucker
        $results[ $transient ] = delete_transient( str_replace( '_transient_', '', $transient ) );
    }

    // Return an array of total number, and number deleted
    return array(
        'total'   => count( $results ),
        'deleted' => array_sum( $results ),
    );
}

So putting those to use to delete our transients for this example would look something like this:

wds_delete_transients( 'wds_post_count' )

Quite a bit shorter and easier, eh?

Two words of caution:

  1. Since these functions search for and delete all transients that begin with the prefix you provide, make sure that you don’t have any other transient keys that begin with the same thing. For instance, if I were to save data with transient keys like wds_post_count_* and wds_post_count_featured_*, those would both be deleted if I were to call wds_delete_transients( 'wds_post_count' );
  2. Because the wds_search_database_for_transients_by_prefix() function searches the database for transients, it can’t be used on sites that have a persistent object cache in place. If a persistent object cache is being used, transients are stored in memory rather than in the database. So on those sites, you should delete transients using the full keys instead, such as delete_transient( 'transient_key_param1_param2' ).

Have any more?

I hope incorporating the techniques outlined in this post is as helpful for you as it has been for me when working with transients. Do you have some others? Let us know in the comments.

The post Working with Transients like a Boss appeared first on WebDevStudios.

]]>
https://webdevstudios.com/2016/07/19/working-transients-like-boss/feed/ 3 13438