[wp-hackers] Taxonomy Mirrored Posts Best Practice?
The WordPress Web Warlock
wordpress at web-warlocks.net
Tue Aug 3 19:41:23 UTC 2010
En/na Michael Pretty ha escrit:
> We have a couple more projects lined up where we need a many to many
> relationship between objects and have decided that for us the best
> option is to bite the bullet and create a new table. (...) I haven't
> yet worked out all the details of how it is going to work other than a
> table that stores parent_id, child_id and relationship_type, with lots
> of caching. At this point it just seems like it is going to be the
> only way to provide the many to many relationship that is both stable
> and provides decent performance.
Hello,
If I didn't misunderstand, this resembles a feature I've developed and
under progress/improvement/completion; I called those "description
posts". It's just a name, it doesn't imply any specific custom post type.
A description post links to its term template —not the single.php for
the post itself—, and are mostly used as extended term descriptions.
They may be listed in regular term templates, along "regular" posts, and
you can have more than a description post per term (so, for instance,
different authors can give a definition for a term).
Instead of storing what in a graph would be the link, that is, the
relationship itself, I'm using and storing one or both possible
directions of that relationship (in separate places/tables), from which
the relationship itself can be built if needed. I'll elaborate further.
I needed access from both ways. I would need quick access *from the post
side*, to know which term(s) the post pointed to, to f.i. get the link;
as I told, they were meant to link to a term. And I'd need, too, good
access from the *term side*; as I said, I was using them as term
descriptions, so there should be an easy way to retrieve the description
posts in the template (I use a wrapper for get_posts). I had no visible
need, though, for "the relationship itself" (the relationship regardless
of the point of origin, so to speak), so I priorized the "exits" of each
"origin point" above the relationship data, which could be built from
both origin points.
So, on the one hand, there is a special meta box for the postmeta. There
are several different (non-unique) keys, for the different taxonomies.
There is _query_cat , _query_post_tag, _query_{taxonomy-slug}, &c,
mimicking the args for WP_Query... In practice, all those posts point
just to one category, though, because I don't use taxonomies, but a
custom replacement. And, though only a value is used, this meta box and
keys may have other uses; that's why they're non-unique. When updated,
the meta box save function does not only update (or delete) the
postmeta, but also the data in the other table, unless it's post_type
Page (I'll explain why later). I use it with a function that returns a
single array of term_id and taxonomy, scanning the relevant postmeta
keys in a given order. With a bit more of code, I think it'd be able to
return many arrays of term_id and taxonomy (say, just a $single flag?),
and filter the order of the keys; I haven't had need for that yet, but I
have some theorical use cases: for instance, a partly-predefined search
results Page template (which also would be a good use case for multiple
values per key; say, you're pre-querying some "content categories" with
category__and, like "all movie reviews written in English": "movie
reviews" and "written in English" would be the categories).
On the other hand, the direction/relationship *from the term* is also
stored in another table, a termmeta table, with the same typical
meta-table structure (it maps to term_id, not to term_taxonomy_id). I
was using it already for a lot of things, had all the API for it already
written, and it behaves much like wp_options (loads once and caches a
lot of data). So I suppose you could store this "term side" in
{wp_}options... after all, the term hierarchy is stored there (to
mention an example of "termmeta" stored in wp_options). As in the meta
box, when the term is updated with the edit form, the postmeta is
updated, too. (Right now, though, it only allows for deletion, so
updates have to be made from the post side. As I told, it's still under
improvement :) ).
In any of the save functions (hooked to save_post or edit_term_meta) you
can do whatever filters and actions you might need (like adding the
termmeta when a new postmeta is added, &c). Also, it's quite possible
that this technique requiers no additional queries, for all the data is
previously cached somehow (by postmeta and options or, in my case,
termmeta tables). You're dealing (mostly) with core data (term_id and
post_id, not some, say, "custom_relationship_id"), so it's easy to
integrate that with the API, namely, get_posts where post__in is
termmeta for $term_id, and get_term_link for the according postmeta of
$post_id. Or query_posts( array( 'category__and' => get_post_meta(
$post->ID, '_query_cat' , true ) ) );. Etc.
I'd suggest you that, since you're already adding a table, make it
wp_termmeta and have it available for other term-related uses. For
instance, if you're using custom post types as "a regular post with
added custom fields", is better IMHO to make the custom fields
term-based, not post-type based, because a post can belong to several
terms, but can only be of a single post type. (These "custom fields by
term" are obviously "termmeta" data. And a darnly useful one, I might
add, "piling up" all the custom fields for all the terms). Also, that
support and APIs you make (say, get_term_meta, get_post_as_term_link...)
will be available for future projects —harder to do with the
often-too-specific post-type-specific code.
I have included this feature in a termmeta plugin (which does the other
things I said I was already doing), not yet released, but you can check
it out at my domain; all posts in wordpress/descriptions are like that
(WordPress/Contents/Descriptions, if you're browsing from the menu at
the right; for search and SEO vs. navigation/browsing purposes, the
paths/permalinks aren't the same as the category structure, which has
lots of indexs: see below about linking terms to Pages). In other
(localhost) sites, there is more than a single "description" category,
there are "descriptions of X"; most possibly I'll break down the
"description" category in my domain, and have "function reference",
"plugin reference" & so. If you're interested in the code, drop me a
mail or just ask here.
There's still more to it (tangentially related, at least).
I have category.php doubling also as a Page template; another of the
termmeta features is to link a term to a Page, and if it's linked, the
get_term_link function returns —via hook— the permalink to that Page,
not the "default" permalink to the term. Obviously, the Page must "know"
somehow which term is supposed to show, and it uses the same meta fields
and meta boxes that the "description posts" use. But this relationship
is one-way only —so, in my approach, one-way-only relationships are not
a bug, but a feature.
So, you can have actually a somewhat complicated-to-make-it-easy
relationships path: The "description post" links to a term, which "is"
actually a Page (representing that term and using the term template),
which in turn includes the description posts (because they're the
extended description for that term), besides the actual Page content.
So, both posts of both post types (post and Page) represent somehow the
same term, but in different ways. That is, the *direction* of the
relationship is strongly relevant, and this is really a big conceptual
difference with your approach as I understood it: it looked to me like
your relationships would be always two-way, and the direction depended
on the point where you were retrieving the relationship from: if you're
looking at the A-B relationship from A, the direction would be
necessarily "to B" and vice versa. I'm doing it the opposite way: after
checking the "exit paths" from A, and getting B as exit path, the
relationship is bidirectional if B also points to A, one-way if otherwise.
Variations of this behavior are supposed to be implemented via the
different hooks (get_term_meta, update_term_meta, some in the meta
boxes, &c). So, quite in the good ol' WordPress style, add the
additional data as wp_{whatever}meta, do it some way, and allow lots of
hooks and filters to override it and do it differently :).
More information about the wp-hackers
mailing list