Category: Code Snippets

  • Using Parse and Serialize Blocks

    Here’s a script we used to update all of the first heading tags to h1’s if they weren’t already, but I think you can see the potential of this technique:

    <?php
    
    /*
    This site has post content (just pages for now) that all start with h2 tags. This script updates all those h2 tags to h1 tags for SEO purposes.
    
    The first thing we need to do is loop through all posts and grab the post content.
    Then we need to search for the first instance of an h2 tag and replace it with an h1 tag.
    Finally, we need to update the post content with the new content.
    
    An example of one of the headings looks like this:
    <!-- wp:heading {"className":"hero-heading","style":{"spacing":{"margin":{"top":"0","bottom":"0"}},"elements":{"link":{"color":{"text":"var:preset|color|secondary"}}}},"textColor":"secondary"} -->
    <h2 class="wp-block-heading hero-heading has-secondary-color has-text-color has-link-color" style="margin-top:0;margin-bottom:0">Explore the Wonders of Germany Tour</h2>
    <!-- /wp:heading -->
    
    Since it's gutenberg content, we need to make sure we only replace the h2 tag within the wp:heading block, and specify the level of the heading tag to be 1 instead of 2 (or not specified).
    */
    
    require_once __DIR__ . '/wp-load.php';
    
    // Get all posts
    $all_posts = get_posts(array(
        'numberposts' => -1, // Get all posts
        'post_type'   => 'page', // Change this if you want to target a
        'post_status' => 'any', // Include all post statuses
    ));
    
    foreach ($all_posts as $post) {
        // Get the post content
        $content = $post->post_content;
    
        // Here we use parse_blocks to parse the content into blocks and manage them properly
        $blocks = parse_blocks($content);
        $updated = false;
        foreach ($blocks as &$block) {
            if ($block['blockName'] === 'core/heading') {
                // Check if the heading level is 2
                if (isset($block['attrs']['level']) && $block['attrs']['level'] == 2) {
                    // Change it to level 1
                    $block['attrs']['level'] = 1;
                    $block['innerHTML'] = str_replace('h2', 'h1', $block['innerHTML']);
                    $block['innerContent'] = str_replace('h2', 'h1', $block['innerContent']);
                    $updated = true;
                } elseif (!isset($block['attrs']['level'])) {
                    // If level is not set, it defaults to 2, so we change it to 1
                    $block['attrs']['level'] = 1;
                    $block['innerHTML'] = str_replace('h2', 'h1', $block['innerHTML']);
                    $block['innerContent'] = str_replace('h2', 'h1', $block['innerContent']);
                    $updated = true;
                }
            }
    
            if ($updated) {
                // We only want to change the first heading we find
                break;
            }
        }
    
        if ($updated) {
            // Convert blocks back to content
            $new_content = serialize_blocks($blocks);
    
            // Update the post content
            wp_update_post(array(
                'ID'           => $post->ID,
                'post_content' => $new_content,
            ));
    
            echo "Updated post ID " . $post->ID . "\n";
        } else {
            echo "No changes for post ID " . $post->ID . "\n";
        }
    }
  • A Better Underline Transition

    Saw this on an e-com form that worked REALLY well. It even handled the line break 👍

    span {
    	background-repeat: no-repeat;
    	background-size: 0 2px;
    	background-image: linear-gradient(to right,#000 0,#000 100%);
    	-webkit-transition: background-size .55s cubic-bezier(.2,.75,.5,1);
    	transition: background-size .55s cubic-bezier(.2,.75,.5,1);
    	background-position: left bottom;
    }
    
    span:hover {
    	background-size: 100% 2px;
    }

    (must target inline element)

  • Loop Through All Posts and Update

    Here’s a quick script that we used to loop through all posts and update them one by one with dynamic data (well… kinda dynamic)…

    <?php
    
    require_once __DIR__ . '/wp-load.php';
    
    // Get the global $wpdb object
    global $wpdb;
    
    // Query all posts
    $posts = $wpdb->get_results("SELECT ID, post_date FROM {$wpdb->posts}");
    
    // Loop through each post and update the post_modified column
    foreach ($posts as $post) {
        $wpdb->update(
            $wpdb->posts,
            ['post_modified' => $post->post_date, 'post_modified_gmt' => $post->post_date],
            ['ID' => $post->ID]
        );
    }
    
    // Output a success message
    echo "All posts have been updated successfully.\n";
    
  • Quick Script to Count Block Usage

    Here’s a quick script to determine what blocks a site is using and how often they use them:

    <?php
    
    require_once __DIR__ . '/wp-load.php';
    
    $sql = 'select * from wp_posts where post_content like "%<!-- wp:%"';
    
    $results = $wpdb->get_results($sql);
    $blocks = [];
    
    // Loop through each post and get a list of all blocks used in gutenberg post_content
    foreach ($results as $post) {
        $matches = [];
        preg_match_all('/<!-- wp:(.*?) -->/', $post->post_content, $matches);
        if ( !empty($matches) ) {
            $block_names = $matches[1];
    
            foreach ( $block_names as $block_name ) {
                $name = explode(' ', $block_name);
                $name = $name[0] ?? $block_name;
    
                if ( !isset($blocks[$name]) ) {
                    $blocks[$name] = 0;
                }
                ++$blocks[$name];
            }
        }
    }
    
    print_r($blocks);
    
  • Table Size in SQL

    Here’s a quick way to determine which table has the most data in it:

    SELECT 
        table_name AS `Table`, 
        round(((data_length + index_length) / 1024 / 1024), 2) `Size in MB` 
    FROM information_schema.TABLES 
    WHERE table_schema = "INSERT-DB-NAME-HERE";

    Replace INSERT-DB-NAME-HERE with the database name of your choosing, but other than that, it should display all tables in a database and list their relative sizes.

    You can also sort by Size in MB to narrow your search quicker. This will list them alphabetically.

  • Custom Post Types

    Here’s an example of a custom post type for an “Example” CPT:

    <?php
    
    class CPT_Example
    {
        /**
         * CPT_NAME is the custom post type name. Also used to generate the CPT Label using `ucfirst()`.
         * 
         * @var string
         */
        const CPT_NAME = 'example';
    
        const TAX_NAME_TYPE = 'example-type';
    
        const NONCE_NAME = 'example_meta_box';
    
        public $fields = [
            'example_name',
            'example_company',
        ];
    
        /**
         * Set all the required hooks to register the Staff custom post type and all other required components/features
         * 
         * @return void
         */
        public function init()
        {
            // All the init operations we need to do in one call
            add_action('init', [$this, 'register']);
    
            // Remove the "Archive: " prefix (eg: "Archive: Staff Directory")
            add_filter('get_the_archive_title_prefix', [$this, 'remove_archive_prefix'], 10, 1);
    
            // Add a custom metabox for the example post type
            add_action('add_meta_boxes', [$this, 'add_meta_boxes']);
        }
    
        /**
         * Method to choreograph the registration of the custom post type and all of its required components
         * 
         * @return void
         */
        public function register()
        {
            $this->register_taxonomies();
            $this->register_post_type();
    
            add_action('save_post_' . self::CPT_NAME, [$this, 'save_example_meta']);
        }
    
        /**
         * Registers the Staff custom post type
         * 
         * @return void
         */
        public function register_post_type()
        {
            $uc_label = ucwords(str_replace('-', ' ', self::CPT_NAME));
            $uc_plural = $uc_label . 's';
    
            register_post_type(
                self::CPT_NAME,
                [
                    'labels' => [
                        'name'                => $uc_plural,
                        'singular_name'       => $uc_label,
                        'menu_name'           => $uc_label,
                        'parent_item_colon'   => 'Parent ' . $uc_label,
                        'all_items'           => 'All ' . $uc_plural,
                        'view_item'           => 'View ' . $uc_label,
                        'add_new_item'        => 'Add New ' . $uc_label,
                        'add_new'             => 'Add New',
                        'edit_item'           => 'Edit ' . $uc_label,
                        'update_item'         => 'Update ' . $uc_label,
                        'search_items'        => 'Search ' . $uc_plural,
                        'not_found'           => 'Not Found',
                        'not_found_in_trash'  => 'Not found in Trash',
                    ],
                    'taxonomies'    => [ self::TAX_NAME_TYPE, /* Add more taxonomies here */ ],
                    'show_ui'       => true,
                    'public'        => true,
                    'has_archive'   => true,
                    'rewrite'       => [
                        'slug'          => self::CPT_NAME . 's',
                        'with_front'    => false, // removes the /blog/ prefix in the url
                        'feeds'         => false,
                        'pages'         => true,
                        'ep_mask'       => EP_PAGES
                    ],
                    'menu_icon'     => 'dashicons-businessman',
                    'supports'      => ['title', 'editor', 'thumbnail'],
                    'show_in_rest'  => true,
                    'rest_base'     => self::CPT_NAME
                ]
            );
        }
    
        public function register_taxonomies()
        {
            register_taxonomy(
                self::TAX_NAME_TYPE,
                self::CPT_NAME,
                [
                    'labels' => [
                        'name'              => 'Example Types',
                        'singular_name'     => 'Example Type',
                        'search_items'      => 'Search Example Types',
                        'all_items'         => 'All Example Types',
                        'parent_item'       => 'Parent Example Type',
                        'parent_item_colon' => 'Parent Example Type:',
                        'edit_item'         => 'Edit Example Type',
                        'update_item'       => 'Update Example Type',
                        'add_new_item'      => 'Add New Example Type',
                        'new_item_name'     => 'New Example Type Name',
                        'menu_name'         => 'Example Types',
                    ],
                    'show_ui'           => true,
                    'show_admin_column' => true,
                    'hierarchical'      => true,
                    'show_in_rest'      => true,
                    'rest_base'         => self::TAX_NAME_TYPE . 's'
                ]
            );
        }
    
        /**
         * Remove the "Archives:" prefix from the archive title for the post type
         * 
         * @param string $prefix    The current prefix as given by the filter
         * @return string           The modified prefix
         */ 
        public function remove_archive_prefix($prefix)
        {
            return is_post_type_archive(self::CPT_NAME)
                || is_tax(self::TAX_NAME_TYPE)
                    ? ''
                    : $prefix;
        }
    
        public function add_meta_boxes()
        {
            add_meta_box(
                'example_posting_meta_box',
                'Example Details',
                [$this, 'render_example_meta_box'],
                self::CPT_NAME,
                'normal',
                'high'
            );
        }
    
        public function render_example_meta_box($post)
        {
            // Get existing meta values ('post' here means the object type comes from the posts table)
            $meta = get_metadata( 'post', $post->ID, '', true );
            foreach ( $this->fields as $field ) {
                $$field = $meta[$field][0] ?? '';
            }
    
            // Add the hidden nonce field
            wp_nonce_field( self::NONCE_NAME, self::NONCE_NAME . '_nonce');
    
            include_once EXAMPLE_TEMPLATEDIR . 'metabox.php';
        }
    
        public function save_example_meta($post_id) 
        {
            // Add nonce check, autosave check, and capability check here
            $nonce = $_POST[self::NONCE_NAME . '_nonce'] ?? false;
            if ( !$nonce || !wp_verify_nonce($nonce, self::NONCE_NAME) ) {
                return $post_id;
            }
    
            foreach ( $this->fields as $field ) {
                if (isset($_POST[$field])) {
                    update_post_meta($post_id, $field, $_POST[$field]);
                }
            }
        }
    }