soo_txp_obj Page 2 of 6 « »
Proof of concept: article_image() redux
Summary:
Demonstrating the soo_txp_obj library
Pretty little aster, isn’t it? Not showy, but this was shot in late November, when one takes what one can get in the gardening way.
What does this have to do with a Textpattern library? Well, since you asked …
After one too many convoluted procedural nightmares in a large-ish plugin project, I started over by writing some basic classes for object-oriented Textpattern plugin code. This is my first extensive foray into OOP, but I have already found the new classes so helpful I thought they would be worth sharing.
This image and its thumbnail were put here using soo_article_image_redux()
, a largely object-oriented re-write of article_image()
. It works just the same as the original (actually a little better). I don’t need the new tag; I chose article_image()
to demonstrate the library with a familiar function.
First, have a look through the real article_image(). Two things stand out on a quick perusal: the nesting goes fairly deep (up to five levels of nested if
statements), and there are three big chunks of concatenated strings for building the output. Here’s an overview of what article_image()
needs to sort out:
- Is there anything in the “Article Image” field?
- Does it appear to be an ID or a URL?
- If an ID, is there such a record in the database?
- Is the user (the Txp author) requesting a thumbnail? If so, is there one?
- Does the user want the alt and title attributes HTML-escaped?
- Does the user want a wraptag, and/or class and (HTML) id as tag attributes?
article_image()
is rather unwieldy for such a straightforward problem. That’s because it mixes input (getting data, interpreting user requests) and output (building the tag). Hence the three separate chunks for the img
tag, each very similar, but just different enough that it would be a pain to ship them off to a utility function that you could use for all three.
Now have a look at soo_article_image_redux():
That there’s less code is to be expected: after all, it’s based on a library. You might be wondering what kind of black box is ->tag()
(see line 61). Actually it’s pretty simple:
public function tag()
{
$out = '<' . $this->element_name;
foreach ( $this->html_attributes() as $property => $value )
if ( $value or $property == 'alt' )
$out .= " $property=\"$value\"";
if ( $this->is_empty )
return $out . ' />';
$out .= '>' . $this->newline();
foreach ( $this->contents as $item )
if ( $item instanceof soo_html )
$out .= $item->tag();
else
$out .= $item;
return $out . $this->newline() . "</$this->element_name>" . $this->newline();
}
As you can see, tag()
is not specific to image tags. It’s part of the abstract Soo_Html class, which Soo_Html_Img extends. Note that it will recursively tag any Soo_Html objects contained within. Feed it a table or even a multi-dimensional ul
or ol
and it will tag the whole thing at one go.
The classes as a whole are also quite simple, as abstract and general as possible. There are query objects (e.g., soo_txp_select), data objects (e.g., soo_txp_img) and display objects (e.g., soo_html_img). The parent classes are trivially easy to extend when you need to work with a new type of data record or HTML element.
Back to soo_article_image_redux(), note that control structure nesting never goes beyond two levels. All of the input logic is at the top, and all of the output logic is at the bottom. Okay, it’s not a razor-sharp division, but it’s quite clear. Note line 47: this is where the data object passes its properties to a new output object. Compare this to line 33, where we are dealing with an article image set by URL. In this case we create a blank Soo_Html_Img object and then set its src property.
It catches a contradiction that article_image
misses: if the user requests a thumbnail but the article image is set by URL (hence there is no thumbnail available) nothing is returned (see line 39).
I’m not picking on article_image()
, which gets the job done (except for the thing about thumbnails I pointed out). This kind of coding works well enough for small and medium-sized functions. Beyond a certain size, though, it becomes exponentially harder to maintain.
Posted 2009-01-24 (last modified 2010-06-30)