Skip to main content
WordPress made easy with the drag & drop Total WordPress Theme!Learn More

Manage WordPress Posts with PHP – Create and Update

Last updated on:
  1. 1. Create WordPress Posts And Pages Using PHP – A 101 Tutorial
  2. 2. Currently Reading: Manage WordPress Posts with PHP – Create and Update

As promised, here is the second part to my first post, ‘Create WordPress Posts And Pages Using PHP – A 101 Tutorial’. In this article we will be looking at how we can extend the basic functions and theory I discussed in Part 1 to create a robust and ultimately reliable PHP Wrapper Class.

As always, I do sympathize with those of you who simply want to dive straight into the code. You can find it in one of my Github repos (it was slightly too long to paste directly into this article itself!).


What we want to achieve:

  • A fully functional and reliable PHP production-grade framework.
  • Be able to manage not only post creation, but also to manage and update pre-existing posts.
  • Be able to search for and manipulate posts by their; ID; Title or Slug.

There will be 2 sections to this article: Usage Notes and a Step-by-Step Guide.

Without any further ado, let’s begin!


First of all, make sure you have either included the class.postcontroller.php file from Github or have pasted the contents of that file into your PHP document. This won’t work without it! We’ll have a look at the code in this file in the second half of this tutorial.

Instantiate the class as follows;

$Poster = new PostController;

Post Controller works like this; you set post attributes using the relevant functions. Then you either create or update a post. To update a post, you need to have first searched and found one to update. Sound simple enough?

Set Attributes

This sets the post’s title. No HTML is allowed here, and is stripped out.

$Poster->set_title( 'The Title' );

This method sets the post type of the page. Input the slug of the post type, eg. ‘post’ and ‘page’. Custom post types are supported.

$Poster->set_type( 'page' );

This method sets the post’s content. HTML is allowed.

$Poster->set_content( '<h1>This is my awesome new post!</h1>' );

This sets the post’s author. Simply specify the author ID of the new author. This must be an integer.

$Poster->set_author_id( 12 );

Slug / ‘Name’
This is the custom url path of the post (if enabled). Take care with this, as if the slug is already in use, it may cause some errors. I have included validation to try and avoid this at all costs though. No special characters or html allowed.

$Poster->set_post_slug( "new_slug" );

Template (Pages Only)
This method allows you to set your page’s template (must be a page). If applied to a different post type it will be ignored, and an error will be added to the errors array. The format of the input will be ‘php_template_name.php’ and will be unique to each theme.

$Poster->set_page_template( 'fullwidth_page.php' );

This method sets the post’s state. Available options;
[ ‘draft’ | ‘publish’ | ‘pending’| ‘future’ | ‘private’ | custom registered status ]

$Poster->set_post_state( "pending" );


The minimum requisite for creating a post is having the title set. You can do this by using the following code;

$Poster->set_title( "New Post" );

After this has been set, simply run the create method.


Note: The method will check if there is another post with the same name (just to ensure that it isn’t making duplicates). Hence a unique title is required.


Before we can update an existing post, we first have to find it with the Search function.

To do this, we use the search method.

$Poster->search( 'SEARCH_BY' , 'DATA' );

This method accepts 2 parameters, the attribute to search by and the data to search for.

You can search by title;

$Poster->search( 'title' , 'DATA' );

You can search by ID;

$Poster->search( 'id' , 'DATA' );

And you can search by slug;

$Poster->search( 'slug' , 'DATA' );

The data parameter will accept either a string (if searching by title or slug) or an integer (if searching by ID). If a post or page cannot be found with the specified parameters, an error will be added to the $errors array. You can call and display this with the following code.

$error = $Poster->get_var( 'errors' );
$Poster->PrettyPrint( $error );


Once a post has been found, you can assign it new attributes. After the attributes have been set, simply call the update method.


For example, if you wish to change the title of post 1 (ID = 1), you would use the following code.

$Poster->search( 'id', 1 );
$Poster->set_title( 'New Title' );

All the attributes can be updated this way.

Retrieving Variables

To retrieve defined variables you can use the get_vars method.

$Poster->get_var( 'title' );    // Returns the title (if you have set it)
$Poster->get_var( 'type' );     // Returns the post type (if you have set it)
$Poster->get_var( 'content' );  // Returns the content (if you have set it)
$Poster->get_var( 'category' ); // Returns the category as an array (if set)
$Poster->get_var( 'template' ); // Returns the template (if you have set it)
$Poster->get_var( 'slug' );     // Returns the slug (if you have set it)
$Poster->get_var( 'auth_id' );  // Returns the author id (if you have set it)
$Poster->get_var( 'status' );   // Returns the post's status (if you have set it)
$Poster->get_var( 'errors' );   // Returns the errors array


	// Returns the a PHP Object of the post. This can be used to check if the search method was successful
	$Poster->get_var( 'current_post' );

	// Returns the selected post's ID (integer).
	$Poster->get_var( 'current_post_id' );

	// Returns the URL permalink of the selected post
	$Poster->get_var( 'current_post_permalink' );

That is that! If you have any questions about usage you can ask me in the comments here. If you find a bug please tell me about it on Github.

Ready for the second part?

Building the Class: Step by Step Guide

Now we have seen how to use the class, let’s go back to the beginning.
Our class will be called PostController;

class PostController {

. . . class functions go here . . .


For those of you who aren’t well versed with PHP Classes, a Class is defined as an object which contains a collection of functions and variables which are working together. Check out the Introduction for more details about the syntax and theory we will be using to create our framework. Next on our to-do list is to declare all the variables we will be using.

// Variables for Post Data
public $PC_title;
public $PC_type;
public $PC_content;
public $PC_category;
public $PC_template;
public $PC_slug;
public $PC_auth_id;
public $PC_status = "publish";

// Variables for Post Updating
public $PC_current_post;
public $PC_current_post_id;
public $PC_current_post_permalink;

// Error Array
public $PC_errors;

Ok, so let’s break this down into three blocks.The first block will hold the Post Data which YOU have specified. This will be the data which will be used to update and create a post (when we come to it). Although you can probably guess by the variables names, here is a list of the data we want to be able to store.

  1. Title
  2. Type (Page/Post etc.)
  3. Content
  4. Category/Categories
  5. Page Template
  6. Slug
  7. Author ID, and
  8. The Post’s Status (eg. published/draft)

The second block will hold the actual WordPress post data which our search query will return (or the data about a created post after it has been created). The ‘$PC_current_post’ variable will hold an object of the whole post. This holds all of the data about the post, including (importantly) the data listed above. Similarly, the Post ID and Permalink variables will hold those pieces of data about the existing post.

The third and final block only has a single variable: the errors array. If a problem is encountered at a known ‘pinch’ point, then a message about the problem will be added to this array for your reference. Please note that I have included the ‘PC_’ just out of good habit: variable names should never be ambiguous. PC stands for ‘PostController’. If you change this remember to also change the prefix in the get_var() function.

Right, now that we have everything set up, let’s dive into our first function.

// Creation functions
public function create() {

	if ( isset( $this->PC_title ) ) {

		if ( $this->PC_type == 'page' ) {
			$post = get_page_by_title( $this->PC_title, 'OBJECT', $this->PC_type );
		} else {
			$post = get_page_by_title( $this->PC_title, 'OBJECT', $this->PC_type );

		$post_data = array(
			'post_title'    => wp_strip_all_tags($this->PC_title),
			'post_name'     => $this->PC_slug,
			'post_content'  => $this->PC_content,
			'post_status'   => $this->PC_status,
			'post_type'     => $this->PC_type,
			'post_author'   => $this->PC_auth_id,
			'post_category' => $this->PC_category,
			'page_template' => $this->PC_template

		if ( ! isset( $post ) ) {
			$this->PC_current_post_id        = wp_insert_post( $post_data, $error_obj );
			$this->PC_current_post           = get_post( ( integer ) $this->PC_current_post_id, 'OBJECT' );
			$this->PC_current_post_permalink = get_permalink( ( integer ) $this->PC_current_post_id );
			return $error_obj;
		} else {
			$this->errors[] = 'That page already exists. Try updating instead. Control passed to the update() function.';
			return FALSE;

	} else {
		$this->errors[] = 'Title has not been set.';
		return FALSE;

For those of you who have read Part 1 of this Series: Create WordPress Posts And Pages Using PHP – A 101 Tutorial, this function will look familiar to you. This is merely the finished function from that tutorial after being adapted to this new set up. To read the explanation of this particular function I’d recommend checking out part 1. The only major difference to the function itself is that instead of the adding the function to the WordPress head, it is run immediately.

As a quick run-through, the function checks that a title has been set (this is the minimum requirement for post creation). It then checks for an existing post (by title). If a post is found, a message is added to the errors array and control is passed to the update() function. If a post isn’t found with this title, then the $post_data is used to create a new post. The Current Post variable ( as well as the other 2 data variables ) are updated to hold this new post’s data. This is good for checking the post you have just made. If there are problems, then a message is added to the $errors array.

Sorry if that was a bit fast: if you are a bit confused, I do recommend you check out my 101 article on this.

For all of you still with me, let’s continue on.

public function set_title( $name ) {
	$this->PC_title = $name;
	return $this->PC_title;

public function set_type( $type ) {
	$this->PC_type = $type;
	return $this->PC_type;

public function set_content( $content ) {
	$this->PC_content = $content;
	return $this->PC_content;

public function set_author_id( $auth_id ) {
	$this->PC_auth_id = $auth_id;
	return $this->PC_auth_id;

public function set_post_state( $content ) {
	$this->PC_status = $content;
	return $this->PC_status;

This bit is pretty simple. The create() and update() functions both use local variables as the source of their data. Therefore we need a way to set this data. The first 5 variables are set as you can see above. ‘What about the other 3 though?’, I hear you ask. Don’t fret, here they are, but you will see that there is a bit of validation to verify their inputs (to maximize reliability).

public function set_post_slug( $slug ) {

	$args = array('name' => $slug);
	$posts_query = get_posts( $args );

	if ( ! get_posts( $args ) && ! get_page_by_path( $this->PC_slug ) ) {

		$this->PC_slug = $slug;
		return $this->PC_slug;

	} else {
		$this->errors[] = 'Slug already in use.';
		return FALSE;


public function set_page_template( $content ) {

	if ( $this->PC_type == 'page' ) {
		$this->PC_template = $content;
		return $this->PC_template;
	} else {
		$this->errors[] = 'You can only use template for pages.';
		return FALSE;


public function add_category( $IDs ){

	if ( is_array( $IDs ) ) {
		foreach ( $IDs as $id ) {
			if ( is_int( $id ) ) {
				$this->PC_category[] = $id;
			} else {
				$this->errors[] = '<b>' .$id . '</b> is not a valid integer input.';
				return FALSE;
	} else {
		$this->errors[] = 'Input specified in not a valid array.';
		return FALSE;



These last three are a bit more complicated, but very important to avoid horrible errors.


The set_post_slug() function is something you need to be very careful with. The post slug is essentially the string added onto the end of the base URL to create the post’s individual address. This is also used as a fundamental post identifier by WordPress. Eg. This post’s slug is ‘manage-wordpress-posts-php-create-update’. This is added on to the end of the base URL,, to create this article’s unique URL: I’m sure you can imagine the problems you’d run into if more than one post had the same slug (and hence, URL). It is a truly nasty mess best avoided if at all possible.

The Post Slug MUST be UNIQUE

This is the reason for the validation. What is happening is that the function searches for posts with the specified slug. If none are found AND there are no pages with that as a path (essentially the same as the slug in this context), then great! Set the variable. If not, an error is added to the $errors array and the variable is not set (the function returns a Boolean FALSE).

set_page_template() & add_category()

set_page_template() assigns a page template (in the form my_page_template.php) to a Page, and only a page. The validation here simply checks if the post type has been set to ‘page’. If true, store the data. If not, don’t bother.

add_category() allows you to add an array of integers to the post’s categories (must still be in an array, even if only adding 1 number). The integers represent the category IDs. The validation simply loops through the array you specify, checking that each cell has an integer value. If the cell has an integer value, it is added to the master categories array. If not, then it is ignored and an error is added to the errors array. Brilliant! Now we can assign all the variables we need.


At the beginning of this tutorial, we said that we wanted to make a system which has the ability to update existing WordPress posts. For this to be possible, we first need to find the post we want to edit. For this we will use the search() function.

// Search for Post function
public function search( $by, $data ) {

	switch ( $by ) {

	case 'id' :
		if ( is_integer( $data ) && get_post( ( integer ) $data ) !== NULL ) {
			$this->PC_current_post           = get_post( ( integer ) $data, 'OBJECT' );
			$this->PC_current_post_id        = ( integer ) $data;
			$this->PC_current_post_permalink = get_permalink( (integer) $data );
			return TRUE;
		} else {
			$this->errors[] = 'No post found with that ID.';
			return FALSE;


	case 'title' :

		$post = get_page_by_title( $data );

		if ( ! $post ) {
			$post = get_page_by_title( $data, OBJECT, 'post' );

		$id = $post->ID;

		if ( is_integer($id) && get_post( (integer)$id) !== NULL ) {
			$this->PC_current_post           = get_post( (integer)$id, 'OBJECT' );
			$this->PC_current_post_id        = (integer) $id;
			$this->PC_current_post_permalink = get_permalink( (integer) $id );
			return TRUE;
		} else {
			$this->errors[] = 'No post found with that title.';
			return FALSE;


		case 'slug' :
		$args = array(
			'name'          => $data,
			'max_num_posts' => 1
		$posts_query = get_posts( $args );
		if ( $posts_query ) {
			$id = $posts_query[0]->ID;
		} else {
			$this->errors[] = 'No post found with that slug.';

		if ( is_integer($id) && get_post( (integer)$id) !== NULL ) {
			$this->PC_current_post           = get_post( (integer)$id, 'OBJECT' );
			$this->PC_current_post_id        = (integer)$id;
			$this->PC_current_post_permalink = get_permalink((integer)$id);
			return TRUE;
		} else {
			$this->errors[] = 'No post found with that slug.';
			return FALSE;



		$this->errors[] = 'No post found.';
		return FALSE;



This is the search() function. I am using the PHP Switch statement (a different form of IF statements, which in this case will be more efficient than multiple nested Ifs. Find out more about Switch on the Manual The search() function accepts 2 parameters;

1. The ‘Search By’ Value
2. The Data to Search For

You can see from the code above that there are 3 ‘Search By’ options; ‘id’, ‘title’ and ‘slug’. Let’s look at each a little more closely.

Search by ID

This is the simplest search method and merely requires you to know the post’s integer ID. If the data specified is an integer, then the function will send a query to your WordPress database (using the inbuilt get_post() function). If a post with that integer exists, then the post object will be returned and stored in the PC_current_post variable (as well as the ID and permalink being stored as well). Simple!

Search by Title

I don’t think this really needs much explanation: it’s pretty much the same as searching by ID, but searches for the post by its title instead. If you have posts/pages which have the same name (something I do not recommend) then the post with the smallest ID will be chosen. If you know that you do have posts which have the same names, don’t use this method to avoid confusion. Please Note: You will see this code;

$post = get_page_by_title( $data );

if ( ! $post ) {
	$post = get_page_by_title( $data, OBJECT, 'post' );

The reason why we have to do this is because although get_page_by_title() can return data for both posts and pages, it will only return pages unless the third parameter is specified as ‘post’.

I don’t mean to state the obvious, but do realize that if you are searching by title and changing the title at the same time, the code will only be able to run once (the first time). After that it will return a 404 Not Found error.

Search by Slug

This is a nice and reliable method of finding posts. There should be no ambiguity because, as I have said previously, the slug must be unique. Check out the SET_POST_SLUG() section if you aren’t clear what I mean by ‘slug’. To do this, we simply needed to run a WordPress posts query to find posts with the specific slug. We can retrieve the ID from this method and set the current post variables in the same way as the other 2 methods. In the same way as Searching by Title, searching for and changing slug together will cause problems. Beware!

Update and Manage

Right! Now that searching is out of the way, we can move on to the actual update() function.

// Update Post
public function update(){
	if ( isset($this->PC_current_post_id ) ) {

		// Declare ID of Post to be updated
		$PC_post['ID'] = $this->PC_current_post_id;

		// Declare Content/Parameters of Post to be updated
		if ( isset($this->PC_title ) && $this->PC_title !== $this->PC_current_post->post_title ) {
			$PC_post['post_title'] = $this->PC_title;

		if ( isset( $this->PC_type ) && $this->PC_type !== $this->PC_current_post->post_type ) {
			$PC_post['post_type'] = $this->PC_type;

		if ( isset($this->PC_auth_id) && $this->PC_auth_id !== $this->PC_current_post->post_type ) {
			$PC_post['post_author'] = $this->PC_auth_id;

		if ( isset($this->PC_status) && $this->PC_status !== $this->PC_current_post->post_status ) {
			$PC_post['post_status'] = $this->PC_status;

		if ( isset($this->PC_category) && $this->PC_category !== $this->PC_current_post->post_category ) {
			$PC_post['post_category'] = $this->PC_category;

		if ( isset($this->PC_template )
			&& $this->PC_template !== $this->PC_current_post->page_template
			&& ( $PC_post['post_type'] == 'page' || $this->PC_current_post->post_type == 'page' )
		) {
			PC_post['page_template'] = $this->PC_template;

		if ( isset( $this->PC_slug ) && $this->PC_slug !== $this->PC_current_post->post_name ) {
			$args = array('name' => $this->PC_slug);

		if ( ! get_posts( $args ) && !get_page_by_path( $this->PC_slug ) ) {
			$PC_post['post_name'] = $this->PC_slug;
		} else {
			$errors[] = 'Slug Defined is Not Unique';

		if ( isset($this->PC_content) && $this->PC_content !== $this->PC_status->post_content ) {
			$PC_post['post_content'] = $this->PC_content;

		wp_update_post( $PC_post );


	return( $errors );

I’m not going to lie, this bit is really quite easy. Essentially, the update function checks to see if any of the 8 post parameters have been set. If so, it creates and populates an array which is compatible with the wp_update_post(). It runs wp_update_post() and hey presto! Job well done. For efficiency we will add a simple check before assigning each variable. It is only updated if it has changed. Although I am a bit biased, I have to admit that that is beautiful code!

The set values are compared against the values returned by WordPress itself. Only a simple comparison is required then. You will notice that the template and slug IF statements have a little bit more validation than the others. Although we have already included validation to filter out errors for these, there is no harm in double checking! The page template check (it checks to see if the post type is set to page) is simply because trying to set a page template for a non-post is a bug bear of mine. Sorry about the overkill! The slug check on the other hand is an important one! You REALLY don’t want to have slug conflicts. In this case, there is no such thing as overkill. That is pretty much it for update(). Finally, we have some useful general functions.

Additional Functions

Now that the bulk of the class has been written, it’s always a good idea to add in a couple of useful/time saving functions. Have a look;

// General functions
public function get_content() {

	if ( isset( $this->PC_current_post->post_content ) ) {
		return $this->PC_current_post->post_content;


public function get_var( $name ) {
	$name = 'PC_'.$name;
	if ( isset( $this->$name ) ) {
		return $this->$name;

public function unset_all() {
	foreach ( get_class_vars(get_class($this)) as $name => $default ) {
		$this->$name = $default;

public function __toString() {
	return 'Use the PrettyPrint function to return the contents of this Object. E.g;<pre>$my_post->PrettyPrintAll();</pre>';

public function PrettyPrint($data) {
	echo "<pre>";
	print_r( $data );
	echo "</pre>";

public function PrettyPrintAll() {
	echo "<pre>";
	print_r( $this );
	echo "</pre>";


Quick Explanations;

  • get_content() : This function returns the content of the post you have searched for (this is provided by WordPress and not set by you). This doesn’t accept any parameters.
  • get_var( ‘var_name’ ) : Sometimes it can get confusing when you have set a lot of data. This accepts one parameter: a string of the variable name you want. Here is a list of the available ‘var_name’s;
  • ‘title’ – The Post Title you have set.
  • ‘type’ – The Post Type you have set.
  • ‘content’ – The Content you have set.
  • ‘category’ – The Categories you have set (returns array).
  • ‘template’ – The Page Template you have set.
  • ‘slug’ – The Slug you have set.
  • ‘auth_id’ – The Author ID you have set.
  • ‘status’ – The Post Status you have set.
  • ‘current_post’ – The WP Post Object returned after searching.
  • ‘current_post_id’ – The WP Post ID returned after searching.
  • ‘current_post_permalink’ – The WP Post’s permalink returned after searching.
  • ‘errors’ – The Errors Array (very useful).
  • unset_all() : No parameters. One job. Deletes all data stored in the instance of the class. Beware.
  • __toString() : This is what is run when the instance of the class itself is printed. Error message printed. Use Use PrettyPrintAll()
// Start new class
$Poster = new PostController();

// Echoes error message
echo $Poster;
  • PrettyPrint() : This is just a helpful function which displays objects and arrays nicely in ‘pre’ tags. Takes one parameter: the data to be ‘PrettyPrinted’.
  • PrettyPrintAll() : This is the same as PrettyPrint(), except ‘PrettyPrints’ the whole instance of the class (including all variables).

And that’s a wrap!

Thank you for reading my article: I hope you have found it both useful and enlightening!

You can find the latest version of the PostController Class on Github: PostController. If you have enjoyed this tutorial, please share! (A mention would go far amiss either: @harribellthomas 😉 ). If you have any questions, leave them in the comments section below.

Subscribe to the Newsletter

Get our latest news, tutorials, guides, tips & deals delivered to your inbox.


  1. Alexander James

    Wow! Great article – very insightful! Definitely have to try this out.
    Good job!

  2. Anthony

    Can you explain where the code is meant to be go? I’m getting this fatal error: “Class ‘WP_Rewrite’ not found” when I paste in class.postcontroller.php

    • Harri Bell-Thomas

      Hello Anthony,

      You just need to put this code before you call the PostController class.

      As for the WP_Rewrite error (which I have seen many, many times while developing this), can you check whether you have the following 2 lines at the beginning of the PostController script? (If you copied the code from Github you should, but I seem to have omitted them from this article).

      if (!isset($wp_rewrite))
      $wp_rewrite = new WP_Rewrite();

      If you don’t have these lines, hopefully this will fix this error (PHP is simply complaining about how WP_Rewrite hasn’t been instantiated before use). If you do, or it still doesn’t work, just shoot me quick line and I will happily look into this more for you (Also, if you do have these lines, try removing them and re-running to see what error message you get).


    • Harri Bell-Thomas

      Just wondering, are you sure that your script includes the WordPress header? Are you able to access other WordPress functions? This is another thing I have found causes this kind of error.

  3. joemillano

    Hi Harri ,

    I figured out how to use the script and works like a charm..However I need to post tags… Any ideas on how to do that?


  4. joemillano

    Actually figured it out , thanks!

  5. James

    Works great! just struggeling how to add a custom post type to it. Everyone has a SEO plugin like yoast etc it would be awsome to know how to add functions to add support for filling in stuff like meta and so on

  6. Oluwatumbi

    Please may I ask, what is the best “single” include file to include if I am writing a stand-alone php file just to create a post by sending an http post request to the file directly. I just wonder which of the wordpress files should i just include and then include the class.postcontroller.php. Thanks for your help in advance

  7. CavemanUK

    Hi, just discovered this post and will be giving it a go. I cant see anything about credentials. How does it manage security and who is allowed to post. I wanted to add this to a php site i have so i can post from my site directly to my wordpress site.

  8. bbmax


    Always get this error : Fatal error: Call to undefined function get_posts() … can you please specify where we have to put the downloaded files ?


    • AJ Clarke

      Your code should be added inside a WordPress theme or plugin.

      • aja

        Whats the mean inside WordPress theme or plugin? (make manual plugin or etc)

        • AJ Clarke

          Depends on the project. If you are making a custom theme for yourself or a client then you can place it inside the custom theme. If you are adding to a website that already exists and is using a 3rd party plugin that it’s best to create a custom plugin for this.

  9. Replayers

    I think there’s an error where it says: “The data parameter will accept either a string (if searching by title or slug) or an integer (if searching by title).” it should be “The data parameter will accept either a string (if searching by title or slug) or an integer (if searching by id).”

    Could be wrong but the ID’s are integers.

    • Kyla

      You are correct, thanks for the heads up!

Leave a Reply

Your email address will not be published. Required fields are marked *

Learn how your comment data is processed by viewing our privacy policy here.