jtsternberg / Taxonomy_MetaData

WordPress Helper Class for saving pseudo-metadata for taxonomy terms
54 stars 10 forks source link

Using custom tables instead of options for meta storage (extended class) #23

Open underscorefunk opened 9 years ago

underscorefunk commented 9 years ago

First please let met apologize for if I've put this in the wrong place. I admit that I'm totally unfamiliar with git and community protocol. :(

In any event, I've never been a fan of throwing everything in the options table. I wrote a little extension to allow CMB2 to use custom tables instead of options. Just an idea I had. I've no doubt that there is a more elegant way to do this. Thanks for all of your hard work.

<?php

if ( ! class_exists( 'Taxonomy_MetaData_CMB2_mod' ) ) :

/**
 * Allows custom meta for terms to be stored in taxonomy term meta tables
 * @version 0.0.1
 * @author  John Funk (Underscorefunk Design)
 */

class Taxonomy_MetaData_CMB2_mod extends Taxonomy_MetaData_CMB2 {

    /**
     * Meta type
     * @since  0.0.a
     * @var string
     */
    public $meta_type = '';

    /**
     * Meta table
     * @since  0.0.a
     * @var string
     */
    public $meta_table_name = '';

    public $object_type = '';

    public function __construct( $taxonomy, $metabox, $title = '', $option_callbacks = array() ) {

        $this->meta_type = $taxonomy . '_term';
        $this->object_type = 'taxonomy_metadata_' . $taxonomy;
        parent::__construct( $taxonomy, $metabox, '', $option_callbacks );

        add_action( 'init', array( $this, 'create_meta_table' ) );
        add_filter( 'cmb2_override_meta_value', array( $this, 'override_get_meta_value' ), 10, 4 );
        add_filter( 'cmb2_override_meta_save',  array( $this, 'override_save_meta_value'), 10, 4 );
    }

    /**
     * Filter getting the meta value and extract the ID from params to perform proper get_metadata
     */
    public function override_get_meta_value( $object_id, $args, $object_type, $object ) {
        if ( strpos( $args, $this->object_type ) === 0 ) {
            $term_id = str_replace( $this->object_type . '_', '', $args );
            return get_metadata( $this->meta_type, $term_id, $object_type['_id'], TRUE );
        }
        return $object_id;
    }

    /**
     * Filter saving the meta value and extract the ID from params to perform proper get_metadata
     */
    public function override_save_meta_value( $check, $args, $field_args, $field_obj ) {
        extract($args);
        if ( strpos( $id, $this->object_type ) === 0 ) {
            $term_id = str_replace( $this->object_type . '_', '', $id );
            update_metadata( $this->meta_type, $term_id, $field_id, $value );
        }

        return $check;
    }

    /**
     * Creates a meta table for the custom taxonomy meta type
     * @since  0.0.1
     */
    public function create_meta_table() {
        global $wpdb;

        $this->meta_table_name = $wpdb->prefix . $this->meta_type . 'meta';
        create_metadata_table( $this->meta_table_name, $this->meta_type );
        $variable_name = $this->meta_type . 'meta';
        $wpdb->$variable_name = $this->meta_table_name;
    }

    /**
     * Returns term meta with options to return a subset
     * @since  0.1.3
     * @param  string  $term_id  The term id for the options we're getting
     * @param  string  $key      Term meta key to check
     * @return mixed             Requested value | false
     */
    public function get_meta( $term_id, $key = '' ) {
        if ( ! class_exists( 'CMB2' ) )
            return;

        $this->do_override_filters( $term_id );

        $value = $key
            ? get_metadata( $this->meta_type, $term_id, $key, TRUE )
            : get_metadata( $this->meta_type, $term_id ) ;

        return $value;
    }

}

endif; // end class_exists check

/* eof */
jtsternberg commented 9 years ago

Very cool @underscorefunk. I'll definitely have to try this out sometime soon. Do you think this is vastly better than using the wp-large-options solution?

underscorefunk commented 9 years ago

I always like to keep the options table for site and plugin options exclusively. Otherwise it becomes a junk drawer which makes content hard to find. The reason I like the separate table is that it's easy enough to strip out a taxonomy and dump the table. The visibility of seeing the meta data in its own table creates clear code which makes interfacing with it less reliant on the class or the wp-large-options solution. Though to be honest, I didn't really look at the large-options solution. I think the beauty of CMB is that it acts as an interface layer and augments interaction with WP-core instead of creating more requirements. I wonder if there might be benefits in being able to create more efficient keys, etc, as well. Though you'll have to ask someone more clever about that. I just hacked this together in an evening... mileage may vary. ;)

underscorefunk commented 9 years ago

p.s. Pardon the formatting as well. I didn't familiarize myself with all of the classes and the taxonomy extension so it's a bit of short hand. :/

jtsternberg commented 9 years ago

 I think the beauty of CMB is that it acts as an interface layer and augments interaction with WP-core instead of creating more requirements.

You hit the nail on the head. The goal was to abstract as much as possible so that it can be used at any layer. I like the custom table solution, but if wanting to keep it closer to the 'WordPress Way', the wp-large-options solution is the way to go. If you know exactly what you're doing, and why, then a custom table is a great solution.

I may need to add a wiki to this repo to add your solution as well as better documentation. For now, I'll leave this issue in place. Thanks for your input!

underscorefunk commented 9 years ago

I advise that you test this more thoroughly in the context of your knowledge before anything. At least, I'd feel more comfortable about it if you did. :)

If terms (a taxonomy) have meta data stored in their own table, how is using a totally different process for another term's meta data closer to the WordPress Way? To me it seems less consistent and more obtuse. It also requires further parsing to do anything with the data instead of being able to just match on a key or val with some joins. i suppose the benefit is that the get option can be cached but as can get____metadata. I ask not as a contrarian but out of curiosity and inquiry while embracing my ignorance.

As always, thanks for the speedy responses!

... though I suppose storing all of this as standard meta data in term meta is even more WordPress(ie) than what I proposed. Hindsight eh? lol

jtsternberg commented 9 years ago

Well, the 'WordPress Way' is definitely subjective and a moving target, and I'll stop assuming I know how it's defined. ;) I'm just saying that WLO uses post meta on a custom post type, all WordPress sanctioned APIs. Custom tables have a sordid history. :)

I advise that you test this more thoroughly in the context of your knowledge before anything. At least, I'd feel more comfortable about it if you did. :)

I'd love to, and will do so as soon as I get a chance and/or need it for a project.

underscorefunk commented 9 years ago

Ah yeah, totally hear what you're saying. Perhaps I should have written up something that was just storing these bits as term meta as one would expect... which I guess you could actually do pretty easily. That might even be the most straightforward solution.

jcchavezs commented 9 years ago

If the discussion is not too old I would say that I would prefer using a custom table instead of wp-large-option due to scalability. The options table has enough huge data stored so adding more would not be that elegant it we think that the taxonomies metadata are attributes of the taxonomies and not options of the website and would probably impact in the performance, more over based on the fact that the table options is MyISAM and not INNODB so the table locking is an issue.

jtsternberg commented 9 years ago

@jcchavezs I do think a custom table is a good solution for some circumstances. And to be clear, I don't recommend the default, using the options table. WP Large Options does NOT use the options table. It uses a custom post-type, behind the scenes, which handles the data storage. It is the solution that is recommended/required for WordPress VIP as they do not allow custom tables.

jcchavezs commented 9 years ago

@jtsternberg thanks for your answer. I do not know why I had the concept of WP Large Options using the options table but anyways, using a custom post type sounds weird in the other hand, an approach with a custom table still using the WP API (metadata API which also handles cache). By the way the wp-large-options approach is recommended/required for wordpress.com so maybe one of the reasons is the conflict of interests (as hosts).

underscorefunk commented 9 years ago

I think it comes down to different strokes for different folks. I really like the visibility of the extra table and the simplicity of it and using the metadata api in the way that I do. Mileage may vary.

One thing I should state about the code sketch I provided is that variables left blank don't get posted properly which means that they don't get updated. A field left blank needs to be caught by wp_parse_args. Caveat emptor.

jcchavezs commented 9 years ago

@underscorefunk by the way, maybe you want to check my approach https://github.com/jcchavezs/cmb2-taxonomy

joshuadavidnelson commented 9 years ago

@jcchavezs Thanks for posting this link, I was having an issue with this plugin not working on a production site (though it worked fine on my local dev version) and I switched to yours - works like a charm!

jcchavezs commented 9 years ago

Great @joshuadavidnelson! I am now looking for a way to publish in wordpress.org keeping github as the subversion manager.

jcchavezs commented 9 years ago

By the way, I just added my plugin in the Wordpress Plugin Registry: https://wordpress.org/plugins/cmb2-taxonomy/