You can totally create a category, get its ID from URL or database by looking into it. Then create a function, use $wpdb, write the MySQL query as you see fit adding the ID that you just got from URL or database.
Here may be some information to help you: http://codex.wordpress.org/Class_Reference/wpdb
There are 3 or 4 tables that you want to look on your database:
wp_posts - looking for ID and post_type columns;
wp_terms - looking for term_id and name, to see which category's ID is your new one, 'recipe' on this case.
wp_term_taxonomy - looking for the term_id and the taxonomy, just to make sure you'll use a category.
and where all the magic happens:
wp_terms_relationship - where object_id, is your Post's ID, and term_taxonomy_id is the term_taxonomy_id from wp_term_taxonomy.
Careful: Do not assume that the term_id from wp_terms is the same term_taxonomy_id from wp_term_taxonomy. There is a column for each of these and it surely has a good reason for that.
What you want to do is insert here, wp_posts.object_id and wp_term_taxonomy.term_taxonomy_id.
You can run something like:
INSERT INTO wp_term_relationships (object_id, term_taxonomy_id) (SELECT DISTINCT ID, your_new_category_id FROM wp_posts WHERE post_type = "your_post_type")
Replacing:
your_new_category_id = by your new category term_taxonomy_id, from wp_term_taxonomy
your_post_type = between those quotes, the post_type, if wordpress defaults, replace by "post"
Eg, assuming your new category has taxonomy_id 7 and your posts are recipe_post:
INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id) (SELECT DISTINCT ID, 7 FROM wp_posts WHERE post_type = "recipe_post")
"Yeah dude, but OP wants to run something in functions.php".
That's ok:
function update_categories(){
global $wpdb;
if ($_GET['updateall'] == 'banana'){
echo "updating";
$wpdb->query("INSERT IGNORE INTO wp_term_relationships (object_id, term_taxonomy_id) (SELECT DISTINCT ID, 7 FROM wp_posts WHERE post_type = 'recipe_post')");
}
}
add_action('wp_loaded', 'update_categories');
This will hook at everytime WordPress is loaded (usually each page), checking if you're sending a GETvalue equals to something that only you want to run, and want to run it once only. So replace 'banana' with something you see easy to remember and access: http://www.yourdomain.com/?updateall=banana
and done !
Of course, remove that hook and the function as soon as you end this update.
Your questions:
Will this method add the category "recipe" to both new and old posts, or just new posts?
Your call, if you want to select a range of posts, you may add more conditionals to WHERE like:
WHERE post_type = 'recipe_post' AND something = 'something something' AND date = 'some-good-date'
Also, if I already have some posts in the category "recipe" will this method add the "recipe" category TWICE to the post, therefore causing this post to appear twice in the category "recipe" ?
No, that's why IGNORE keyword is right after INSERT.