WordPress as Framework for Web Apps – Building a simple Recipe Database

In the past WordPress has grown from a powerful blog-system to a powerful CMS and, more recently, to a powerful framework for web applications. In fact, using WordPress customization features and the WordPress API, there are not many applications I can think of, you couldn’t build with WordPress as framework. However, there are a lot of applications you shouldn’t build with WordPress. Choosing which framework to use (or choosing not to use one) is balancing between many pros and cons affecting architecture, performance, security, scalability and so on.

I found WordPress extremely useful as base for medium sized web apps, without too much traffic. Building a small application, for example a restaurant table booking system, from scratch or even with a framework like Rails or CakePHP, would involve thinking about database scheme, controller structure, authentication, user interfaces etc. A lot of this stuff WordPress is already doing: you already have rough user management, a working admin interface and you only have to think about how to map your data-model to the already existing WordPress database structure.

Our use case: A recipe database

Now, I want to show you how to implement a simple recipe database with WordPress as framework. Requirements are really basic:

  1. Allows adding recipes and editing them just like ordinary posts or pages
  2. Allows categorizing recipes in hierarchical categories like Healthy -> Chicken -> Marinated Chicken Breasts
  3. Allows adding ingredients to a recipe and finding recipes by ingredients
  4. Allows adding quantities to ingredients of a recipe, e.g. 500ml milk, 20g sugar, 3 tablespoons olive oil
I will focus on how to customize WordPress to adjust it to your data model, mainly by showing you how to use the essentials to build every WordPress powered web app:
  • Custom post types
  • Custom taxonomies
  • Custom fields

Setup custom post type for recipes

In WordPress the base of every content holding entity is a post. The two post types you surely are familiar with are posts and pages. For our recipe database we will create a new type: recipes.

register_post_type('recdb_recipe',
	array(
		'labels' => array(
			'name' => 'Recipes',
		),
		'public' => true,
		'has_archive' => true,
		'supports' => array('title','editor','custom-fields')
	)
);

The new post type will show up in the admin interface along with posts and pages. It supplies a field for the recipe title and a editor field which is meant for the recipes description part. We even added support for custom fields, which will be necessary later on.

Setup custom taxonomies for recipe categories and ingredients

In WordPress, taxonomies are not for holding content, like posts, they are for organizing it. You should be used to the two default taxonomies: categories (hierarchical) and tags (not hierarchical). However, like with post types, you can add your own kinds of taxonomies. For our recipes database you could use categories and tags, but they are meant to be used for posts really, so we create our own recipe categories and link them to the recipe post type:

register_taxonomy(
	'recdb_categories',
	array('recdb_recipe'),
	array(
		'label' => 'Recipe categories',
		'sort' => true,
		'rewrite' => array('slug' => 'recipe-categories'),
		'hierarchical' => true,
		'show_in_nav_menus' => true
	)
);

In the admin interface they will show up under the recipes-menu and furthermore there will be a checkbox-list on every recipe to sort it into categories. The same way we want it with ingredients, but with a slight difference: they don’t have to be hierarchical, because every recipe will have just a plain list of ingredients. Here is how to create an ingredients-taxonomy, that will work just like ordinary tags. You can manage it from your admin interface and add it to any recipe:

register_taxonomy(
	'recdb_categories',
	array('recdb_recipe'),
	array(
		'label' => 'Recipe categories',
		'sort' => true,
		'rewrite' => array('slug' => 'recipe-categories'),
		'hierarchical' => true,
		'show_in_nav_menus' => true
	)
);

Setup custom fields for ingredient units

For now we can create new recipes, categorize them and add ingredients. The last missing feature is to add some sort of quantities to the ingredients, because the information that you will need milk, while cooking your dinner, is useless if you don’t know how much. Here are custom fields coming into the picture. For all ingredient-tags you are adding to a recipe, you have to add a custom filed as well. The name of the field is always quantities, that will allow us to read them as an array later on. The value has to be formatted like this: taxonomy-term-slug:string. Here some concrete examples for the custom fields on our recipes:

  1. quantities -> water:100ml
  2. quantities -> milk:200ml
  3. quantities -> sugar:20g

Embedding recipes in your theme

I am assuming you have a working theme and now want to add special templates for recipes. Begin with adding recipe-categories to your menus. Are you using custom menus, they will show up in the menu editor just like ordinary categories (you might have to activate them in your screen options panel). Now you should be able to navigate to the added recipe categories on your website. The containing recipes getting displayed with your index.php or another matching template.

The next step is to build a custom loop for recipes. We will generally build quite an ordinary loop, including the_title and the_content template tags. Building the ingredients list is a bit special, though. For this, we first have to get the quantities meta-value, using get_post_meta. Then we are looping through the quantities, reading both the quantity and the ingredient and finally we provide an ingredient link, which leads to a site listing recipes with the same ingredient. Create a file called loop-recipes.php in your template directory and add the following loop:

<?php if (have_posts()): while (have_posts()): the_post(); ?>
	<div class="recipe">
		<h2><?php the_title(); ?></h2>
		<ul class='ingredients'>
			<?php
				if (is_single()):
					$quantities = get_post_meta(get_the_ID(),'quantities');
					foreach ($quantities as $quantity):
						$quantity = explode(':',$quantity); ?>
						<li>
							<?php echo $quantity[1]; ?> <a href='<?php echo get_term_link($quantity[0],'recdb_ingredients'); ?>'>
								<?php echo $quantity[0]; ?>
							</a>
						</li><?php
					endforeach;
				endif;
			?>
		</ul>
		<?php
			if (is_single()):
				the_content();
			else:
				the_excerpt();
				?><a href="<?php the_permalink(); ?>">Read recipe</a><?php
			endif; ?>
	</div>
<?php endwhile; endif; ?>

Now it is only left to embed the loop in the template files for recdb_categories and recdb_ingredients and adding a template to show a full recipe. Create three files called taxonomy-recdb_categories.phptaxonomy-recdb_ingredients.php and single-recdb_recipe.php in your template directory. The following codes goes into the category-template:

<?php get_header(); ?>
<h1>Category: <?php single_cat_title(); ?></h1>
<p>List of recipes in category <?php single_cat_title(); ?></p>
<?php get_template_part('loop','recipes'); ?>
<?php get_footer(); ?>

Here the code for the ingredients-template:

<?php get_header(); ?>
<h1><?php single_cat_title(); ?></h1>
<p>List of recipes, which use <?php single_cat_title(); ?></p>
<?php get_template_part('loop','recipes'); ?>
<?php get_footer(); ?>

And finally the single recipe template:

<?php get_header(); ?>
<?php get_template_part('loop','recipes'); ?>
<?php get_footer(); ?>

WordPress as framework for web-apps: Conclusion

If you followed all the steps, you should have a really basic, but working recipe database running. Of course, the way to add ingredients is really horrifying, from a users point of view, but this tutorial isn’t about usability. It is a starting point for one who wants to take WordPress as framework for web apps that aren’t blogs or ordinary websites. The vital point, I think, is to really understand the WordPress customization abilities and learn to map them to your own use-case.

Surely there are quite some drawbacks using WordPress for your web-app and you shouldn’t ignore them. However, once you have decided to go the WordPress way, you get a lot of cool features to defeat the disadvantages.