diff --git a/wp-admin/admin-db.php b/wp-admin/admin-db.php index 6e1a238ea..8bebb4fc0 100644 --- a/wp-admin/admin-db.php +++ b/wp-admin/admin-db.php @@ -282,27 +282,14 @@ function category_exists($cat_name) { } function tag_exists($tag_name) { - global $wpdb; - if (! $tag_nicename = sanitize_title($tag_name)) - return 0; - - return (int) $wpdb->get_var("SELECT cat_ID FROM $wpdb->categories WHERE category_nicename = '$tag_nicename' AND ( type & " . TAXONOMY_TAG . " != 0 )"); + return is_term($tag_name, 'post_tag'); } function wp_create_tag($tag_name) { if ( $id = tag_exists($tag_name) ) return $id; - $tag_array = array('cat_name' => $tag_name, 'type' => TAXONOMY_TAG); - if ( $id = category_object_exists($tag_name) ) { - $category = get_category($id); - $tag_array['type'] = $category->type | $tag_array['type']; - $tag_array['cat_ID'] = $id; - $id = wp_update_category($tag_array); - return $id; - } else { - return wp_insert_category($tag_array); - } + $tag_id = add_term($tag_name, 'post_tag'); } function wp_delete_user($id, $reassign = 'novalue') { diff --git a/wp-admin/admin-functions.php b/wp-admin/admin-functions.php index 973aff999..f3e849d2b 100644 --- a/wp-admin/admin-functions.php +++ b/wp-admin/admin-functions.php @@ -664,16 +664,13 @@ function get_tags_to_edit( $post_id ) { if ( !$post_id ) return false; - $tags = $wpdb->get_results( " - SELECT category_id, cat_name - FROM $wpdb->categories, $wpdb->post2cat - WHERE $wpdb->post2cat.category_id = cat_ID AND $wpdb->post2cat.post_id = '$post_id' AND rel_type = 'tag' - " ); + $tags = wp_get_post_tags($post_id); + if ( !$tags ) return false; foreach ( $tags as $tag ) - $tag_names[] = $tag->cat_name; + $tag_names[] = $tag->name; $tags_to_edit = join( ', ', $tag_names ); $tags_to_edit = attribute_escape( $tags_to_edit ); $tags_to_edit = apply_filters( 'tags_to_edit', $tags_to_edit ); diff --git a/wp-admin/upgrade-schema.php b/wp-admin/upgrade-schema.php index d3b4c6dc9..771f0845b 100644 --- a/wp-admin/upgrade-schema.php +++ b/wp-admin/upgrade-schema.php @@ -10,20 +10,29 @@ if ( version_compare(mysql_get_server_info(), '4.1.0', '>=') ) { $charset_collate .= " COLLATE $wpdb->collate"; } -$wp_queries="CREATE TABLE $wpdb->categories ( - cat_ID bigint(20) NOT NULL auto_increment, - cat_name varchar(55) NOT NULL default '', - category_nicename varchar(200) NOT NULL default '', - category_description longtext NOT NULL, - category_parent bigint(20) NOT NULL default '0', - category_count bigint(20) NOT NULL default '0', - link_count bigint(20) NOT NULL default '0', - tag_count bigint(20) NOT NULL default '0', - posts_private tinyint(1) NOT NULL default '0', - links_private tinyint(1) NOT NULL default '0', - type tinyint NOT NULL default '1', - PRIMARY KEY (cat_ID), - KEY category_nicename (category_nicename) +$wp_queries="CREATE TABLE $wpdb->terms ( + term_id bigint(20) NOT NULL auto_increment, + name varchar(55) NOT NULL default '', + slug varchar(200) NOT NULL default '', + term_group bigint(10) NOT NULL default 0, + PRIMARY KEY (term_id), + UNIQUE KEY slug (slug) +) $charset_collate; +CREATE TABLE $wpdb->term_taxonomy ( + term_taxonomy_id bigint(20) NOT NULL auto_increment, + term_id bigint(20) NOT NULL default 0, + taxonomy varchar(32) NOT NULL default '', + description longtext NOT NULL, + parent bigint(20) NOT NULL default 0, + count bigint(20) NOT NULL default 0, + PRIMARY KEY (term_taxonomy_id), + UNIQUE KEY (term_id, taxonomy) +) $charset_collate; +CREATE TABLE $wpdb->term_relationships ( + object_id bigint(20) NOT NULL default 0, + term_taxonomy_id bigint(20) NOT NULL default 0, + PRIMARY KEY (object_id), + KEY (term_taxonomy_id) ) $charset_collate; CREATE TABLE $wpdb->comments ( comment_ID bigint(20) unsigned NOT NULL auto_increment, @@ -45,13 +54,6 @@ CREATE TABLE $wpdb->comments ( KEY comment_approved (comment_approved), KEY comment_post_ID (comment_post_ID) ) $charset_collate; -CREATE TABLE $wpdb->link2cat ( - rel_id bigint(20) NOT NULL auto_increment, - link_id bigint(20) NOT NULL default '0', - category_id bigint(20) NOT NULL default '0', - PRIMARY KEY (rel_id), - KEY link_id (link_id,category_id) -) $charset_collate; CREATE TABLE $wpdb->links ( link_id bigint(20) NOT NULL auto_increment, link_url varchar(255) NOT NULL default '', @@ -86,14 +88,6 @@ CREATE TABLE $wpdb->options ( PRIMARY KEY (option_id,blog_id,option_name), KEY option_name (option_name) ) $charset_collate; -CREATE TABLE $wpdb->post2cat ( - rel_id bigint(20) NOT NULL auto_increment, - post_id bigint(20) NOT NULL default '0', - category_id bigint(20) NOT NULL default '0', - rel_type varchar(64) NOT NULL default 'category', - PRIMARY KEY (rel_id), - KEY post_id (post_id,category_id) -) $charset_collate; CREATE TABLE $wpdb->postmeta ( meta_id bigint(20) NOT NULL auto_increment, post_id bigint(20) NOT NULL default '0', @@ -404,4 +398,4 @@ function populate_roles_230() { } } -?> \ No newline at end of file +?> diff --git a/wp-includes/post.php b/wp-includes/post.php index f1ed762ea..834414a5f 100644 --- a/wp-includes/post.php +++ b/wp-includes/post.php @@ -459,10 +459,8 @@ function wp_get_post_tags( $post_id = 0 ) { $post_id = (int) $post_id; - if ( !isset( $tag_cache[$blog_id][$post_id] ) ) - update_post_category_cache( $post_id ); // loads $tag_cache - - return $tag_cache[$blog_id][$post_id]; + $tags = get_object_terms($post_id, 'post_tag'); + return $tags; } function wp_get_recent_posts($num = 10) { @@ -792,76 +790,11 @@ function wp_set_post_tags( $post_id = 0, $tags = '', $append = false ) { if ( !$post_id ) return false; - - // prevent warnings for unintialized variables - $tag_ids = array(); if ( empty($tags) ) $tags = array(); $tags = (is_array($tags)) ? $tags : explode( ',', $tags ); - - foreach ( $tags as $tag ) { - $tag = trim( $tag ); - if ( !$tag_slug = sanitize_title( $tag ) ) - continue; // discard - if ( !$tag_id = tag_exists( $tag ) ) - $tag_id = wp_create_tag( $tag ); - $tag_ids[] = $tag_id; - } - - if ( empty($tag_ids) && ( !empty($tags) || $append ) ) - return false; - - $tag_ids = array_unique( $tag_ids ); - - // First the old tags - $old_tags = $wpdb->get_col(" - SELECT category_id - FROM $wpdb->post2cat - WHERE post_id = '$post_id' AND rel_type = 'tag'"); - - if ( !$old_tags ) { - $old_tags = array(); - } else { - $old_tags = array_unique( $old_tags ); - } - - // Delete any? - $delete_tags = array_diff( $old_tags, $tag_ids); - if ( $delete_tags && !$append ) { - foreach ( $delete_tags as $del ) { - $wpdb->query(" - DELETE FROM $wpdb->post2cat - WHERE category_id = '$del' - AND post_id = '$post_id' - AND rel_type = 'tag' - "); - } - } - - // Add any? - $add_tags = array_diff( $tag_ids, $old_tags ); - if ( $add_tags ) { - foreach ( $add_tags as $new_tag ) { - $new_tag = (int) $new_tag; - if ( !empty($new_tag) ) - $wpdb->query(" - INSERT INTO $wpdb->post2cat (post_id, category_id, rel_type) - VALUES ('$post_id', '$new_tag', 'tag')"); - } - } - - // Update category counts. - $all_affected_tags = array_unique( array_merge( $tag_ids, $old_tags ) ); - foreach ( $all_affected_tags as $tag_id ) { - $count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->post2cat, $wpdb->posts WHERE $wpdb->posts.ID=$wpdb->post2cat.post_id AND post_status = 'publish' AND post_type = 'post' AND category_id = '$tag_id' AND rel_type = 'tag'" ); - $wpdb->query( "UPDATE $wpdb->categories SET tag_count = '$count', type = type | " . TAXONOMY_TAG . " WHERE cat_ID = '$tag_id'" ); - if ( $count == 0 ) - $wpdb->query( "UPDATE $wpdb->categories SET type = type & ~". TAXONOMY_TAG . " WHERE cat_ID = '$tag_id'" ); - clean_category_cache( $tag_id ); - do_action( 'edit_category', $tag_id ); - do_action( 'edit_tag', $tag_id ); - } + add_term_relationship($tags, $post_id, 'post_tag'); } function wp_set_post_categories($post_ID = 0, $post_categories = array()) { diff --git a/wp-includes/taxonomy.php b/wp-includes/taxonomy.php new file mode 100644 index 000000000..1bc48fc56 --- /dev/null +++ b/wp-includes/taxonomy.php @@ -0,0 +1,174 @@ + '', 'description' => '', 'parent' => 0); + $args = wp_parse_args($args, $defaults); + extract($args); + + $term_group = 0; + if ( $alias_of ) { + $alias = $wpdb->fetch_row("SELECT term_id, term_group FROM $wpdb->terms WHERE slug = '$alias_of'"); + if ( $alias->term_group ) { + // The alias we want is already in a group, so let's use that one. + $term_group = $alias->term_group; + } else { + // The alias isn't in a group, so let's create a new one and firstly add the alias term to it. + $term_group = $wpdb->get_var("SELECT MAX() term_group FROM $wpdb->terms GROUP BY term_group") + 1; + $wpdb->query("UPDATE $wpdb->terms SET term_group = $term_group WHERE term_id = $alias->term_id"); + } + } + + if ( ! $term_id = is_term($slug) ) { + $wpdb->query("INSERT INTO $wpdb->terms (name, slug, term_group) VALUES ('$term', '$slug', '$term_group')"); + $term_id = (int) $wpdb->insert_id; + } + + $tt_id = $wpdb->get_var("SELECT tt.term_taxonomy_id FROM $wpdb->term_taxonomy AS tt INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = '$taxonomy' AND t.term_id = $term_id"); + + if ( !empty($tt_id) ) + return $term_id; + + $wpdb->query("INSERT INTO $wpdb->term_taxonomy (term_id, taxonomy, description, parent, count) VALUES ('$term_id', '$taxonomy', '$description', '$parent', '0')"); + // TODO: Maybe return both term_id and tt_id. + return $term_id; +} + +/** + * Removes a term from the database. + */ +function remove_term() {} + + +/** + * Returns the index of a defined term, or 0 (false) if the term doesn't exist. + */ +function is_term($term, $taxonomy = '') { + global $wpdb; + if ( ! $term = sanitize_title($term) ) + return 0; + + return $wpdb->get_var("SELECT term_id FROM $wpdb->terms WHERE slug = '$term'"); +} + +/** + * Given an array of terms, returns those that are defined term slugs. Ignores integers. + * @param array $terms The term slugs to check for a definition. + */ +function get_defined_terms($terms) { + global $wpdb; + + foreach ( $terms as $term ) { + if ( !is_int($term) ) + $searches[] = $term; + } + + $terms = "'" . implode("', '", $searches) . "'"; + return $wpdb->get_col("SELECT slug FROM $wpdb->terms WHERE slug IN ($terms)"); +} + +/** + * Relates an object (post, link etc) to a term and taxonomy type. Creates the term and taxonomy + * relationship if it doesn't already exist. Creates a term if it doesn't exist (using the slug). + * @param array|int|string $term The slug or id of the term. + * @param int $object_id The object to relate to. + * @param array|string $taxonomies The context(s) in which to relate the term to the object. + */ +function add_term_relationship($terms, $object_id, $taxonomies) { + global $wpdb; + + if ( !is_array($taxonomies) ) + $taxonomies = array($taxonomies); + + if ( !is_array($terms) ) + $terms = array($terms); + + $defined_terms = get_defined_terms($terms); + + foreach ( $terms as $term ) { + if ( !is_int($term) ) { + if ( !isset($defined_terms[$term]) ) + $new_terms[] = $term; + $slugs[] = $term; + } else { + $term_ids[] = $term; + } + } + + $term_clause = isset($term_ids) ? 'tt.term_id IN (' . implode(', ', $term_ids) . ')' : ''; + if ( isset($slugs) ) { + if ($term_clause) { + $term_clause .= ' OR '; + } + $term_clause .= "t.slug IN ('" . implode("', '", $slugs) . "')"; + $term_join = "INNER JOIN $wpdb->terms AS t ON tt.term_id = t.term_id"; + } else { + $term_join = ''; + } + + // Now add or increment the term taxonomy relationships. This is inefficient at the moment. + foreach ( $taxonomies as $taxonomy ) { + foreach ( $terms as $term ) { + add_term($term, $taxonomy); + } + } + + $taxonomies = "'" . implode("', '", $taxonomies) . "'"; + + // Finally, relate the term and taxonomy to the object. + // Use IGNORE to avoid dupe warnings for now. + $wpdb->query("INSERT IGNORE INTO $wpdb->term_relationships(object_id, term_taxonomy_id) SELECT '$object_id', term_taxonomy_id FROM $wpdb->term_taxonomy AS tt $term_join WHERE ($term_clause) AND tt.taxonomy IN ($taxonomies)"); +} + +/** + * Returns the terms associated with the given object(s), in the supplied taxonomies. + * @param int|array $object_id The id of the object(s)) to retrieve for. + * @param string|array $taxonomies The taxonomies to retrieve terms from. + * @return array The requested term data. + */ +function get_object_terms($object_id, $taxonomy) { + global $wpdb; + $taxonomies = ($single_taxonomy = !is_array($taxonomy)) ? array($taxonomy) : $taxonomy; + $object_ids = ($single_object = !is_array($object_id)) ? array($object_id) : $object_id; + + $taxonomies = "'" . implode("', '", $taxonomies) . "'"; + $object_ids = implode(', ', $object_ids); + + if ( $taxonomy_data = $wpdb->get_results("SELECT t.* FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tr.object_id IN ($object_ids)") ) { + if ($single_taxonomy && $single_object) { + // Just one kind of taxonomy for one object. + return $taxonomy_data; + } else { + foreach ($taxonomy_data as $data) { + if ($single_taxonomy) { + // Many objects, one taxonomy type. + $return[$data->object_id][] = $data; + } elseif ($single_object) { + // One object, many taxonomies. + $return[$data->taxonomy][] = $data; + } else { + // Many objects, many taxonomies. + $return[$data->object_id][$data->taxonomy][] = $data; + } + } + return $return; + } + } else { + return array(); + } +} + +?> \ No newline at end of file diff --git a/wp-includes/version.php b/wp-includes/version.php index 7a0d5d34a..022728edb 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -3,6 +3,6 @@ // This holds the version number in a separate file so we can bump it without cluttering the SVN $wp_version = '2.3-alpha'; -$wp_db_version = 5200; +$wp_db_version = 5495; ?> diff --git a/wp-includes/wp-db.php b/wp-includes/wp-db.php index 39724b300..7eff0fd18 100644 --- a/wp-includes/wp-db.php +++ b/wp-includes/wp-db.php @@ -34,6 +34,9 @@ class wpdb { var $optiongroups; var $optiongroup_options; var $postmeta; + var $terms; + var $term_taxonomy; + var $term_relationships; var $charset; var $collate; diff --git a/wp-settings.php b/wp-settings.php index 0b5b81ec6..30a18ecc7 100644 --- a/wp-settings.php +++ b/wp-settings.php @@ -116,6 +116,9 @@ $wpdb->links = $wpdb->prefix . 'links'; $wpdb->options = $wpdb->prefix . 'options'; $wpdb->postmeta = $wpdb->prefix . 'postmeta'; $wpdb->usermeta = $wpdb->prefix . 'usermeta'; +$wpdb->terms = $wpdb->prefix . 'terms'; +$wpdb->term_taxonomy = $wpdb->prefix . 'term_taxonomy'; +$wpdb->term_relationships = $wpdb->prefix . 'term_relationships'; if ( defined('CUSTOM_USER_TABLE') ) $wpdb->users = CUSTOM_USER_TABLE; @@ -168,6 +171,7 @@ require (ABSPATH . WPINC . '/cron.php'); require (ABSPATH . WPINC . '/version.php'); require (ABSPATH . WPINC . '/deprecated.php'); require (ABSPATH . WPINC . '/script-loader.php'); +require (ABSPATH . WPINC . '/taxonomy.php'); if (strpos($_SERVER['PHP_SELF'], 'install.php') === false) { // Used to guarantee unique hash cookies