Simple Database Seeder for WordPress

I've been doing some WordPress development at my day job and have come to the conclusion that the development life-cycle of WordPress is kinda bad, actually. However, there are systems & processes we can implement to improve the developer and development experience. We _can_ make it more enjoyable to work on WordPress!

One of those systems that I sorely miss is a database seeder that can populate your site with test and dummy content. The go-to for WordPress seems to be to download a database from a live or test site. I am not a fan of this pattern at all and would rather not touch production data. If the database is large this can be a very time-consuming process. If you need media and attachments this can become a huge burden on your workstation as you download gigabytes of assets.

The default method for seeding data on WordPress is to first set up a WordPress site somewhere, then create a bunch of test content, then export the content into an XML file. You can import that XML file into your local install of WordPress. However, a downside of this is that if there are attachments, they need to be publicly available either at the test site or somehow locally available. You need to have already created the content. Making updates is also tedious. I think the workflow is poor and we can do better.

What I would prefer is to be more intentional about creating that content.

My end goal is to be able to run a command to seed content from classes I create that very intentionally define the type of content I want available. Also, it would be nice to be able to reset the data and reseed. The command interface I want:

$ wp-cli dbseeder seed
$ wp-cli dbseeder truncate
$ wp-cli dbseeder replant

I leaned into wp-cli to help me here. I created a custom wp-cli command to wrap the business logic of seeding, truncating, and replanting the content.

<?php

namespace RoycomCommands;

use Exception;
use HaydenPierce\ClassFinder\ClassFinder;
use WP_CLI;
use WP_CLI_Command;
use RoycomTheme\DbTruncate;

if (class_exists('WP_CLI_Command')) {
    /**
     * Class DbSeederCommand
     */
    class DbSeederCommand extends WP_CLI_Command
    {
        /**
         * @return void
         * @throws Exception
         */
        public function seed(): void
        {
            $classes = $this->getSeedClasses();
            foreach ($classes as $class) {
                WP_CLI::log(sprintf("Seeding %s...", $class));

                $seed = new $class;
                $result = $seed->run();

                if ($result["success"] === false) {
                    WP_CLI::error($result["message"]);
                } else {
                    WP_CLI::success($result["message"]);
                }
            }

            WP_CLI::success("Command executed");
        }

        /**
         * @return void
         */
        public function truncate(): void
        {
            WP_CLI::warning("Truncating database...");
            $DbTruncate = new DbTruncate();
            $DbTruncate->run();
        }

        /**
         * @return void
         * @throws Exception
         */
        public function replant(): void
        {
            $this->truncate();
            $this->seed();
        }

        /**
         * @return array
         * @throws Exception
         */
        private function getSeedClasses(): array
        {
            $class_finder = new ClassFinder();
            $class_finder::setAppRoot(ABSPATH);
            return $class_finder::getClassesInNamespace('RoycomTheme\\DbSeeds', ClassFinder::RECURSIVE_MODE);
        }
    }
}

I registered the command on my WordPress site.

<?php

use WP_CLI;

# ... the rest of functions.php...

WP_CLI::add_command('dbseeder', 'RoycomCommands\\DbSeederCommand');

I created my first seed. The class `AbstractSeed` is pretty bare bones here, it just defines an interface method called `run1 that we have to implement in our seed class.

<?php

namespace RoycomTheme\DbSeeds;

use RoycomTheme\AbstractDbSeed;

class ExampleSeed extends AbstractDbSeed
{
    public function run(): array
    {
        $post_id = wp_insert_post([
            'post_title' => 'Example Post',
            'post_content' => 'This is an example post.',
            'post_status' => 'publish',
            'post_author' => 1,
            'post_type' => 'post',
        ]);

        return [
            "success" => true,
            "message" => sprintf('Created Post with ID #%d', $post_id),
            "post_id" => $post_id
        ];
    }
}

My composer.json file defines where to find the seeds, and where to find commands. This is a very trimmed down version of composer.json.

{
  "require": {
    "haydenpierce/class-finder": "^0.5.3"
  },
  "autoload": {
    "psr-4": {
      "Roycom\\": "src/",
      "RoycomCommands\\": "wp-cli/commands/",
      "RoycomTheme\\": "wp-content/themes/roylart/includes/classes"
    }
  }
}

The gist of what is happening is I discover seed files in the directory `DbSeeds` in my theme directory using `ClassFinder` and then for each of them I execute a `run` method. The `run` method simply runs some WordPress functions to create posts, create taxonomies, add placeholder images. Really, it can do whatever I need it to do.

For example, I created a `PagesSeed` class that creates some generic pages, makes one of them the front page and one the blog page, and then also create a menu.

Another seed I created is called `ArtworksSeed` for a custom post type called `artworks` that will create example posts, download images from loremflickr, and also add data to custom MetaBox fields.

 

 

Did you like this post? Let me know by sending me a message. Is there a topic you would like me to cover? Let me know about that too. I look forward to hearing from you!

Let's Connect!