Chapter 2. WordPress Basics

WordPress was first developed in 2003 and was created primarily as blogging software. By the release of version 3.5, the image of WordPress had changed from blogging software to a versatile CMS (content management system) and the word “blog” was actually removed from the description of the software and in most places in the source code. Today, WordPress has evolved to become the largest platform on the web and is used on about 30% of all the websites on the Internet. This is pretty amazing if you think about it. Over a half a billion websites on the internet run on top of WordPress.

There are a couple of reasons WordPress has gained so much popularity over the years. The first is that WordPress is open source software and has an entire community of people who are invested in improving it and continually contributing new code to extend its functionality. WordPress users, developers, and designers are always thinking of new creative ways to use WordPress and creating plugins for these new features, which can be made available to the community.

Another reason WordPress has been so successful is the fact that it’s an extremely flexible CMS laced with hooks and filters so plugin and theme developers can have almost total control to build all different kinds of websites. Developers are constantly exploring innovative new ways to use the software, including building web and mobile applications, which is the focus of this book. The use of hooks and filters is covered later in this chapter.

Note

We are going to assume that you already know how to use WordPress, and have already installed the latest version somewhere. If this is your first time using WordPress, you might want to check out https://wordpress.org to familiarize yourself with WordPress a little bit more.

WordPress Directory Structure

Let’s take a quick top-level look at the folders and files that are included within a typical WordPress install.

Root Directory

In the root directory, there are a few core WordPress files. Unless you are digging around in the core WordPress code looking for hooks to use or trying to figure out how certain functionality is coded, the only core WordPress file you may need to ever alter is wp-config.php. You should never, ever, ever, ever1 alter any other core WordPress files. Hacking core is a bad idea because you won’t be able to upgrade WordPress when a new version becomes available without overriding your changes. The only directory you should need to interact with is wp-content because it contains your plugins, themes, and uploaded files.

Any time you find yourself wanting to hack a core WordPress file, think again. There is probably a hook or filter you could use to accomplish the same goal. If there isn’t a hook or filter available to do what you need, add one and try to get it added to core. The core WordPress developers are very responsive about adding in new hooks and filters.

There is one more file you may need to update in the WordPress root directory depending on your setup and how you are using WordPress, the .htaccess file. It’s not a WordPress core file but an Apache file WordPress uses to handle directory configuration, permalinks and redirects. This file is not there by default but gets created by WordPress automatically the first time you define your permalink structure. Check out all of the .htaccess configuration options at your leisure here: https://codex.wordpress.org/htaccess

/wp-admin

This directory contains core directories and files for managing the WordPress admin dashboard interface. A key file in this directory is admin-ajax.php, which all AJAX requests should be run through. AJAX is covered in Chapter 9.

/wp-includes

This directory contains core directories and files for various WordPress functionality. It is highly encouraged that you look over the structure and code in this directory if you have some extra time on your hands to better understand the inner workings of WordPress.

/wp-content

This directory is where WordPress users and developers can make WordPress do whatever they want. It contains subdirectories for the plugins and themes you have installed on your website as well as any media files you upload to your website.

The following directories are subdirectories of the wp-content directory.

/wp-content/plugins

Any WordPress plugin you install on your WordPress site will be located in this directory. By default, WordPress comes with the Hello Dolly and Akismet plugins.

Hello Dolly is included as a quick example of how a basic WordPress plugin is set up. The plugin itself just displays a random line from the song Hello Dolly in the upper right of the admin dashboard.

Akismet is a plugin that helps to stop spam comments by checking incomming comments against the database at Akismet.com. This plugin and service greatly reduces the number of spam comments that make it onto the frontend of your website. The Akismet service is free (or name your own price) for personal use.

/wp-content/themes

Any WordPress themes you install on your WordPress site will be located in this directory. By default, WordPress comes with a few standard themes all named after the year they were released (e.g. Twenty Sixteen, Twenty Seventeen, etc).

/wp-content/uploads

Once you start uploading any photos or files to your media library, you will start seeing this directory being populated with those uploaded files. All uploaded media is stored in the uploads directory. Some plugins will also create a subdirectory in the uploads directory to various files used or managed by the plugin.

/wp-content/mu-plugins

In WordPress, you can force the use of any plugin by creating a mu-plugins directory inside of the wp-content directory. This directory does not exist unless you create it. The “mu” stands for “must use,” and any plugin you put in the mu-plugins folder will automatically run without needing to be manually activated on the admin plugins page. In fact, you won’t even see any must use plugins listed there.

Must use plugins are especially useful on multisite installs of WordPress so you can use plugins that your individual network site admins won’t be able to deactivate.

It is a good idea to check for the mu-plugins folder on any existing site you start working on. See what if there are any plugins in there and what they do. So many times we have been debugging an issue and wondering why something unexpected was happening even though we had disabled all of the active plugins, only to find out that there was an overlooked mu-plugin responsible for the issue.

WordPress Database Structure

WordPress runs on top of a MySQL database and creates its own tables to store data and content. Below is the database schema created by a default install of WordPress. We have also included some basic information on built-in WordPress functions for interacting with these tables. If you can grasp the database (DB) schema and get comfortable with the list functions in this chapter, you can push and pull any data into and out of WordPress.

Note

The following table names use the default prefix of wp_. This prefix can be changed during the WordPress installation, and so the exact table names of your WordPress install may vary.

wp_options

The wp_options table stores any sitewide data for you. This table stores the name, description, and admin email that you entered when running a typical install. This table will also come prepopulated with a few records that store the various default settings within WordPress. Table 2-1 shows the database structure for the wp_options table.

Table 2-1. DB schema for wp_options table
Column Type Collation Null Default Extra

option_id

bigint(20)

No

None

AUTO_INCREMENT

option_name

varchar(64)

utf8_general_ci

No

option_value

longtext

utf8_general_ci

No

None

autoload

varchar(20)

utf8_general_ci

No

Yes

WordPress apps and plugins will typically store their settings in the wp_options table using the functions defined below. The settings can be stored in separate rows, while using a common prefix for the option names. In most cases, it is more performant to store all of the options in one array and save them into just one row in the wp_options table.

Functions Found in /wp-includes/option.php

The following functions can all be found in /wp-includes/option.php:

add_option( string $option, mixed $value = ', string $deprecated = ', string|bool $autoload = yes )

First checks if an option_name exists before inserting a new row:

  • $option—A required string of the option_name you would like to add.

  • $value—An optional mixed variable of the option_value you would like to add. If the variable passed is an array or object, the value will be serialized before storing in the database.

  • $deprecated—This parameter was deprecated in version 2.3 and is not used anymore.2

  • $autoload—An optional Boolean used to distinguish whether to load the option into cache when WordPress starts up. Set to yes or no. The default value is yes. If you are sure you are going to need this option on every page load, you can leave the value as the default yes. If you are only going to need to lookup the option on specific pages, then it’s usually better to set autoload to no.

update_option( $option, $newvalue )

Updates an existing option but will also add it if it doesn’t already exist:

  • $option—A required string of the option_name you would like to update/add.

  • $newvalue—An optional mixed variable of the option_value you would like to update/add.

get_option( $option, $default = false )

Retrieves the option_value for a provided option_name:

  • $option—A required string of the option_name you would like to get.

  • $default—An optional mixed variable you would like to return if the option_name you provided doesn’t exist in the table. By default, this parameter is false.

delete_option( $option )

Deletes an existing option from the database permanently:

  • $option—A required string of the option_name you would like to delete.

Note

Most of the code examples in this book are not fully functional code. They are basic theoretical examples of how to use the functions we are talking about. You can follow along with most of the code examples if you like in a custom plugin or your theme’s functions.php file.

Example 2-1 demonstrates some of the basic functions for interacting with the wp_options table.

Example 2-1. Adding, updating, getting, and deleting records in the wp_options table
<?php
// add option
$twitters = array( '@bwawwp', '@bmess', '@jason_coleman' );
add_option( 'bwawwp_twitter_accounts', $twitters );

// get option
$bwawwp_twitter_accounts = get_option( 'bwawwp_twitter_accounts' );
echo '<pre>';
print_r( $bwawwp_twitter_accounts );
echo '</pre>';

// update option
$twitters = array_merge(
         $twitters,
         array(
                 '@alphaweb',
                 '@pmproplugin'
         )
);
update_option( 'bwawwp_twitter_accounts', $twitters );

// get option
$bwawwp_twitter_accounts = get_option( 'bwawwp_twitter_accounts' );
echo '<pre>';
print_r( $bwawwp_twitter_accounts );
echo '</pre>';

// delete option
delete_option( 'bwawwp_twitter_accounts' );


/*
The output from the above example should look something like this:
Array
(
    [0] => @bwawwp
    [1] => @bmess
    [2] => @jason_coleman
)
Array
(
    [0] => @bwawwp
    [1] => @bmess
    [2] => @jason_coleman
    [3] => @alphaweb
    [4] => @pmproplugin
)
*/
?>

wp_users

When you log into WordPress with your username and password, you are referencing data stored in this table. All users and their default data are stored in the wp_users table. Table 2-2 shows the database structure for the wp_users table.

Table 2-2. DB schema for wp_users table
Column Type Collation Null Default Extra

ID

bigint(20)

No

None

AUTO_INCREMENT

user_login

varchar(60)

utf8_general_ci

No

user_pass

varchar(64)

utf8_general_ci

No

user_nicename

varchar(50)

utf8_general_ci

No

user_email

varchar(100)

utf8_general_ci

No

user_url

varchar(100)

utf8_general_ci

No

user_registered

datetime

No

0000-00-00 00:00:00

user_activation_key

varchar(60)

utf8_general_ci

No

user_status

int(11)

No

0

display_name

varchar(250)

utf8_general_ci

No

For many WordPress apps, you will handle the creation and management of users through the admin dashboard GUI. However, if you need to create users in your code or update metadata about them, the functions defined below will be useful.

Functions Found in /wp-includes/…

The following functions are found in /wp-includes/pluggable.php and /wp-includes/user.php:

wp_insert_user( $userdata )

Inserts a new user into the database. This function can also be used to update a user if the user ID is passed in with the $user_data. $userdata is a required array of field names and values. The accepted fields are:

  • ID—An integer that will be used for updating an existing user.

  • user_pass—A string that contains the plain-text password for the user.

  • user_login—A string that contains the user’s username for logging in.

  • user_nicename—A string that contains a URL-friendly name for the user. The default is the user’s username.

  • user_url—A string containing the URL for the user’s website.

  • user_email—A string containing the user’s email address.

  • display_name—A string that will be shown on the site. Defaults to the user’s username. It is likely that you will want to change this, for appearance.

  • nickname—The user’s nickname. Defaults to the user’s username.

  • first_name—The user’s first name.

  • last_name—The user’s last name.

  • description—A string containing content about the user.

  • rich_editing—A string for whether to enable the rich editor. False if not empty.

  • user_registered—The date the user registered. Format is Y-m-d H:i:s.

  • role—A string used to set the user’s role.

  • jabber—User’s Jabber account.

  • aim—User’s AOL IM account.

  • yim—User’s Yahoo IM account.

wp_create_user( $username, $password, $email )

This function utilizes the prior function wp_insert_user() and makes it easier to add a new user based on the required columns:

  • $username—A required string of the username/login of a new user.

  • $password—A required string of the password of a new user.

  • $email—A required string of the email address of a new user.

wp_update_user( $userdata )

This function can be used to update any of the fields in the wp_users and wp_usermeta (covered next) tables tied to a specific user. Note that if a user’s password is updated, all of his cookies will the cleared, logging him out of WordPress:

  • $userdata—A required array of field names and values. The ID and at least one other field is required. These fields are the same ones accepted in the wp_insert_post() function.

get_user_by( $field, $value )

This function returns the WP_User object on success and false if it fails. The WordPress User class is found in /wp-includes/capabilities.php and basically queries the wp_user table like so:

SELECT * FROM wp_users WHERE $field = $value;

The WP_User class caches the results so it is not querying the database every time it is used. The class also figures out the roles and capabilities of a specific user, which we will go over in more detail in Chapter 6:

  • $field—A required string of the field you would like to query the user data by. This string can only be id, slug, email, or login.

  • $value—A required integer or string of the value for a given id, slug, email or login.

get_userdata( $userid )

This function actually utilizes the previous function get_user_by() and returns the same WP_User object:

  • $userid—A required integer of the user ID of the user you would like to get data for.

wp_delete_user( $id, $reassign = novalue )

You guessed it: this function deletes a user and can also reassign any of their posts or links to another user:

  • $id—A required integer of the ID of the user you would like to delete.

  • $reassign—An optional integer of the ID you would like to reassign any post or links from the deleted user to. Example 2-2 demonstrates some of the basic functions for interacting with the wp_users table.

Example 2-2. Working with the wp_users table
<?php
// insert user
$userdata = array(
    'user_login'    => 'brian',
	'user_pass'     => 'KO03gT7@n*',
	'user_nicename' => 'Brian',
	'user_url'      => 'https://alphaweb.com/',
	'user_email'    => 'brian@alphaweb.com',
	'display_name'  => 'Brian',
	'nickname'      => 'Brian',
	'first_name'    => 'Brian',
	'last_name'     => 'Messenlehner',
	'description'   => 'This is a WordPress Administrator account.',
	'role'          => 'administrator'
);
wp_insert_user( $userdata );

// create users
wp_create_user( 'jason', 'YR529G%*v@', 'jason@schoolpress.me' );

// get user by login
$user = get_user_by( 'login', 'brian' );
echo 'email: ' . $user->user_email  . ' / ID: ' . $user->ID . '<br>';
echo 'Hi: ' . $user->first_name . ' ' . $user->last_name . '<br>';

// get user by email
$user = get_user_by( 'email', 'jason@schoolpress.me' );
echo 'username: ' . $user->user_login . ' / ID: ' . $user->ID . '<br>';

// update user - change user name fields and change role to admin
$userdata = array(
	'ID'         => $user->ID,
	'first_name' => 'Jason',
	'last_name'  => 'Coleman',
	'user_url'   => 'http://strangerstudios.com/',
	'role'       => 'administrator'
);
wp_update_user( $userdata );

// get userdata for brian
$user = get_userdata( $user->ID );
echo 'Hi: ' . $user->first_name . ' ' . $user->last_name . '<br>';

// delete user - delete the original admin and set their posts to our new admin
// wp_delete_user( 1, $user->ID );

/*
The output from the above example should look something like this:
email: brian@schoolpress.me / ID: 2
Hi: Brian Messenlehner
username: jason / ID: 3
Hi: Jason Coleman
*/
?>
-------------------------------------------

wp_usermeta

Sometimes you may want to store additional data along with a user. WordPress provides an easy way to do this without having to add additional columns to the users table. You can store as much user metadata as you need to in the wp_usermeta table. Each record is associated to a user ID in the wp_user table by the user_id field. Table 2-3 shows the database structure for the wp_usermeta table.

Table 2-3. DB schema for wp_usermeta table
Column Type Collation Null Default Extra

umeta_id

bigint(20)

No

None

AUTO_INCREMENT

user_id

bigint(20)

No

0

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

get_user_meta( $user_id, $key = '', $single = false )

Gets a user’s meta value for a specified key:

  • $user_id—A required integer of a user ID.

  • $key—An optional string of the meta key of the value you would like to return. If blank then all metadata for the given user will be returned.

  • $single—A Boolean of whether to return a single value or not. The default is false and the value will be returned as an array.

There can be more than one meta key for the same user ID with different values. If you set $single to true, you will get the first key’s value; if you set it to false, you will get an array of the values of each record with the same key.

update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' )

This function will update user metadata but will also insert metadata if the passed-in key doesn’t already exist:

  • $user_id—A required integer of a user ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to store. If this meta key already exists, it will update the current row’s meta value, if not it will insert a new row.

  • $meta_value—A required mixed value of an integer, string, array, or object. Arrays and objects will automatically be serialized.

  • $prev_value—An optional mixed value of the current metadata value. If a match is found, it will replace the previous/current value with the new value you specified. If left blank, the new meta value will replace the first instance of the matching key. If you have five rows of metadata with the same key and you don’t specify which row to update with this value, it will update the first row and remove the other four.

Note

This function relies on the update_metadata() function located in /wp-includes/meta.php. Check it out!

add_user_meta($user_id, $meta_key, $meta_value, $unique = false)

Yup, this function will insert brand-new user meta into the wp_usermeta table. We don’t use this function often anymore because we can just use update_user_meta() to insert new rows as well as update them. If you want to ensure that a given meta key is only ever used once per user, you should use this function and set the $unique parameter to true:

  • $user_id—A required integer of a user ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to store.

  • $meta_value—A required mixed value of an integer, string, array, or object.

  • $unique—An optional Boolean, which when set to true will make sure the meta key can only ever be added once for a given ID.

delete_user_meta($user_id, $meta_key, $meta_value = '')

Deletes user metadata for a provided user ID and matching key. You can also specify a matching meta value if you only want to delete that value and not other metadata rows with the same meta key:

  • $user_id—A required integer of a user ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to delete.

  • $meta_value—An optional mixed value of the meta value. If you have more than one record with the same meta key, you can specify which one to delete by matching the meta value. It defaults to nothing, which will delete all meta rows with a matching user_id and meta_key.

Example 2-3 demonstrates some of the basic functions for interacting with the wp_username table.

Example 2-3. Working with the wp_username table
<?php
// get brian's id
$brian_id = get_user_by( 'login', 'brian' )->ID;

// add user meta - unique is set to true.
add_user_meta( $brian_id, 'bwawwp_wife', 'Married to the game', true);

// get user meta - returning a single value
$brians_wife = get_user_meta( $brian_id, 'bwawwp_wife', true);
echo "Brian's wife: " . $brians_wife . "<br>";

// add user meta - 3rd parameter is a unique value
add_user_meta( $brian_id, 'bwawwp_kid', 'Dalya' );
add_user_meta( $brian_id, 'bwawwp_kid', 'Brian' );
add_user_meta( $brian_id, 'bwawwp_kid', 'Nina' );
add_user_meta( $brian_id, 'bwawwp_kid', 'Cam' );

// update user meta - this will update brian to brian jr.
update_user_meta( $brian_id, 'bwawwp_kid', 'Brian Jr', 'Brian' );

// get user meta - returning an array
$brians_kids = get_user_meta( $brian_id, 'bwawwp_kid' );
echo "Brian's kids:";
echo '<pre>';
print_r($brians_kids);
echo '</pre>';

// delete brian's user meta
delete_user_meta( $brian_id, 'bwawwp_wife' );
delete_user_meta( $brian_id, 'bwawwp_kid' );

// get jason's id
$jason_id = get_user_by( 'login', 'jason' )->ID;

// update user meta - this will create meta if the key doesn't exist for the user.
update_user_meta( $jason_id, 'bwawwp_wife', 'Kimberly Ann Coleman' );

// get user meta - returning an array
$jasons_wife = get_user_meta( $jason_id, 'bwawwp_wife' );
echo "Jason's wife:";
echo '<pre>';
print_r($jasons_wife);
echo '</pre>';

// add user meta - storing as an array
add_user_meta( $jason_id, 'bwawwp_kid', array( 'Isaac', 'Marin' ) );

// get user meta - returning a single value which happens to be an array.
$jasons_kids = get_user_meta( $jason_id, 'bwawwp_kid', true );
echo "Jason's kids:";
echo '<pre>';
print_r($jasons_kids);
echo '</pre>';

// delete jason's user meta
delete_user_meta( $jason_id, 'bwawwp_wife' );
delete_user_meta( $jason_id, 'bwawwp_kid' );

/*
The output from the above example should look something like this:
Brian's wife: Married to the game
Brian's kids:
Array
(
    [0] => Dalya
    [1] => Brian Jr
    [2] => Nina
    [3] => Cam
)
Jason's wife:
Array
(
    [0] => Kimberly Ann Coleman
)
Jason's kids:
Array
(
    [0] => Isaac
    [1] => Marin
)
*/
?>
-------------------------------------------

wp_posts

Ah, the meat of WordPress. The wp_posts table is where most of your post data is stored. By default, WordPress comes with posts and pages. Both of these are technically posts and are stored in this table. The post_type field is what distinguishes what type of post a post is, whether it is a post, a page, a menu item, a revision, or any custom post type that you may later create (custom post types are covered more in Chapter 5). Table 2-4 shows the database structure for the wp_posts table.

Table 2-4. DB schema for wp_posts table
Column Type Collation Null Default Extra

ID

bigint(20)

No

None

AUTO_INCREMENT

post_author

bigint(20)

No

0

post_date

datetime

No

0000-00-00 00:00:00

post_date_gmt

datetime

No

0000-00-00 00:00:00

post_content

longtext

utf8_general_ci

No

None

post_title

text

utf8_general_ci

No

None

post_excerpt

text

utf8_general_ci

No

None

post_status

varchar(20)

utf8_general_ci

No

Publish

comment_status

varchar(20)

utf8_general_ci

No

Open

ping_status

varchar(20)

utf8_general_ci

No

Open

post_password

varchar(20)

utf8_general_ci

No

post_name

varchar(200)

utf8_general_ci

No

to_ping

text

utf8_general_ci

No

None

pinged

text

utf8_general_ci

No

None

post_modified

datetime

No

0000-00-00 00:00:00

post_modified_gmt

datetime

No

0000-00-00 00:00:00

post_content_filtered

longtext

utf8_general_ci

No

None

post_parent

bigint(20)

No

0

guid

varchar(255)

utf8_general_ci

No

menu_order

int(11)

No

0

post_type

varchar(20)

utf8_general_ci

No

Post

post_mime_type

varchar(100)

utf8_general_ci

No

comment_count

bigint(20)

No

0

Functions found in /wp-includes/post.php

The following functions are found in /wp-includes/post.php.

wp_insert_post($postarr, $wp_error = false)

Inserts a new post with provided post data:

  • $postarr—An array or object of post data. Arrays are expected to be escaped; objects are not.

  • $wp_error—An optional Boolean that will allow for a WP_Error if returned false.

The defaults for the parameter $postarr are:

  • post_status—Default is draft.

  • post_type—Default is post.

  • post_author—Default is current user ID ($user_ID). The ID of the user who added the post.

  • ping_status—Default is the value in the default_ping_status option. Whether the attachment can accept pings.

  • post_parent—Default is 0. Set this for the post it belongs to, if any.

  • menu_order—Default is 0. The order it is displayed.

  • to_ping—Whether to ping.

  • pinged—Default is empty string.

  • post_password—Default is empty string. The password to access the attachment.

  • guid—Global unique ID for referencing the attachment.

  • post_content_filtered—Post content filtered.

  • post_excerpt—Post excerpt.

wp_update_post( $postarr = array(), $wp_error = false )

Updates a post with provided post data.

  • $postarr—A required array or object of post data. Arrays are expected to be escaped, objects are not.

  • $wp_error—An optional Boolean that will allow for a WP_Error if returned false.

get_post( $post = null, $output = OBJECT, $filter = raw )

Get post data from a provided post ID or a post object:

  • $post—An optional integer or object of the post ID or post object you want to retrieve. The default is the current post you are on inside of the post loop, which is covered later in this chapter.

  • $output—An optional string of the output format. The default value is OBJECT (WP_Post object) and the other values can be ARRAY_A (associative array) or ARRAY_N (numeric array).

  • $filter—An optional string of how the context should be sanitized on output. The default value is raw, but other values can be edit, db, display, attribute, or js. Sanitization is covered in Chapter 8.

get_posts($args = null)

Returns a list of posts from matching criteria. This function uses the WP_Query class, which you will see examples of throughout the book: $args is an optional array of post arguments. The defaults are:

  • numberposts—Default is 5. Total number of posts to retrieve. –1 is all.

  • offset—Default is 0. Number of posts to pass over.

  • category—What category to pull the posts from.

  • orderby—Default is post_date. How to order the posts.

  • order—Default is DESC. The order to retrieve the posts.

  • include—A list of post IDs to include

  • exclude—A list of post IDs to exclude

  • meta_key—Any metadata key

  • meta_value—Any metadata value. Must also use meta_key.

  • post_type—Default is post. Can be page, or attachment, or the slug for any custom CPT. The string any will return posts from all post types.

  • post_parent—The parent ID of the post.

  • post_status—Default is publish. Post status to retrieve.

wp_delete_post( $postid = 0, $force_delete = false )

This function will trash any post or permanently delete it if $force_delete is set to true:

  • $postid—A required integer of the post ID you would like to trash or delete.

  • $force_delete—An optional Boolean that if set to true will delete the post; if left blank, it will default to false and will move the post to a deleted status.

Example 2-4 demonstrates some of the basic functions for interacting with the wp_posts table.

Example 2-4. Working with the wp_posts table
<?php
// insert post - set post status to draft
$args = array(
	'post_title'   => 'Building Web Apps with WordPress',
	'post_excerpt' => 'WordPress as an Application Framework',
	'post_content' => 'WordPress is the key to successful cost effective
	web solutions in most situations. Build almost anything on top of the
	WordPress platform. DO IT NOW!!!!',
	'post_status'  => 'draft',
	'post_type'    => 'post',
	'post_author'  => 1,
	'menu_order'   => 0
);
$post_id = wp_insert_post( $args );
echo 'post ID: ' . $post_id . '<br>';

// update post - change post status to publish
$args = array(
	'ID'  => $post_id,
	'post_status' => 'publish'
);
wp_update_post( $args );

// get post - return post data as an object
$post = get_post( $post_id );
echo 'Object Title: ' . $post->post_title . '<br>';

// get post - return post data as an array
$post = get_post( $post_id, ARRAY_A );
echo 'Array Title: ' . $post['post_title'] . '<br>';

// delete post - skip the trash and permanently delete it
wp_delete_post( $post_id, true );

// get posts - return 100 posts
$posts = get_posts( array( 'numberposts' => '100') );
// loop all posts and display the ID & title
foreach ( $posts as $post ) {
	echo $post->ID . ': ' .$post->post_title . '<br>';
}

/*
The output from the above example should look something like this:
post ID: 589
Object Title: Building Web Apps with WordPress
Array Title: Building Web Apps with WordPress
"A list of post IDs and Titles from your install"
*/
?>
----

wp_postmeta

Sometimes you may want to store additional data along with a post. WordPress provides an easy way to do this without having to add additional fields to the posts table. You can store as much post metadata as you need to in the wp_postmeta table. Each record is associated to a post through the post_id field. When editing any post in the backend of WordPress, you can add/update/delete metadata or Custom Fields via the UI. Table 2-5 shows the database structure for the wp_postmeta table.

Note

Metadata keys that start with an underscore are hidden from the Custom Fields UI on the edit post page. This is useful to hide certain meta fields that you don’t want end users editing directly.

Table 2-5. DB schema for wp_postmeta table
Column Type Collation Null Default Extra

meta_id

bigint(20)

No

None

AUTO_INCREMENT

post_id

bigint(20)

No

0

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

Functions Found in /wp-includes/post.php

The following functions are found in /wp-includes/post.php.

get_post_meta($post_id, $key = '', $single = false)

Get post metadata for a given post:

  • $post_id—A required integer of the post ID, for which you would like to retrieve post meta.

  • $key—Optional string of the meta key name for which you would like to retrieve post meta. The default is to return metadata for all of the meta keys for a particular post.

  • $single—A Boolean of whether to return a single value or not. The default is false, and the value will be returned as an array.

There can be more than one meta key for the same post ID with different values. If you set $single to true, you will get the first key’s value; if it is set to false, you will get an array of the values of each record with the same key.

update_post_meta($post_id, $meta_key, $meta_value, $prev_value = '')

This function will update post metadata but will also insert metadata if the passed-in key doesn’t already exist:

  • $post_id—A required integer of a post ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to store. If this meta key already exists, it will update the current row’s meta value; if not, it will insert a new row.

  • $meta_value—A required mixed value of an integer, string, array, or object. Arrays and objects will automatically be serialized.

  • $prev_value—An optional mixed value of the current metadata value. If a match is found, it will replace the previous/current value with the new value you specified. If left blank, the new meta value will replace the first instance of the matching key. If you have five rows of metadata with the same key and you don’t specify which row to update with this value, it will update the first row and remove the other four.

Note

This function relies on the update_metadata() function located in /wp-includes/meta.php. Check it out!

add_post_meta($post_id, $meta_key, $meta_value, $unique = false)

This function will insert brand-new post meta into the wp_postmeta table. We don’t use this function so often anymore because we can just use the previous function we talked about, update_post_meta(), to insert new rows as well as update them. If you want to insure that a given meta key is only ever used once per post, you should use this function and set the $unique parameter to true:

  • $user_id—A required integer of a post ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to store.

  • $meta_value—A required mixed value of an integer, string, array, or an object.

  • $unique—An optional Boolean that when set to true will make sure the meta key can only ever be added once for a given ID.

delete_post_meta($post_id, $meta_key, $meta_value = '')

Deletes post metadata for a provided post ID and matching key. You can also specify a matching meta value if you only want to delete that value and not other metadata rows with the same meta key:

  • $post_id - A required integer of a post ID.

  • $meta_key - A required string of the

  • $meta_value - An optional mixed value of the meta value. If you have more than one record with the same meta key, you can specify which one to delete by matching this value. It defaults to nothing, which will delete all meta rows with a matching post_id and meta_key.

In Example 2-5 we will get the last post and add, update, and delete various post meta.

Example 2-5. Working with post metadata
<?php
// get posts - return the latest post
$posts = get_posts( array( 'numberposts' => '1', 'orderby' =>
    'post_date', 'order' => 'DESC' ) );
foreach ( $posts as $post ) {
	$post_id = $post->ID;

	// update post meta - public metadata
	$content = 'You SHOULD see this custom field when editing your latest post.';
	update_post_meta( $post_id, 'bwawwp_displayed_field', $content );

	// update post meta - hidden metadata
	$content = str_replace( 'SHOULD', 'SHOULD NOT', $content );
	update_post_meta( $post_id, '_bwawwp_hidden_field', $content );

	// array of student logins
	$students[] = 'dalya';
	$students[] = 'ashleigh';
	$students[] = 'lola';
	$students[] = 'isaac';
	$students[] = 'marin';
	$students[] = 'brian';
	$students[] = 'nina';
 $students[] = 'cam';

	// add post meta - one key with array as value, array will be serialized
    // automatically
	add_post_meta( $post_id, 'bwawwp_students', $students, true );

	// loop students and add post meta record for each student
	foreach ( $students as $student ) {
		add_post_meta( $post_id, 'bwawwp_student', $student );
	}

	// get post meta - get all meta keys
	$all_meta = get_post_meta( $post_id );
	echo '<pre>';
	print_r( $all_meta );
	echo '</pre>';

	// get post meta - get 1st instance of key
	$student = get_post_meta( $post_id, 'bwawwp_student', true );
	echo 'oldest student: ' . $student;

	// delete post meta
	delete_post_meta( $post_id, 'bwawwp_student' );
}

/*
The output from the above example should look something like this:
Array
(
    [_bwawwp_hidden_field] => Array
        (
        [0] => You SHOULD NOT see this custom field when editing your latest post.
        )

    [bwawwp_displayed_field] => Array
        (
            [0] => You SHOULD see this custom field when editing your latest post.
        )

    [bwawwp_students] => Array
        (
        [0] => a:7:{i:0;s:5:"dalya";i:1;s:8:"ashleigh";i:2;s:4:"lola";i:3;s:5:
        "isaac";i:4;s:5:"marin";i:5;s:5:"brian";i:6;s:4:"nina";i:6;s:5:"cam";}
        )

    [bwawwp_student] => Array
        (
            [0] => dalya
            [1] => ashleigh
            [2] => lola
            [3] => isaac
            [4] => marin
            [5] => brian
            [6] => nina
	[7] => cam
        )
)
oldest student: dalya
*/
?>

wp_comments

Comments can be left on any post. The wp_comments table stores individual comments and associated comment data for any post. Table 2-6 shows the database structure for the wp_comments table.

Table 2-6. DB schema for wp_comments table
Column Type Collation Null Default Extra

comment_ID

bigint(20)

No

None

AUTO_INCREMENT

comment_post_ID

bigint(20)

No

0

comment_author

tinytext

utf8_general_ci

No

comment_author_email

varchar(100)

utf8_general_ci

No

comment_author_url

varchar(200)

utf8_general_ci

No

comment_author_IP

varchar(100)

utf8_general_ci

No

comment_date

datetime

No

0000-00-00 00:00:00

comment_date_gmt

datetime

No

0000-00-00 00:00:00

comment_content

text

utf8_general_ci

No

None

comment_karma

int(11)

No

0

comment_approved

varchar(20)

utf8_general_ci

No

1

comment_agent

varchar(20)

utf8_general_ci

No

comment_type

varchar(20)

utf8_general_ci

No

comment_parent

bigint(20)

No

0

user_id

bigint(20)

No

0

Functions Found in /wp-includes/comment.php

The following functions are found in /wp-includes/comment.php.

get_comment( $comment, $output = OBJECT )

Returns comment data from a comment ID or comment object. If the comment is empty, then the global comment variable will be used if set:

  • $comment—An optional integer, string, or object of a comment ID or object.

  • $output—An optional string that defines what format the output should be in. Possible values are OBJECT, ARRAY_A, and ARRAY_N.

get_comments( $args = '' )

Retrieves a list of comments for specific posts or a single post. It calls the WP_Comment_Query class, which we will cover in the next chapter. $args are an optional array or string of arguments to query comments. The default arguments are:

  • author_email—A string of a comment author’s email address.

  • ID—An integer of the ID of a comment.

  • karma—An integer of a comment’s karma, which can be used by plugins for rating.

  • number—An integer of the number of comments to return. Default is all comments.

  • offset—An integer of the number of comments to pass over. Default is 0.

  • orderby—A string of the field to order the comment by. Allowed values are: comment_agent, comment_approved, comment_author, comment_author_email, comment_author_IP, comment_author_url, comment_content, comment_date, comment_date_gmt, comment_ID, comment_karma, comment_parent, comment_post_ID, comment_type, user_id.

  • order—A string of how to order the selected order by argument. Defaults to DESC and also accepts ASC.

  • parent—An integer of a comment’s parent comment ID.

  • post_id—An integer of the post ID a comment is attached to.

  • post_author—An integer of the post author ID a comment is attached to.

  • post_name—A string of the post name a comment is attached to.

  • post_parent—An integer of the post parent ID a comment is attached to.

  • post_status—A string of the post status a comment is attached to.

  • post_type—A string of the post type a comment is attached to.

  • status—A string of the status of a comment. Optional values are hold, approve, spam, or trash.

  • type—A string of the type of a comment. Optional values are '', pingback, or trackback.

  • user_id—An integer of the user ID of a comment.

  • search—A string of search terms to search a comment on. Searches the comment_author, comment_author_email, comment_author_url, comment_author_IP, and comment_content fields.

  • count—A Boolean that will make the query return a count or results. The default value is false.

  • meta_key—The comment meta key of comment meta to search on.

  • meta_value—The comment meta value of comment meta to search on; meta_key is required.

wp_insert_comment( $commentdata )

Inserts a comment into the database:

  • $commentdata—A required array of comment fields and values to be inserted. Available fields to be inserted are comment_post_ID, comment_author, comment_author_email, comment_author_url, comment_author_IP, comment_date, comment_date_gmt, comment_content, comment_karma, comment_approved, comment_agent, comment_type, comment_parent, and user_id.

wp_update_comment( $commentarr )

Updates comment data and filters to make sure all required fields are valid before updating in the database:

  • $commentarr - An optional array of arguments containing comment fields and values to be updated. These are the same field arguments just listed for the wp_insert_comment() function.

wp_delete_comment( $comment_id, $force_delete = false )

Deletes a comment. By default, it will trash the comment unless specified to permanently delete:

  • $comment_id - A required integer of the comment ID to trash/delete.

  • $force_delete - An optional Boolean that if set to true will permanently delete a comment. Example 2-6 demonstrates some of the basic functions for interacting with the wp_comments table.

Example 2-6 demonstrates managing comment data attached to a post.

Example 2-6. Working with the wp_comments table
<?php
// insert post
$args = array(
 'post_title'   => 'What should I do tonight?',
 'post_content' => 'Think of something cool to do and make a comment about it!',
 'post_status'  => 'publish'
);
$post_id = wp_insert_post( $args );
echo 'post ID: ' . $post_id . ' - ' . $args['post_title'] . '<br>';

// make comments array
$comments[] = 'ICE CREAM!!!!';
$comments[] = 'Taco Bell';
$comments[] = 'Get a good night sleep';

//loop comments array
foreach ( $comments as $key => $comment ) {
	// insert comments
	$commentdata = array(
		'comment_post_ID' => $post_id,
		'comment_content' => $comments[$key],
	);
	$comment_ids[] = wp_insert_comment( $commentdata );
}
echo 'comments:<pre>';
print_r( $comments );
echo '</pre>';

// update comment
$commentarr['comment_ID'] = $comment_ids[0];
$commentarr['comment_content'] = 'Read this entire book';
wp_update_comment( $commentarr );

// insert comment - sub comment from parent id
$commentdata = array(
	'comment_post_ID' => $post_id,
	'comment_parent' => $comment_ids[0],
	'comment_content' => 'That is a pretty good idea...',
);
wp_insert_comment( $commentdata );

// get comments - search taco bell
$comments = get_comments( 'search=Taco Bell&number=1' );
foreach ( $comments as $comment ) {
	// insert comment - sub comment of taco bell comment id
	$commentdata = array(
		'comment_post_ID' => $post_id,
		'comment_parent' => $comment->comment_ID,
		'comment_content' => ',
	);
	wp_insert_comment( $commentdata );
}

// get comment - count of comments for this post
$comment_count = get_comments( 'post_id= ' . $post_id . '&count=true' );
echo 'comment count: ' . $comment_count . '<br>';

// get comments - get all comments for this post
$comments = get_comments( 'post_id=' .$post_id );
foreach ( $comments as $comment ) {
	// update 1st comment
	if ( $comment_ids[0] == $comment->comment_ID ) {
	 $commentarr = array(
	  'comment_ID' => $comment->comment_ID,
	  'comment_content' => $comment->comment_content . ' & build some apps!',
	);
		wp_update_comment( $commentarr );
		// delete all other comments
	}else {
		// delete comment
		wp_delete_comment( $comment->comment_ID, true );
	}
}

// get comment - new comment count
$comment_count = get_comments( 'post_id= ' . $post_id . '&count=true' );
echo 'new comment count: ' . $comment_count . '<br>';

// get comment - get best comment
$comment = get_comment( $comment_ids[0] );
echo 'best comment: ' . $comment->comment_content;

/*
The output from the above example should look something like this:
post ID: 91011 - What should I do tonight?
comments:
Array
(
    [0] => ICE CREAM!!!!
    [1] => Taco Bell
    [2] => Get a good night sleep
)
comment count: 5
new comment count: 1
best comment: Read this entire book & build some apps!
*/
?>

wp_commentsmeta

Just like the wp_usermeta and wp_postmeta table, this table stores any custom, additional data tied to a comment by the comment_id fields. Table 2-7 shows the database structure for the wp_commentsmeta table.

Table 2-7. DB schema for wp_commentsmeta table
Column Type Collation Null Default Extra

meta_id

bigint(20)

No

None

AUTO_INCREMENT

comment_id

bigint(20)

No

0

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

Functions Found in /wp-includes/comment.php

The following functions are found in /wp-includes/comment.php.

get_comment_meta($comment_id, $key = '', $single = false)

Get comment meta for a given comment ID:

  • $comment_id—A required integer of the comment ID for which you would like to retrieve comment meta.

  • $key—Optional string of the meta key name for which you would like to retrieve comment meta. The default is to return metadata for all of the meta keys for a particular post.

  • $single—A Boolean of whether to return a single value or not. The default is false, and the value will be returned as an array.

add_comment_meta($comment_id, $meta_key, $meta_value, $unique = false)

Add comment meta for given comment ID:

  • $comment_id—A required integer of a comment ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to store.

  • $meta_value—A required mixed value of an integer, string, array, or object.

  • $unique—An optional Boolean that when set to true will make sure the meta key can only ever be added once for a given ID.

update_comment_meta($comment_id, $meta_key, $meta_value, $prev_value = '')

Update comment meta for a given comment ID:

  • $comment_id—A required integer of a comment ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to store. If this meta key already exists, it will update the current row’s meta value; if not, it will insert a new row.

  • $meta_value—A required mixed value of an integer, string, array, or object. Arrays and objects will automatically be serialized.

  • $prev_value—An optional mixed value of the current metadata value. If a match is found, it will replace the previous/current value with the new value you specified. If left blank, the new meta value will replace the first instance of the matching key. If you have five rows of metadata with the same key and you don’t specify which row to update with this value, it will update the first row and remove the other four.

delete_comment_meta($comment_id, $meta_key, $meta_value = '')

Deletes comment metadata for a provided comment ID and matching key. You can also specify a matching meta value if you only want to delete that value and not other metadata rows with the same meta key:

  • $comment_id—A required integer of a comment ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to delete.

  • $meta_value—An optional mixed value of the meta value. If you have more than one record with the same meta key, you can specify which one to delete by matching this value. It defaults to nothing, which will delete all meta rows with a matching post_id and meta_key.

Example 2-7 demonstrates some of the basic functions for interacting with the wp_commentsmeta table.

Example 2-7. Working with the wp_commentsmeta table
<?php
// get comments - last comment ID
$comments = get_comments( 'number=1' );
foreach ( $comments as $comment ) {
	$comment_id = $comment->comment_ID;

	// add comment meta - meta for view date & IP address
	$viewed = array( date( "m.d.y" ), $_SERVER["REMOTE_ADDR"] );
	$comment_meta_id = add_comment_meta( $comment_id, 'bwawwp_view_date',
        $viewed, true );
	echo 'comment meta id: ' . $comment_meta_id;

	// update comment meta - change date format to format like
	// October 23, 2020, 12:00 am instead of 10.23.20
	$viewed = array( date( "F j, Y, g:i a" ), $_SERVER["REMOTE_ADDR"] );
	update_comment_meta( $comment_id, 'bwawwp_view_date', $viewed );

	// get comment meta - all keys
	$comment_meta = get_comment_meta( $comment_id );
	echo '<pre>';
	print_r( $comment_meta );
	echo '</pre>';

	// delete comment meta
	delete_comment_meta( $comment_id, 'bwawwp_view_date' );
}

/*
The output from the above example should look something like this:
comment meta id: 16
Array
(
    [bwawwp_view_date] => Array
        (
            [0] => a:2:{i:0;s:24:"August 11, 2018, 4:16 pm";i:1;s:9:"127.0.0.1";}
        )

)
*/
?>

wp_terms

The wp_terms table stores each category name or term name that you create. Each record is tied to its taxonomy in the wp_term_taxonomy table by the term_id. So you’re familiar with post categories and tags? Well, each category or tag is stored in this table, and technically they are both taxonomies. Every term that is stored in the name column is a taxonomy term. We will be covering taxonomies in much more detail in Chapter 5, so if you don’t fully grasp what a taxonomy is, you will soon. Table 2-8 shows the database structure for the wp_terms table.

Table 2-8. DB schema for wp_terms table
Column Type Collation Null Default Extra

term_id

bigint(20)

No

None

AUTO_INCREMENT

name

varchar(200)

No

slug

varchar(200)

utf8_general_ci

No

term_group

bigint(10)

No

0

Functions Found in /wp-includes/taxonomy.php

The following functions are found in /wp-includes/taxonomy.php.

get_terms( $taxonomies, $args = '' )

Gets the terms of a specific taxonomy or an array of taxonomies:

  • $taxonomies—A required string or array of a taxonomy or list of taxonomies.

  • $args—An optional string or array of arguments. Available arguments are:

    1. orderby—Default is name. Can be name, count, term_group, slug, or nothing, which will use term_id. Passing a custom value other than these will cause the terms to be ordered on that custom value.

    2. orderASC or DESC. The default is ASC.

    3. hide_empty—The default value is true, which will only return terms that are attached to a post. If set to false, you can return all terms regardless, if they are being used by a post or not.

    4. exclude—An array or comma-separated or space-delimited string of term IDs to exclude from the query results. If include is being used, exclude will be ignored.

    5. exclude_tree—An array or comma-separated or space-delimited string of term IDs to exclude from the query results, including any child terms. If include is being used, exclude_tree will be ignored.

    6. include—An array or comma-separated or space-delimited string of term IDs to include in the query results.

    7. number—The number of terms for the query to return. The default is all.

    8. offset—The number by which to offset the terms query.

    9. fields—You can specify if you want to return term IDs or names. The default is all, which returns an array of term objects.

    10. slug—A string that will return any terms that have a matching slug.

    11. hierarchical—Includes all child terms if they are attached to posts. The default is true, so to not return terms hierarchically, set this to false.

    12. search—A string that will return any terms whose names match the value provided. The search is case-insensitive.

    13. name_like—A string that will return any terms whose names begin with the value provided. Like the search, this is case-insensitive.

    14. pad_counts—If set to true, the query results will include the count of each term’s children.

    15. get—If set to all, returns terms regardless of ancestry or whether the terms are empty.

    16. child_of—When set to a term ID, the query results will contain all descendants of the provided term ID. The default is 0, which returns everything.

    17. parent—When set to a term ID, the query results will contain the direct children of the provided term ID. The default is an empty string.

    18. cache_domain—Enables a unique cache key to be produced when this query is stored in object cache.

get_term( $term, $taxonomy, $output = OBJECT, $filter = raw )

Gets all term data for any given term:

  • $term—A required integer or object of the term to return.

  • $taxonomy—A required string of the taxonomy of the term to return.

  • $output—An optional string of the output format. The default value is OBJECT, and the other values can be ARRAY_A (associative array) or ARRAY_N (numeric array).

  • $filter—An optional string of how the context should be sanitized on output. The default value is raw.

wp_insert_term( $term, $taxonomy, $args = array() )

Adds a new term to the database:

  • $term—A required string of the term to add or update.

  • $taxonomy—A required string of the taxonomy the term will be added to.

  • $args—An optional array or string of term arguments to be inserted/updated. Available arguments are:

    1. alias_of—An optional string of the slug that the term will be an alias of.

    2. description—An optional string that describes the term.

    3. parent—An optional integer of the parent term ID that this term will be a child of.

    4. slug—An optional string of the slug of the term.

wp_update_term( $term_id, $taxonomy, $args = array() )

Updates an existing term in the database:

  • $term_id—A required integer of the term ID of the term you want to update.

  • $taxonomy—A required string of the taxonomy the term is associated with.

  • $args—An optional array or string of term arguments to be updated. These are the same arguments used in wp_insert_term().

wp_delete_term( $term, $taxonomy, $args = array() )

Deletes a term from the database. If the term is a parent of other terms, then the children will be updated to that term’s parent:

  • $term—A required integer of the term ID of the term you want to delete.

  • $taxonomy—A required string of the taxonomy the term is associated with.

  • $args—An optional array to overwrite term field values.

wp_termmeta

Since WordPress 4.4, meta data can be stored for terms. The Simple Taxonomy Ordering plugin by YIKES, Inc uses term meta to allow you to reorder how your categories and other taxonomies show up in lists and widgets.

Table 2-9. DB schema for wp_termmeta table
Column Type Collation Null Default Extra

meta_id

bigint(20)

No

None

AUTO_INCREMENT

term_id

bigint(20)

No

0

meta_key

varchar(255)

utf8_general_ci

Yes

NULL

meta_value

longtext

utf8_general_ci

Yes

NULL

The functions below work similarly to the variants for user meta, post meta, and comment meta.

get_term_meta( $term_id, $key = '', $single = false )

Gets a term’s meta value for a specified key:

  • $term_id—A required integer of a term ID.

  • $key—An optional string of the meta key of the value you would like to return. If blank then all metadata for the given term will be returned.

  • $single—A Boolean of whether to return a single value or not. The default is false and the value will be returned as an array.

There can be more than one meta key for the same term ID with different values. If you set $single to true, you will get the first key’s value; if you set it to false, you will get an array of the values of each record with the same key.

update_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' )

This function will update term metadata but will also insert metadata if the passed-in key doesn’t already exist:

  • $term_id—A required integer of a term ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to store. If this meta key already exists, it will update the current row’s meta value, if not it will insert a new row.

  • $meta_value—A required mixed value of an integer, string, array, or object. Arrays and objects will automatically be serialized.

  • $prev_value—An optional mixed value of the current metadata value. If a match is found, it will replace the previous/current value with the new value you specified. If left blank, the new meta value will replace the first instance of the matching key. If you have five rows of metadata with the same key and you don’t specify which row to update with this value, it will update the first row and remove the other four.

Note

This function relies on the update_metadata() function located in /wp-includes/meta.php. Check it out!

add_term_meta($term_id, $meta_key, $meta_value, $unique = false)

Inserts brand-new term meta into the wp_termmeta table. Again, it is preferred to use update_term_meta() to insert new rows as well as update them. If you want to ensure that a given meta key is only ever used once per term, you should use this function and set the $unique parameter to true:

  • $term_id—A required integer of a term ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to store.

  • $meta_value—A required mixed value of an integer, string, array, or object.

  • $unique—An optional Boolean, which when set to true will make sure the meta key can only ever be added once for a given ID.

delete_term_meta($term_id, $meta_key, $meta_value = '')

Deletes term metadata for a provided term ID and matching key. You can also specify a matching meta value if you only want to delete that value and not other metadata rows with the same meta key:

  • $term_id—A required integer of a term ID.

  • $meta_key—A required string of the meta key name for the meta value you would like to delete.

  • $meta_value—An optional mixed value of the meta value. If you have more than one record with the same meta key, you can specify which one to delete by matching the meta value. It defaults to nothing, which will delete all meta rows with a matching term_id and meta_key.

wp_term_taxonomy

The wp_term_taxonomy table stores each taxonomy type you are using. WordPress has two taxonomy types built in, category and post_tag, but you can also register your own taxonomies. When a new term gets added in the wp_terms table, it is associated with its taxonomy in this table, along with that taxonomy term ID, description, parent, and count. Table 2-10 shows the structure for the wp_term_taxonomy table.

Table 2-10. DB schema for wp_term_taxonomy table
Column Type Collation Null Default Extra

term_taxonomy_id

bigint(20)

No

None

AUTO_INCREMENT

term_id

bigint(20)

No

0

taxonomy

varchar(32)

utf8_general_ci

No

description

longtext

utf8_general_ci

No

None

parent

bigint(20)

No

0

count

bigint(20)

No

0

/wp-includes/taxonomy.php

The following functions are found in /wp-includes/taxonomy.php.

get_taxonomies( $args = array(), $output = names, $operator = and )

This function returns a list of registered taxonomy objects or a list of taxonomy names:

  • $args—An optional array of arguments to query what taxonomy objects get returned. There are a lot, and we will cover all of them in Chapter 5.

  • $output—An optional string of either names or objects. The default is names, which will return a list of taxonomy names.

  • $operator—An optional string of either and or or. The default is and, which means that all of the arguments passed in must match. If set to or, any of the arguments passed in can match.

get_taxonomy( $taxonomy )

This function will first check that the parameter string given is a taxonomy object; if it is, it will return it:

  • $taxonomy—A required string of the name of the taxonomy object to return.

register_taxonomy( $taxonomy, $object_type, $args = array() )

This function creates or updates a taxonomy object. Registering custom taxonomies can really extend WordPress because you can categorize your posts anyway you see fit. We are going to go over registering taxonomies in much more detail in Chapter 5:

  • $taxonomy - A required string of the name of the taxonomy.

  • $object_type - A required array or string of the object types (post types like post and page) that this taxonomy will be tied to.

  • $args - An optional array or string of arguments. There are a lot, and we will cover all of them in Chapter 5.

wp_term_relationships

The wp_term_relationships table relates a taxonomy term to a post. Every time you assign a category or tag to a post, it’s being linked to that post in this table. Table 2-11 shows the structure for the wp_term_relationships table.

Table 2-11. DB schema for wp_term_relationships table
Column Type Collation Null Default Extra

object_id

bigint(20)

No

0

term_taxonomy_id

bigint(20)

No

0

term_order

int(11)

No

0

get_object_taxonomies( $object, $output = names )

Returns all taxonomies associated with a post type or post object:

  • $object—A required array, string, or object of the name(s) of the post type(s) or post object(s).

  • $output—An optional string of either names or objects. The default is names, which will return a list of taxonomy names.

wp_get_object_terms( $object_ids, $taxonomies, $args = array() )

Returns terms associated with a supplied post object ID or IDs and a supplied taxonomy.

  • $object_ids—A required string or array of object IDs for the object terms you would like to return. Passing in a post ID would return terms associated with that post ID.

  • $taxonomies—A required string or array of the taxonomy names from which you want to return terms. Passing in the taxonomy post_tag would return terms of the post_tag taxonomy.

  • $args—An optional array or string of arguments that change how the data is returned. The arguments that can be changed are:

    1. orderby—Defaults to name; also accepts count, slug, term_group, term_order, and none.

    2. order—Defaults to ASC; also accepts DESC.

    3. fields—Defaults to all; also accepts ids, names, slugs, and all_with_object_id. This argument will dictate what values will be returned.

wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false )

Adds taxonomy terms to a provided object ID and taxonomy. It has the ability to overwrite all terms or to append new terms to existing terms. If a term passed into this function doesn’t already exist, it will be created and then related to the provided object ID and taxonomy:

  • $object_id—A required integer of the object ID (post ID) to relate your terms to.

  • $terms—A required array, integer, or string of the terms you would like to add to an object (post).

  • $taxonomy—A required array or string of the taxonomy or taxonomies you want to relate your terms to.

  • $append—An optional Boolean that defaults to false that will replace any existing terms related to an object ID with the new terms you provided. If set to true, your new terms will be appended to the existing terms.

Note

At one point, there was discussion to remove the wp_terms table from WordPress in a future release. The name and slug columns of wp_terms would have been moved into the wp_terms_taxonomy table, and a MySQL view was to be created called wp_terms that could be queried against, preserving backward compatibility for your custom queries. This update has been shelved, but it would be nice to clean up some of the tables around terms and taxonomies.

Hooks: Actions and Filters

WordPress developers hook for a living! Hooks are great, and they make adding functionality into WordPress plugins and themes simple and easy.

Seriously though, action and filter hooks are one of the most powerful tools in WordPress. We spend a lot of time working with actions and filters. Understanding this section is important to your growth as a WordPress developer.

There are two types of hooks: actions and filters.

Actions

Any place an action hook, or technically a do_action() function, exists in code running on WordPress, you can insert your own code by calling the add_action() function and passing in the action hook name and your custom function with the code you want to run:

  • do_action( $tag, $arg );

    • $tag—The name of the action hook being executed.

    • $arg—One or more additional arguments that will get passed through to the function called from the add_action() function referencing this do_action() function. Say what? Keep reading…

You can create your own hooks in a theme or plugin by adding your own do_action() functions. However, most of the time you will be using established hooks in the WordPress core or other plugins. For example, let’s say we wanted to check if a user was logged in when WordPress first loads up but before any output is displayed to the browser. We can use the init hook:

<?php
add_action( 'init', 'my_user_check' );

function my_user_check() {
	if ( is_user_logged_in() ) {
		// do something because a user is logged in
	}
}
?>

So what just happened? In the core of WordPress, there is an action hook, do_action(init), and we are calling a function called “my_user_check” from the add_action() function. At whatever point in time the code is being executed, when it gets to the init action hook, it will then run our custom my_user_check function to do whatever we want before continuing on.

Note

Check out WordPress’s reference page for a list of the most used WordPress hooks.

Filters

Filters are kind of like action hooks in the sense that you can tap into them wherever they exist in WordPress. However, instead of inserting your own code where the hook or do_action() exists, you are filtering the returned value of existing functions that are using the apply_filters() function in WordPress core, plugins, and/or themes. In other words, by utilizing filters, you can hijack content before it is inserted into the database or before it is displayed to the browser as HTML:

  • apply_filters( $tag, $value, $var );

    • $tag—The name of the filter hook.

    • $value—The value that the filter can be applied on.

    • $var—Any additional variables, such as a string or an array, passed into the filter function.

If you search the core WordPress files for apply_filters you will find that the apply_filters() function is called all over the place, and like action hooks, the apply_filters() function can also be added to and called from any theme or plugin. Anywhere in code running on your WordPress site that you see the apply_filters() function being called, you can filter the value being returned by that function. For our example, we are going to filter the title of all posts before they are displayed to the browser. We can hook into any existing filters using the add_filter() function:

  • add_filter( $tag, $function, $priority, $accepted_args );

    • $tag—The name of the filter hook you want to filter. This should match the $tag parameter of the apply_filters() function call you want to filter the results for.

    • $function—The name of the custom function used to actually filter the results.

    • $priority—This number sets the priority in which your add_filter will run compared to other places in the code that might be referencing the same filter hook tag. By default, this value is 10.

    • $accepted_args—You can set the number of parameters that your custom function that handles the filtering can except. The default is 1, which is the $value parameter of the apply_filters function.

OK, so how would real code for this look? Let’s start by adding a filter to alter the title of any post returned to the browser. We know of a filter hook for the_title that looks like this:

apply_filters( 'the_title', $title, $id );

$title is the title of the post and $id is the ID of the post:

<?php
add_filter( 'the_title', 'my_filtered_title', 10, 2 );

function my_filtered_title( $value, $id ) {
	$value = '[' . $value . ']';
	return $value;
}
?>

The preceding code should wrap any post titles in brackets. If your post title was “hello world,” it would now read “[hello world].” Note that we didn’t use the $id in our custom function. If we wanted to, we could have only applied the brackets to specific post IDs.

Note

While add_action() is meant to be used with do_action() hooks and add_filter() is meant to be used with apply_filters() hooks, the functions work the same way and are interchangeable. For readability, it is still a good idea to use the proper function depending on whether you intend to return a filtered result or just perform some code at a specific time.

Development and Hosting Environments

WordPress needs a web server to run. This section covers the basics of setting up a local and remote environment for your WordPress app.

Working Locally

Before we get into WordPress basics, we suggest that you get a local development environment setup to run your WordPress install. You can run WordPress right on your desktop computer or laptop which will make development more efficient and faster. You can run code locally vs FTPing or deploying code from a code repository to web server. You can also write and test code without having an internet connection. I see a productive work day with your laptop and mother nature somewhere in your future.

There are a ton of online resources on how to run WordPress locally so we aren’t going to cover them all in detail but based on your operating system and preferences you will most likely be setting up a MAMP (Mac, Apache, MySQL, PHP), WAMP (Windows, Apache, MySQL, PHP), or LAMP (Linux, Apache, MySQL, PHP) stack. Also check out XAMPP and AMPPS which are cross platform stacks.

There are a few local web server tools developed specifically for WordPress. DesktopServer by ServerPress and Local by Flywheel are popular tools which streamline all of the semi-tedious configuration settings for setting up local WordPress installs via their UI.

Tools like VirtualBox, Vagrant, and Docker are also popular in the WordPress community. These tools are especially good for certain automated testing and deployment schemes. WordPress.org has a good document for installing Varying Vagrant Vagrants (VVV), which is popular among core contributors.

Choosing a Web Host

If you don’t already know what a web host is, you have some Googling to do my friend. In short, a web host is where your website lives, specifically your WordPress directory and database.

We are assuming that most of you reading this already work with a web hosting company, or maybe you have or work for a company that has their own internal web servers. Choosing a good host for your web application is key to ensuring it’s fast, secure, and scalable. There are lots of web hosting companies out there, and pretty much all of them will be able to host WordPress, but not all of them are optimized specifically for WordPress.

A “Managed” WordPress hosting company would be the best choice for a high scale WordPress powered web application because their focus is on providing optimized WordPress hosting environments. For large-scale websites, a lot of work has to go into the caching of the data because of the way the WordPress database is structured with storing metadata. If you use Wordpress as a large CMS with lots of posts with lots of post meta on not a powerful enough server, it will start to buckle and eat up server resources. Hosting companies with a focus on optimized WordPress have built-in caching systems that help optimize your content delivery.

You can always use caching plugins if your host doesn’t do all the caching for you (Chapter 13 covers caching and other scaling considerations if you are managing your own environment). Some managed WordPress hosts offer other advanced tools like automatic plugin updates and services liked CDN (Content Delivery Network) integration geared towards optimizing your hosted WordPress environment.

Some examples of managed WordPress hosting companies include WP Engine, WordPress VIP, and Pagely. There are many other options for hosting. We maintain an up-to-date list of hosts for all sizes at our website at bwawwp.com/hosts/.

Development, Staging, and Production Environments

A typical website project will have three code environments: Development, Staging, and Production. Ideally, all three environments are separate web servers, with separate datbases, and separate installs of WordPress.

The Development environment (sometimes referred to as Dev or DEV) is where you do your new coding and maintenance work. Typically, it’s the local environment you set up using the information earlier in this chapter.

The Staging environment (sometimes referred to as Testing or TEST) is where you do your testing. The data on the staging site should be as close as possible to the Production site. This means that you will sometimes be exporting data from the live site, scrubbing it to remove private user information, and importing it into the staging site.

The Production environment (sometimes referred to as Live or PRD) is the real website your users visit and interact with.

If you can’t have all three code environments, at the very least you want to have a separate Development and Production environment. You should never update code on a Production website without testing it thoroughly on a Development site.

Ideally you would do all of your development locally, then commit your code to a code repository and deploy it to a Development site that can have multiple developers working on it at the same time. Then once it’s ready to be thoroughly tested, maybe by your boss or client in an environment that is an exact copy of the production site, you would deploy your code to the Staging site. Finally, once everything checks out on the Staging site, you deploy your code into Production. Make backups of the Production website before deploying any code in case you need to roll back any updates. And finally, never assume that your code will work without testing it.

Some managed WordPress hosting companies like WP Engine have automatic staging sites for every WordPress site you have with them, which is a really cool feature.

Note

If you are thinking about FTP when “deploying code” was mentioned above, you should up your game and use a source code repository like GitHub.

Extending WordPress

Now you know the basics of how WordPress is set up, how the data is stored, and the basic tools for manipulating that data. You’ve been introduced to action and filter hooks, which are a primary method for extending WordPress.

We will cover more of the various built-in functions and methods used to interact with WordPress data throughout the book. Chapter 3 covers the WordPress Plugin API, including some of the key features of WordPress that make extending it easy, powerful, and consistent!

1 … ever, ever, ever …

2 The third parameter for add_option, which was deprecated in 2.3, used to be a “description” string that was stored along with the option in the wp_options table.