Textpattern plugin: Each

n: smd_each | v: 0.2 | f: Development / Textpattern plugins

Documentation for the Textpattern plugin smd_each by Stef Dawson follows this short message from our sponsor ;-)

Plugin list buttonPlugin download buttonCompressed plugin download button

smd_each

Features

  • Iterate over any variables you can get your hands on. These can be anything from the current article (custom field, article_image, etc), any URL/SERVER variable or cookie (subject to normal escaping rules) or any <txp:variable />
  • Include or exclude particular variables, or only choose variables that match particular text
  • Iterate over subsets of data contained in any field
  • Choose to process each variable one by one with a form, or collect them together into a delimited list to be processed only once by the form
  • Provides free headaches if you don’t keep your wits about you

Author

Stef Dawson. For other software by me, or to make a donation, see the software page.

Installation / Uninstallation

Download the plugin from either textpattern.org, or the software page above, paste the code into the TXP Admin -> Plugins pane, install and enable the plugin. Visit the forum thread for more info or to report a bug/feature request.

To remove the plugin, simply delete it from the Admin->Plugins tab.

Usage

For some ideas on usage scenarios, see the examples.

smd_each

Place one or more smd_each tags in any article, page or form, supply any of the following options to configure it and use a form (or container) to define what to do with each matching variable.

In a nutshell, it grabs every array from the places you specify, allows you to filter the array with matches or specific items, then assigns each successive name/value pair to two replacement tags that you can use in your form to do stuff. You could simply display the values or populate new tables with them, plug values into queries, create smd_vars, test the values with smd_if, you name it.

Attributes

  • type : where to look for variables. They can be combined into a comma-separated list if you wish to search more than one place at once. Options are:
    • field : a TXP article field (the default)
    • urlvar : the URL, e.g. name1=val&name2=val&…
    • svrvar : the server environment variables
    • cookie : any user cookies
    • txpvar : a value from any <txp:variable /> tag
    • fixed : specify your own list of variables to inject into the mix (via include)
  • include : base list of variable names you definitely want to be returned in the result. If you are using fixed as one of the types you can add your own variables here, delimited by paramdelim. For example include="keywords, my_var:north:south:east:west" would include the keywords article field and create my_var, giving it a value of “north, south, east, west”. Note that if you are using a custom field, it is likely to have been converted to all lower case by Textpattern, so you should use an all lower case name here
  • exclude : base list of variable names you definitely do not want to be returned in the result
  • match : list of text strings to search for to refine the returned variables. By default this will match against every variable in every location you specified in type. It is automatically wild so will match portions of a variable
  • matchwith : defaults to name which looks only at the variable’s name for a match. Can be set to value to look at its value or name, value to look in both for matches
  • subset : if you think your variables are going to contain lists themselves, specify subset="1" to have the plugin add each pseudo-variable to the array. For example, if you article_image field contained 14, 6, 17, 3, 9 you would get five more variables called article_image_1 (value:14), article_image_2 (value: 6), and so on.
    Note that you will also get the full article_image (and all other matching non-subset vars) included. If you wish to only see variables that contain sublists of data, use subset="2"
  • var_prefix : if you are nesting smd_each tags you’ll find that any inner {smd_var_value} replacements will take on the values of the outer tag. For this reason you can specify a prefix that will be used in all replacements. Thus if you set var_prefix="opt_" on your inner smd_each tag, you would use {opt_var_value} and {opt_var_name} in the inner container/form. Default: smd_
  • form : the TXP form to execute for every matching variable. If not specified, the container will be used. If there’s no container, a default form with just the {smd_var_value} is used
  • collate : prevent the form being executed for every variable. Instead, collect the variable names internally and then process the entire list by the form once only, after all variables have been read. See collate mode
  • delim : the delimiter to use for specifying plugin options. Defaults to comma (,)
  • paramdelim : the delimiter to use for specifying inter-value plugin options (for example in collate mode). Defaults to colon (:)
  • outdelim : the delimiter to use to separate each variable displayed in collate mode
  • wraptag : the (X)HTML tag to wrap the form in, e.g. wraptag="ul"
  • break : the (X)HTML tag to wrap each call to the form in, e.g. break="li"
  • class : the CSS class name to give to the wraptag

Replacement tags

For every matching variable, you can use the following replacement tags in your form:

  • {smd_var_name} : the variable name
  • {smd_var_value} : the variable’s value
  • {smd_var_counter} : the variable’s position in the list (1, 2, 3…)
  • {smd_var_total} : the total number of matching variables being iterated

(Note that each replacement tag will have whatever var_prefix you have designated: the default is smd_).

These can be used for whatever devious means you see fit. e.g.

  • <txp:article_custom id="{smd_var_value}" /> will display the given article for each matching ID.
  • <txp:article keywords="{smd_var_value}" /> in collate mode, might display the given articles that have keywords matching every user-submitted search term
  • <txp:smd_if field="{smd_var_counter}" operator="eq" value="{smd_var_total}">This is the last item</txp:smd_if>

Collate mode

Instead of parsing each variable with the form/container, you may elect to internally ‘collect’ all matching variables and output them in one big list via the form. Thus, the form is only called once at the end. It may seem useless but it can be of value for creating lists of things from each matching variable.

Starting simply, collate="1" switches collation mode on. If the plugin matched 3 variables from your article and you were to use the replacement tags in your form like this:

The matching vars: {smd_var_name} = {smd_var_value}

you might get this:

The matching vars: section,category1,category2 = article,news,politics

Compare that with the regular mode, which outputs:

The matching vars: section = article
The matching vars: category1 = news
The matching vars: category2 = politics

Sometimes it’s useful to be able to put quotes around each item; you can tell collate mode to do that:

collate="quote:{smd_var_value}"

You would then get:

The matching vars: section,category1,category2 = 'article','news','politics'

The delimiter (a comma in this case) can be overridden with the outdelim attribute. You can quote more than one thing at once by specifying the items as a delimited list:

collate="quote:{smd_var_name}:{smd_var_value}"

but there’s not much point because there are only two replacement tags and you can thus use the shortcut collate="quote" to quote them all. The delimiter used between items (the colon) can be overriden with the paramdelim attribute.

The second special feature of collation mode is that you do not have to always output the entire list. You can grab individual entities from within the internal array by using the ‘#’ notation in your form:

{smd_var_value} might output 'article','news','politics' (as before)
{smd_var_value#1} would only output article
{smd_var_name#3} would only output category2

Note that when pulling out individual entries they do not get quotes added to them, regardless of whether you used quote or not. This is because it is a single item so you can easily put the quotes in the form itself (viz: "{smd_var_name#2}")

Examples

Example 1

Display each article image from a comma-separated list of IDs in the Article Image field.

<txp:smd_each include="article_image" subset="2">
  <txp:image id="{smd_var_value}" />
</txp:smd_each>

Example 2

Display each article image from a comma-separated list of IDs in the Article Image field, and also allow IDs to be given in the URL line via the variable name my_image_list :

<txp:smd_each type="field, urlvar"
     include="article_image, my_image_list"
     subset="2" form="varout" var_prefix="me_" />

And in form varout:

<txp:image id="{me_var_value}" />

Example 3

This is simply to highlight the differences between the various matching modes. Let’s take a simple article:

  • section: animals
  • title (url_title) : The lion (the-lion)
  • category1: mammal
  • category2: dangerous
  • article_image: 25, 28, 12
  • keywords: big, cat, mane, fur, teeth, roar, chomp, ouch
  • custom1 (“origin”): africa

There are a whole host of other variables (switch debug=“1” on to see them all for your chosen types) but we’ll concentrate on these for now to keep things simple.

tag options vars returned remarks
match=“cat” category1, category2 default is to match name only
match=“cat” matchwith=“name, value” category1, category2, keywords, id_keywords now checks value as well
match=“cat” include=“article_image” category1, category2, article_image includes are always selected
match=“cat” include=“article_image, origin” subset=“1” category1, category2, origin, article_image, article_image_1, article_image_2, article_image_3 looked “inside” each var to find any lists
match=“cat” include=“article_image, origin” subset=“2” article_image_1, article_image_2, article_image_3 return only data sets that contain lists and removes the aggregate (base) entry
match=“cat, article_image” matchwith=“name,value” subset=“2” article_image_1, article_image_2, article_image_3, keywords, id_keywords checks both name and value for each match term, and only shows items that have lists in them (if keywords held just one item “cat” it would not be displayed)

Note that if you are using this tag in an article, the ‘body’ may match because it contains the smd_each tag which contains the very information you are matching! In this case, adding exclude="body" will remove it from the results.

Example 4

From a page template, allow visitors to display any number of articles.

<form name="show_arts" action="/my/results/page">
  <txp:article_custom form="list_checks"
       category="animals" limit="999" />
  <input type="submit" />
</form>

Your form list_checks simply displays the article title and a checkbox:

<li>
<txp:title /><input type="checkbox"
     name="article_lists[]"
     value="<txp:article_id />" />
</li>

When the user submits the form we can use smd_each to find which checkboxes have been clicked and show the relevant articles:

<txp:smd_each type="urlvar"
     match="article_lists" subset="2">
  <txp:article_custom id="{smd_var_value}" />
</txp:smd_each>

Alternatively, if you are using a copy of TXP that supports lists in article_custom’s id attribute you can drop the subset.

Changelog

  • 12 Jun 08 | 0.1 | Initial release
  • 22 Jun 08 | 0.11 | Added type=fixed (thanks mrdale)
  • 04 Apr 09 | 0.12 | Fixed subset="2" bug with single entries (thanks jeremywood) ; added {var_counter} and {var_total} (thanks mrdale)
  • 30 Aug 09 | 0.2 | Added var_prefix attribute and set it to smd_ by default

Source code

If you’d rather scrabble amid the ones and noughts, you’ll need to step into the view source page.

Legacy software

If, for some inexplicable reason, you need a legacy version of a plugin, it can probably be found on the plugin archive page.

Experimental software

If you’re feeling brave, or fancy dipping your toe in shark-infested water, you can test out some of my beta code. It can be found on the plugin beta page.