Skip to content

Instantly share code, notes, and snippets.

@chrismeller
Created October 16, 2010 22:01
Show Gist options
  • Save chrismeller/630321 to your computer and use it in GitHub Desktop.
Save chrismeller/630321 to your computer and use it in GitHub Desktop.
Habari Taxonomy Structure Example
<?php
class Term extends QueryRecord {
protected $vocabulary; // set by child classes to indicate which vocabulary your term relates to
// these two would be emulated by __get() and __set() so that they're actually stored in $this->fields
// for QueryRecord to work but this would be the public API
public $term; // the slugified version of the term name
public $display; // the raw term name for display
// these wrap around their QueryRecord parents
function insert () {}
function delete () {}
function update () {}
function attach_to_object ( $object_id, $object_type ) {
// attach $this->id (no need to pass in one) to the object of $object_type with id $object_id
// $object_type would default to the type of object a vocabulary is set to link against, if it is limited to a particular type
// otherwise, presumably it would default to 'post' for convenience
}
function detach_from_object ( $object_id, $object_type ) {}
function rename ( $new_name ) {
// rename $this->term to $new_name. should handle making sure it's available in the given $vocabulary
// as well as merging it with an existing tag of the same name
// just like deleting terms (see note in Vocabulary), you do this on a single term, not on a group of terms
// which means it goes in Term, not Vocabulary
}
function next () {
// get the next Term in the tree, if Vocabulary is heirarchical
// similar methods for working up and down the tree, getting children, etc. would be needed
}
// note that there is no get() method. you don't directly fetch a term, you fetch terms from a vocabulary
}
class Vocabulary extends QueryRecord {
protected $term = 'Term'; // overridden by child classes to indicate which class we use to represent terms. ie: which class should Vocabulary tell PDO to return results as?
protected $vocabulary; // set by child classes to indicate the name of this vocabulary (matches up with Term::$vocabulary)
// these wrap around their QueryRecord parents
function insert () {}
function delete () {}
function update () {}
static function create ( $params = array() ) {
// a factory method that returns a newly-created Vocabulary object after calling ->insert() on it
// this would really only be useful if you're creating some kind of dynamicly named vocabulary and can't create
// a separate class that extends Vocabulary like you normally would
}
function get_terms ( $params = array() ) {
// works the same as Posts::get(), parsing out the parameters from the array
// and returning all matching terms for vocabulary $this->vocabulary (so you don't have to
// pass in 'vocabulary' => 'tags', like you do 'content_type' => 'entry' for Posts calls)
// this would replace get_all(), get_by_id(), get_by_name(), etc.
// we would then return a simple array of $term objects - either the generic Term class or any other class specified in self::$term
// since we only return an array(), which is really all that the current Tags class is once its methods are moved into
// this generic Vocabulary class, there's no need for a "container" class anymore.
// $post->tags is an array, not a Tags object emulating an array
}
// note there are no other term-modifying methods (like delete_term()).
// you get terms and perform operations on individual term objects instead, just like we do with posts
// we would also add handling for the different vocabulary features (heirarchical, etc.) in here,
// as well as getting and managing the tree of terms, if applicable
// also note there is not necessarily an $object_type parameter. a vocabulary could be linked to any type of object
// so limiting it always to a single type is not appropriate. that's why you always pass an $object_type to Term::attach_to_object().
// there should, however, be the option to specifically link a vocabulary to a single type of object
}
// now we set up our default Tags structure!
class Tag extends Term {
protected $vocabulary = 'tags';
// that's it, there is nothing special about a Tag object, it behaves just like any other term,
// so all the methods for dealing with tags are actually in Term
}
class Tags extends Vocabulary {
protected $term = 'Tag'; // our Terms are the Tag class, return that type of object for all terms
protected $vocabulary = 'tags'; // the name of our vocabulary is 'tags' - linked with Tag::$vocabulary
// again, there is nothing special about tags, any functions we might need or actions to be performed on them are in
// the base Vocabulary class. getting tags works just like getting any other type of term, no modifications are
// required to Vocabulary::get_terms().
}
// and finally, examples of using the classes
// in the Post class, you load tags for the post:
$post->tags = Tags::get_terms( array( 'object_type' => 'post', 'object_id' => $post->id ) );
// $posts->tags would be set to array( $tag1 instanceOf Tag, $tag2 instanceOf Tag, ... )
// a better structure, however, would be more generic. every time a post is loaded, we would retrieve all its related
// terms, regardless of vocabulary, and set $post->vocabulary->%vocabulary_name% = array( %vocabulary_terms% )
// so instead of $post->tags we would access them at $post->vocabulary->tags instead.
// delete the 'foo' tag entirely, including associations with any objects
$tag = Tags::get_terms( array( 'name' => 'foo' ) );
// similar to Posts::get(), if it's a single tag i would return simply a single tag object, not an array with a single entry
$tag->delete();
// boom, it's gone and works just like deleting a post
// say i'm dynamically determining the name of a vocabulary - maybe i let the user create any type of vocabulary they want
// in that case i don't know the name of the vocabulary and can't create a class to extend Vocabulary. no big deal, it's convenient
// but not required:
$tags = Vocabulary::create( array( 'vocabulary' => 'tags' ) );
// the vocabulary is created because create() calls ->insert() before it returns the Vocabulary object
// from now on i can get the vocabulary:
$tags = Vocabulary::get( array( 'vocabulary' => 'tags' ) );
// or:
$tags = new Vocabulary( array( 'vocabulary' => 'tags' ) );
// and get some terms from it:
$terms = $tags->get_terms( array( 'name' => 'foo' ) );
// or all of them for a given post:
$terms = $tags->get_terms( array( 'object_id' => 123, 'object_type' => 'post' ) );
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment