[wp-hackers] Storing Arbitrary Data as Custom Post Types

Christopher O'Connell jwriteclub at gmail.com
Tue Dec 8 23:22:04 UTC 2009


I'm going to break this out into it's own thread, to keep threads with some
semblance of one thread per topic:


>
> Now with custom post types most will be using custom fields (though not
> all) but we still have the compatibility problem.  Both plugins use an
> "event" post type but one plugin uses custom fields called "Start Date" and
> "Start Time" and another uses "Event Date" and "Event Time."  Now both
> provide complimentary functionality but because they use incompatible custom
> fields they can't be used together.  That's the problem a shared baseline
> events plugin could solve.
>

Well, I'll go ahead and throw out on the open list something that I've
already discussed with a couple of people individually -- a way of
abstracting away the actual storage of data using something like object
persistence (don't read too much into the word) on top of the posts table.

In essence, I would very much like to be able to simply define a type (the
following example XML is for show only):

<type name="event">
   <field type="date" required="true" display_name="Date">date</field>
   <field type="string" required="true" maps="title" display_name="Event
Title">title</field>
   <field type="text" maps="body" display_name="Event
Information">info</field>
   <field type="email" required="true" display_name="Even
Coordinator">coordinator</field>
</type>

Imagine a WP_Data class that then functions something like this:

<?php

WP_Data::loadDefinition("event.xml"); // Called once, probably where action
hooks are added, etc

$my_event = WP_Data::new("event");
$my_event->data = strtotime("+1 week");
$my_event->title = "My Event";
$my_event->body = "An awesome event";
$my_event->email = "abc.123";
try {
   $my_event->save();
} catch (WP_Data_Exception e) {
   ; // Caught an exception here because 'abc.123' is not a valid email
address
}
$my_event->email = "abc at 123.com";
try {
   $my_event->save(); // Succeed, because everything is groovy
}
?>

(note that to be PHP4 compliant, we could have *->save() return a boolean
instead of throw an error.)

As I invision this, it would create a new post of type "event" with
post_title="My Event", post_body="An awesome event" and meta fields for the
other values.

There's more consideration's, like, how does searching work? (I'd vote for,
e.g. $my_events = WP_Data::search("event",array(date => "some day"));).

For what it's worth, I have much (but not all) of such a class,
appropriately enough called WP_Data: It's at
http://svn.wp-plugins.org/profiles/trunk/wp-data/. Right now it doesn't
actually save data to the DB, but I'll try to fix it up this week and then
release an example built on it.

Some of the problems with this approach include the fact that my current
implementation requires a sort of late dynamic binding of methods, which
makes debugging . . . interesting (I add new methods to classes after they
are instantiated by intercepting an re-routing function calls). On the
upside, such an implementation allows for opcode caching of the generated
types and for additional methods to be inserted into classes at run time.

But why do we need all this? If data is stored this way, then reading
someone else's data is simply a matter of loading the definition originally
used, and it all "just works".

~ Christopher

P.S. As I've discussed with several people, definitions could be saved as
JSON, or whatever other format suits, I just used XML because the parser was
easy to write.

P.P.S. Here's an actual definition that will run with the posted version of
WP_Data:

<?xml version="1.0"?>
<profile>
  <definition>
    <name>People</name>
    <author>Christopher O'Connell</author>
    <url>http://compu.terlicio.us/</url>
    <version>0.1.0</version>
    <signature></signature>
    <signature_url></signature_url>
  </definition>
  <data>
    <data-element type="string" display="First
Name">first_name</data-element>
    <data-element type="string" display="Last Name">last_name</data-element>
    <data-element type="int" required="true" min="0" max="150"
display="Age">age</data-element>
    <data-element type="email" required="true"
display="E-mail">email</data-element>
  </data>
  <access-control>
    <editing-user>Christopher O'Connell</editing-user>
    <anonymous-edits>false</anonymous-edits>
    <user-can-edit-own>false</user-can-edit-own>
  </access-control>
</profile>

You will note that there is no "maps" parameter in this definition, I've not
yet added that to WP_Data, but I will be soon. Also, ignore the
access-control part, I've decided that such fine grained control is probably
beyond the scope of what makes sense, but for the moment, the code still
wants to see the section. Also, at the moment, I don't check signatures, but
if definitions were kept in wp-plugins, it would be important to sign them,
to prevent an attack vector for code injection (and have WP warn a user if
they are trying to turn an unsigned definition into code).

P.P.P.S: Look at
http://svn.wp-plugins.org/profiles/trunk/wp-data/WPD_Template.php, I
actually create PHP code from the logical definitions, which poses problems
both in regards to does the user have file writes, and also in regards to a
potential code injection vector. On the other hand, it allows opcode caching
to work. In a final implementation, this might better be left as an
"advanced" option.


More information about the wp-hackers mailing list